diff --git a/.noir-sync-commit b/.noir-sync-commit index 8f6aafb96d3..2863ab07e89 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -6cc105ee441e093b4fccdd5fcc3db922eb28a3fb +1969ce39378f633e88adedf43b747724b89ed7d7 diff --git a/avm-transpiler/Cargo.lock b/avm-transpiler/Cargo.lock index 6fc2aa934f5..21ff357893f 100644 --- a/avm-transpiler/Cargo.lock +++ b/avm-transpiler/Cargo.lock @@ -1274,6 +1274,7 @@ dependencies = [ "arena", "chumsky", "fm", + "im", "iter-extended", "lalrpop", "lalrpop-util", diff --git a/avm-transpiler/rust-toolchain.toml b/avm-transpiler/rust-toolchain.toml index c9f40a6577e..77b50d3cfe1 100644 --- a/avm-transpiler/rust-toolchain.toml +++ b/avm-transpiler/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.73" +channel = "1.74.1" components = ["rust-src"] targets = [] profile = "default" diff --git a/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr b/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr index e46fb7e8407..7ae2edbda8b 100644 --- a/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr +++ b/noir-projects/noir-contracts/contracts/card_game_contract/src/cards.nr @@ -174,7 +174,8 @@ pub fn get_pack_cards( // generate pseudo randomness deterministically from 'seed' and user secret let secret = context.request_app_nullifier_secret_key(owner); let mix = secret + seed; - let random_bytes = std::hash::sha256_slice(mix.to_le_bytes(32)); + let mix_bytes: [u8; 32] = mix.to_le_bytes(32).as_array(); + let random_bytes = std::hash::sha256(mix_bytes); let mut cards = [Card::from_field(0); PACK_CARDS]; // we generate PACK_CARDS cards diff --git a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr index 30fb2737fc8..25f96128b9d 100644 --- a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/main.nr @@ -80,7 +80,8 @@ contract EcdsaAccount { // Verify payload signature using Ethereum's signing scheme // Note that noir expects the hash of the message/challenge as input to the ECDSA verification. - let hashed_message: [u8; 32] = std::hash::sha256_slice(outer_hash.to_be_bytes(32)); + let outer_hash_bytes: [u8; 32] = outer_hash.to_be_bytes(32).as_array(); + let hashed_message: [u8; 32] = std::hash::sha256(outer_hash_bytes); let verification = std::ecdsa_secp256k1::verify_signature(public_key.x, public_key.y, signature, hashed_message); assert(verification == true); diff --git a/noir/noir-repo/.github/workflows/docs-pr.yml b/noir/noir-repo/.github/workflows/docs-pr.yml index 1f9ccdd946b..945ed555639 100644 --- a/noir/noir-repo/.github/workflows/docs-pr.yml +++ b/noir/noir-repo/.github/workflows/docs-pr.yml @@ -55,7 +55,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.73.0 + uses: dtolnay/rust-toolchain@1.74.1 - uses: Swatinem/rust-cache@v2 with: diff --git a/noir/noir-repo/.github/workflows/formatting.yml b/noir/noir-repo/.github/workflows/formatting.yml index 279e90f5f6f..8166fb0f7c2 100644 --- a/noir/noir-repo/.github/workflows/formatting.yml +++ b/noir/noir-repo/.github/workflows/formatting.yml @@ -32,7 +32,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.73.0 + uses: dtolnay/rust-toolchain@1.74.1 with: targets: ${{ matrix.target }} components: clippy, rustfmt @@ -73,7 +73,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.73.0 + uses: dtolnay/rust-toolchain@1.74.1 - uses: Swatinem/rust-cache@v2 with: diff --git a/noir/noir-repo/.github/workflows/gates_report.yml b/noir/noir-repo/.github/workflows/gates_report.yml index be55236c40f..e694e5fad04 100644 --- a/noir/noir-repo/.github/workflows/gates_report.yml +++ b/noir/noir-repo/.github/workflows/gates_report.yml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.73.0 + uses: dtolnay/rust-toolchain@1.74.1 - uses: Swatinem/rust-cache@v2 with: diff --git a/noir/noir-repo/.github/workflows/publish-acvm.yml b/noir/noir-repo/.github/workflows/publish-acvm.yml index d17d8f294fb..feb4d4216c3 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.73.0 + uses: dtolnay/rust-toolchain@1.74.1 # 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-es-packages.yml b/noir/noir-repo/.github/workflows/publish-es-packages.yml index 819be308169..682fed69c7b 100644 --- a/noir/noir-repo/.github/workflows/publish-es-packages.yml +++ b/noir/noir-repo/.github/workflows/publish-es-packages.yml @@ -24,7 +24,7 @@ jobs: ref: ${{ inputs.noir-ref }} - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.73.0 + uses: dtolnay/rust-toolchain@1.74.1 - uses: Swatinem/rust-cache@v2 with: @@ -58,7 +58,7 @@ jobs: ref: ${{ inputs.noir-ref }} - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.73.0 + uses: dtolnay/rust-toolchain@1.74.1 - uses: Swatinem/rust-cache@v2 with: @@ -95,7 +95,7 @@ jobs: ref: ${{ inputs.noir-ref }} - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.73.0 + uses: dtolnay/rust-toolchain@1.74.1 - uses: Swatinem/rust-cache@v2 with: diff --git a/noir/noir-repo/.github/workflows/publish-nargo.yml b/noir/noir-repo/.github/workflows/publish-nargo.yml index e47e1a13053..85010503b00 100644 --- a/noir/noir-repo/.github/workflows/publish-nargo.yml +++ b/noir/noir-repo/.github/workflows/publish-nargo.yml @@ -46,7 +46,7 @@ jobs: echo "MACOSX_DEPLOYMENT_TARGET=$(xcrun -sdk macosx$(sw_vers -productVersion) --show-sdk-platform-version)" >> $GITHUB_ENV - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.73.0 + uses: dtolnay/rust-toolchain@1.74.1 with: targets: ${{ matrix.target }} @@ -120,7 +120,7 @@ jobs: ref: ${{ inputs.tag || env.GITHUB_REF }} - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.73.0 + uses: dtolnay/rust-toolchain@1.74.1 with: targets: ${{ matrix.target }} diff --git a/noir/noir-repo/.github/workflows/test-js-packages.yml b/noir/noir-repo/.github/workflows/test-js-packages.yml index 06a96ee8932..7fd198d9a89 100644 --- a/noir/noir-repo/.github/workflows/test-js-packages.yml +++ b/noir/noir-repo/.github/workflows/test-js-packages.yml @@ -22,7 +22,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.73.0 + uses: dtolnay/rust-toolchain@1.74.1 - uses: Swatinem/rust-cache@v2 with: @@ -55,7 +55,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.73.0 + uses: dtolnay/rust-toolchain@1.74.1 - uses: Swatinem/rust-cache@v2 with: @@ -88,7 +88,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.73.0 + uses: dtolnay/rust-toolchain@1.74.1 - uses: Swatinem/rust-cache@v2 with: @@ -123,7 +123,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.73.0 + uses: dtolnay/rust-toolchain@1.74.1 - 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 cdd7a064a8d..c13b0815d94 100644 --- a/noir/noir-repo/.github/workflows/test-rust-workspace-msrv.yml +++ b/noir/noir-repo/.github/workflows/test-rust-workspace-msrv.yml @@ -29,7 +29,7 @@ jobs: uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.73.0 + uses: dtolnay/rust-toolchain@1.74.1 with: targets: x86_64-unknown-linux-gnu @@ -72,7 +72,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.73.0 + uses: dtolnay/rust-toolchain@1.74.1 with: targets: x86_64-unknown-linux-gnu diff --git a/noir/noir-repo/.github/workflows/test-rust-workspace.yml b/noir/noir-repo/.github/workflows/test-rust-workspace.yml index 22684de3044..cf35950fa3e 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.73.0 + uses: dtolnay/rust-toolchain@1.74.1 with: targets: x86_64-unknown-linux-gnu @@ -59,7 +59,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup toolchain - uses: dtolnay/rust-toolchain@1.73.0 + uses: dtolnay/rust-toolchain@1.74.1 with: targets: x86_64-unknown-linux-gnu diff --git a/noir/noir-repo/CHANGELOG.md b/noir/noir-repo/CHANGELOG.md index 0ab84df44e7..a084811fc21 100644 --- a/noir/noir-repo/CHANGELOG.md +++ b/noir/noir-repo/CHANGELOG.md @@ -109,7 +109,7 @@ * reserve `unchecked` keyword ([#4432](https://github.com/noir-lang/noir/issues/4432)) * Remove empty value from bounded vec ([#4431](https://github.com/noir-lang/noir/issues/4431)) * Ban Fields in for loop indices and bitwise ops ([#4376](https://github.com/noir-lang/noir/issues/4376)) -* bump msrv to 1.73.0 ([#4406](https://github.com/noir-lang/noir/issues/4406)) +* Bump msrv to 1.73.0 ([#4406](https://github.com/noir-lang/noir/issues/4406)) * **ci:** Bump MSRV to 1.72.1 and enforce that ACVM can be published using updated lockfile ([#4385](https://github.com/noir-lang/noir/issues/4385)) * Restrict bit sizes ([#4235](https://github.com/noir-lang/noir/issues/4235)) * move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index ee83f7f8ddf..35702c8df03 100644 --- a/noir/noir-repo/Cargo.lock +++ b/noir/noir-repo/Cargo.lock @@ -10,8 +10,10 @@ dependencies = [ "base64 0.21.2", "bincode", "brillig", + "criterion", "flate2", "fxhash", + "pprof 0.13.0", "serde", "serde-big-array", "serde-generate", @@ -454,7 +456,7 @@ dependencies = [ [[package]] name = "backend-interface" -version = "0.11.0" +version = "0.27.0" dependencies = [ "acvm", "bb_abstraction_leaks", @@ -624,7 +626,7 @@ dependencies = [ "lazy_static", "noir_grumpkin", "num-bigint", - "pprof", + "pprof 0.12.1", "thiserror", "wasm-bindgen-futures", "wasmer", @@ -2825,7 +2827,6 @@ dependencies = [ "noirc_frontend", "noirc_printable_type", "rayon", - "rustc_version", "serde", "tempfile", "thiserror", @@ -2866,11 +2867,10 @@ dependencies = [ "notify", "notify-debouncer-full", "paste", - "pprof", + "pprof 0.13.0", "predicates 2.1.5", "prettytable-rs", "rayon", - "rustc_version", "serde", "serde_json", "similar-asserts", @@ -3161,6 +3161,7 @@ dependencies = [ "base64 0.21.2", "chumsky", "fm", + "im", "iter-extended", "lalrpop", "lalrpop-util", @@ -3547,6 +3548,28 @@ dependencies = [ "thiserror", ] +[[package]] +name = "pprof" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef5c97c51bd34c7e742402e216abdeb44d415fbe6ae41d56b114723e953711cb" +dependencies = [ + "backtrace", + "cfg-if 1.0.0", + "criterion", + "findshlibs", + "inferno", + "libc", + "log", + "nix 0.26.4", + "once_cell", + "parking_lot 0.12.1", + "smallvec", + "symbolic-demangle", + "tempfile", + "thiserror", +] + [[package]] name = "ppv-lite86" version = "0.2.17" diff --git a/noir/noir-repo/Cargo.toml b/noir/noir-repo/Cargo.toml index 5dd453415aa..132ff181e44 100644 --- a/noir/noir-repo/Cargo.toml +++ b/noir/noir-repo/Cargo.toml @@ -45,7 +45,7 @@ version = "0.27.0" # x-release-please-end authors = ["The Noir Team "] edition = "2021" -rust-version = "1.73.0" +rust-version = "1.74.1" license = "MIT OR Apache-2.0" repository = "https://github.com/noir-lang/noir/" @@ -104,6 +104,14 @@ chumsky = { git = "https://github.com/jfecher/chumsky", rev = "ad9d312", default "ahash", "std", ] } + +# Benchmarking +criterion = "0.5.0" +# Note that using the "frame-pointer" feature breaks framegraphs on linux +# https://github.com/tikv/pprof-rs/pull/172 +pprof = { version = "0.13", features = ["flamegraph","criterion"] } + + dirs = "4" serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0" @@ -124,6 +132,7 @@ tempfile = "3.6.0" jsonrpc = { version = "0.16.0", features = ["minreq_http"] } flate2 = "1.0.24" +im = { version = "15.1", features = ["serde"] } tracing = "0.1.40" tracing-web = "0.1.3" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } diff --git a/noir/noir-repo/README.md b/noir/noir-repo/README.md index adf68b290ef..eea2280c50c 100644 --- a/noir/noir-repo/README.md +++ b/noir/noir-repo/README.md @@ -1,3 +1,13 @@ +
+ + The Noir Programming Language + + +[Website][Noir] | [Getting started] | [Documentation] | [Contributing] +
+ + + # The Noir Programming Language Noir is a Domain Specific Language for SNARK proving systems. It has been designed to use any ACIR compatible proving system. @@ -6,58 +16,35 @@ Noir is a Domain Specific Language for SNARK proving systems. It has been design ## Quick Start -Read the installation section [here](https://noir-lang.org/docs/dev/getting_started/installation/). +Read the [installation section][Getting started] from the [Noir docs][Documentation]. Once you have read through the documentation, you can visit [Awesome Noir](https://github.com/noir-lang/awesome-noir) to run some of the examples that others have created. -## Current Features - -Backends: +## Getting Help -- Barretenberg via FFI -- Marlin via arkworks (Note -- latest interfaces may not be updated to support Marlin backend. Please open an issue if this is relevant to your project and requires attention.) +Join the Noir [forum][Forum] or [Discord][Discord] -Compiler: +## Contributing -- Module System -- For expressions -- Arrays -- Bit Operations -- Binary operations (<, <=, >, >=, +, -, \*, /, %) [See documentation for an extensive list] -- Unsigned integers -- If statements -- Structures and Tuples -- Generics - -ACIR Supported OPCODES: - -- Sha256 -- Blake2s -- Schnorr signature verification -- Pedersen -- HashToField +See [CONTRIBUTING.md][CONTRIBUTING]. ## Future Work The current focus is to gather as much feedback as possible while in the alpha phase. The main focuses of Noir are _safety_ and _developer experience_. If you find a feature that does not seem to be in line with these goals, please open an issue! -Concretely the following items are on the road map: - -- General code sanitization and documentation (ongoing effort) -- Prover and Verifier Key logic. (Prover and Verifier pre-process per compile) -- Fallback mechanism for backend unsupported opcodes -- Visibility modifiers -- Signed integers -- Backend integration: (Bulletproofs) -- Recursion -- Big integers - ## Minimum Rust version -This crate's minimum supported rustc version is 1.73.0. +This workspace's minimum supported rustc version is 1.74.1. ## License Noir is free and open source. It is distributed under a dual license. (MIT/APACHE) -Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this repository by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[Noir]: https://www.noir-lang.org/ +[Getting Started]: https://noir-lang.org/docs/getting_started/installation/ +[Forum]: https://forum.aztec.network/c/noir +[Discord]: https://discord.gg/JtqzkdeQ6G +[Documentation]: https://noir-lang.org/docs +[Contributing]: CONTRIBUTING.md \ No newline at end of file diff --git a/noir/noir-repo/acvm-repo/acir/Cargo.toml b/noir/noir-repo/acvm-repo/acir/Cargo.toml index 4fae9ea20ff..d6990f83281 100644 --- a/noir/noir-repo/acvm-repo/acir/Cargo.toml +++ b/noir/noir-repo/acvm-repo/acir/Cargo.toml @@ -29,8 +29,14 @@ strum_macros = "0.24" serde-reflection = "0.3.6" serde-generate = "0.25.1" fxhash.workspace = true +criterion.workspace = true +pprof.workspace = true [features] default = ["bn254"] bn254 = ["acir_field/bn254", "brillig/bn254"] bls12_381 = ["acir_field/bls12_381", "brillig/bls12_381"] + +[[bench]] +name = "serialization" +harness = false diff --git a/noir/noir-repo/acvm-repo/acir/benches/serialization.rs b/noir/noir-repo/acvm-repo/acir/benches/serialization.rs new file mode 100644 index 00000000000..73e3916a73b --- /dev/null +++ b/noir/noir-repo/acvm-repo/acir/benches/serialization.rs @@ -0,0 +1,123 @@ +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; +use std::{collections::BTreeSet, time::Duration}; + +use acir::{ + circuit::{Circuit, ExpressionWidth, Opcode, Program, PublicInputs}, + native_types::{Expression, Witness}, + FieldElement, +}; + +use pprof::criterion::{Output, PProfProfiler}; + +const SIZES: [usize; 9] = [10, 50, 100, 500, 1000, 5000, 10000, 50000, 100000]; + +fn sample_program(num_opcodes: usize) -> Program { + let assert_zero_opcodes: Vec = (0..num_opcodes) + .map(|i| { + Opcode::AssertZero(Expression { + mul_terms: vec![( + FieldElement::from(2 * i), + Witness(i as u32), + Witness(i as u32 + 10), + )], + linear_combinations: vec![ + (FieldElement::from(2 * i), Witness(i as u32)), + (FieldElement::from(3 * i), Witness(i as u32 + 1)), + ], + q_c: FieldElement::from(i), + }) + }) + .collect(); + + Program { + functions: vec![Circuit { + current_witness_index: 4000, + opcodes: assert_zero_opcodes.to_vec(), + expression_width: ExpressionWidth::Bounded { width: 3 }, + private_parameters: BTreeSet::from([Witness(1), Witness(2), Witness(3), Witness(4)]), + public_parameters: PublicInputs(BTreeSet::from([Witness(5)])), + return_values: PublicInputs(BTreeSet::from([Witness(6)])), + assert_messages: Vec::new(), + recursive: false, + }], + } +} + +fn bench_serialization(c: &mut Criterion) { + let mut group = c.benchmark_group("serialize_program"); + for size in SIZES.iter() { + let program = sample_program(*size); + + group.throughput(Throughput::Elements(*size as u64)); + group.bench_with_input(BenchmarkId::from_parameter(size), &program, |b, program| { + b.iter(|| Program::serialize_program(program)); + }); + } + group.finish(); + + let mut group = c.benchmark_group("serialize_program_json"); + for size in SIZES.iter() { + let program = sample_program(*size); + + group.throughput(Throughput::Elements(*size as u64)); + group.bench_with_input(BenchmarkId::from_parameter(size), &program, |b, program| { + b.iter(|| { + let mut bytes = Vec::new(); + let mut serializer = serde_json::Serializer::new(&mut bytes); + Program::serialize_program_base64(program, &mut serializer) + }); + }); + } + group.finish(); +} + +fn bench_deserialization(c: &mut Criterion) { + let mut group = c.benchmark_group("deserialize_program"); + for size in SIZES.iter() { + let program = sample_program(*size); + let serialized_program = Program::serialize_program(&program); + + group.throughput(Throughput::Elements(*size as u64)); + group.bench_with_input( + BenchmarkId::from_parameter(size), + &serialized_program, + |b, program| { + b.iter(|| Program::deserialize_program(program)); + }, + ); + } + group.finish(); + + let mut group = c.benchmark_group("deserialize_program_json"); + for size in SIZES.iter() { + let program = sample_program(*size); + + let serialized_program = { + let mut bytes = Vec::new(); + let mut serializer = serde_json::Serializer::new(&mut bytes); + Program::serialize_program_base64(&program, &mut serializer).expect("should succeed"); + bytes + }; + + group.throughput(Throughput::Elements(*size as u64)); + group.bench_with_input( + BenchmarkId::from_parameter(size), + &serialized_program, + |b, program| { + b.iter(|| { + let mut deserializer = serde_json::Deserializer::from_slice(program); + Program::deserialize_program_base64(&mut deserializer) + }); + }, + ); + } + group.finish(); +} + +criterion_group!( + name = benches; + config = Criterion::default().sample_size(40).measurement_time(Duration::from_secs(20)).with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); + targets = bench_serialization, bench_deserialization +); + +criterion_main!(benches); diff --git a/noir/noir-repo/acvm-repo/acvm_js/build.sh b/noir/noir-repo/acvm-repo/acvm_js/build.sh index 58724dee02c..7ba69389a9b 100755 --- a/noir/noir-repo/acvm-repo/acvm_js/build.sh +++ b/noir/noir-repo/acvm-repo/acvm_js/build.sh @@ -48,5 +48,3 @@ BROWSER_WASM=${BROWSER_DIR}/${pname}_bg.wasm run_or_fail cargo build --lib --release --target $TARGET --package ${pname} run_or_fail wasm-bindgen $WASM_BINARY --out-dir $NODE_DIR --typescript --target nodejs run_or_fail wasm-bindgen $WASM_BINARY --out-dir $BROWSER_DIR --typescript --target web -run_if_available wasm-opt $NODE_WASM -o $NODE_WASM -O -run_if_available wasm-opt $BROWSER_WASM -o $BROWSER_WASM -O diff --git a/noir/noir-repo/acvm-repo/acvm_js/test/browser/black_box_solvers.test.ts b/noir/noir-repo/acvm-repo/acvm_js/test/browser/black_box_solvers.test.ts index 3c54fe8e38f..695f6b89afc 100644 --- a/noir/noir-repo/acvm-repo/acvm_js/test/browser/black_box_solvers.test.ts +++ b/noir/noir-repo/acvm-repo/acvm_js/test/browser/black_box_solvers.test.ts @@ -4,7 +4,6 @@ import initACVM, { blake2s256, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify, - initLogLevel, keccak256, sha256, xor, @@ -12,8 +11,6 @@ import initACVM, { beforeEach(async () => { await initACVM(); - - initLogLevel('INFO'); }); it('successfully calculates the bitwise AND of two fields', async () => { diff --git a/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs b/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs index c8e7e807d87..f624cde9969 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs @@ -1,8 +1,9 @@ use noirc_errors::{Location, Span}; +use noirc_frontend::ast::{FunctionReturnType, NoirFunction, UnresolvedTypeData}; use noirc_frontend::{ graph::CrateId, macros_api::{FileId, HirContext}, - parse_program, FunctionReturnType, NoirFunction, Type, UnresolvedTypeData, + parse_program, Type, }; use crate::utils::{ diff --git a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs index 4401c867df9..5f68ce98c8a 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/contract_interface.rs @@ -1,9 +1,10 @@ +use noirc_frontend::ast::{NoirFunction, UnresolvedTypeData}; use noirc_frontend::{ graph::CrateId, macros_api::{FileId, HirContext, HirExpression, HirLiteral, HirStatement}, parse_program, parser::SortedModule, - NoirFunction, Type, UnresolvedTypeData, + Type, }; use crate::utils::{ @@ -52,7 +53,7 @@ pub fn stub_function(aztec_visibility: &str, func: &NoirFunction) -> String { }) .collect::>() .join(", "); - let fn_return_type: noirc_frontend::UnresolvedType = func.return_type(); + let fn_return_type: noirc_frontend::ast::UnresolvedType = func.return_type(); let fn_selector = format!("dep::aztec::protocol_types::abis::function_selector::FunctionSelector::from_signature(\"{}\")", SELECTOR_PLACEHOLDER); diff --git a/noir/noir-repo/aztec_macros/src/transforms/events.rs b/noir/noir-repo/aztec_macros/src/transforms/events.rs index b77a5821b81..9c8cd826360 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/events.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/events.rs @@ -1,5 +1,9 @@ use iter_extended::vecmap; use noirc_errors::Span; +use noirc_frontend::ast::{ + ExpressionKind, FunctionDefinition, FunctionReturnType, ItemVisibility, Literal, NoirFunction, + Visibility, +}; use noirc_frontend::{ graph::CrateId, macros_api::{ @@ -8,8 +12,6 @@ use noirc_frontend::{ UnresolvedTypeData, }, token::SecondaryAttribute, - ExpressionKind, FunctionDefinition, FunctionReturnType, ItemVisibility, Literal, NoirFunction, - Visibility, }; use crate::{ diff --git a/noir/noir-repo/aztec_macros/src/transforms/functions.rs b/noir/noir-repo/aztec_macros/src/transforms/functions.rs index 8e90d40aaec..24cad05b866 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/functions.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/functions.rs @@ -1,12 +1,15 @@ use convert_case::{Case, Casing}; use noirc_errors::Span; -use noirc_frontend::{ - macros_api::FieldElement, parse_program, BlockExpression, ConstrainKind, ConstrainStatement, - Distinctness, Expression, ExpressionKind, ForLoopStatement, ForRange, FunctionReturnType, - Ident, Literal, NoirFunction, NoirStruct, Param, PathKind, Pattern, Signedness, Statement, - StatementKind, UnresolvedType, UnresolvedTypeData, Visibility, +use noirc_frontend::ast; +use noirc_frontend::ast::{ + BlockExpression, ConstrainKind, ConstrainStatement, Distinctness, Expression, ExpressionKind, + ForLoopStatement, ForRange, FunctionReturnType, Ident, Literal, NoirFunction, NoirStruct, + Param, PathKind, Pattern, Signedness, Statement, StatementKind, UnresolvedType, + UnresolvedTypeData, Visibility, }; +use noirc_frontend::{macros_api::FieldElement, parse_program}; + use crate::{ chained_dep, chained_path, utils::{ @@ -337,7 +340,7 @@ fn serialize_to_hasher( &UnresolvedType { typ: UnresolvedTypeData::Integer( Signedness::Unsigned, - noirc_frontend::IntegerBitSize::ThirtyTwo, + ast::IntegerBitSize::ThirtyTwo, ), span: None, }, diff --git a/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs index 4b72759a5db..70db1ebd336 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs @@ -1,12 +1,16 @@ use noirc_errors::Span; +use noirc_frontend::ast::{ + ItemVisibility, LetStatement, NoirFunction, NoirStruct, PathKind, TraitImplItem, TypeImpl, + UnresolvedTypeData, UnresolvedTypeExpression, +}; use noirc_frontend::{ graph::CrateId, macros_api::{FileId, HirContext, HirExpression, HirLiteral, HirStatement}, parse_program, parser::SortedModule, - ItemVisibility, LetStatement, NoirFunction, NoirStruct, PathKind, TraitImplItem, Type, - TypeImpl, UnresolvedTypeData, UnresolvedTypeExpression, + Type, }; + use regex::Regex; use crate::{ diff --git a/noir/noir-repo/aztec_macros/src/transforms/storage.rs b/noir/noir-repo/aztec_macros/src/transforms/storage.rs index 66057108517..1e3cc011715 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/storage.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/storage.rs @@ -1,4 +1,8 @@ use noirc_errors::Span; +use noirc_frontend::ast::{ + BlockExpression, Expression, ExpressionKind, FunctionDefinition, Ident, Literal, NoirFunction, + NoirStruct, PathKind, Pattern, StatementKind, TypeImpl, UnresolvedType, UnresolvedTypeData, +}; use noirc_frontend::{ graph::CrateId, macros_api::{ @@ -8,9 +12,7 @@ use noirc_frontend::{ parse_program, parser::SortedModule, token::SecondaryAttribute, - BlockExpression, Expression, ExpressionKind, FunctionDefinition, Ident, Literal, NoirFunction, - NoirStruct, PathKind, Pattern, StatementKind, Type, TypeImpl, UnresolvedType, - UnresolvedTypeData, + Type, }; use crate::{ diff --git a/noir/noir-repo/aztec_macros/src/utils/ast_utils.rs b/noir/noir-repo/aztec_macros/src/utils/ast_utils.rs index 1731dfab49c..ebb4854f86e 100644 --- a/noir/noir-repo/aztec_macros/src/utils/ast_utils.rs +++ b/noir/noir-repo/aztec_macros/src/utils/ast_utils.rs @@ -1,10 +1,11 @@ use noirc_errors::{Span, Spanned}; -use noirc_frontend::{ - token::SecondaryAttribute, BinaryOpKind, CallExpression, CastExpression, Expression, - ExpressionKind, FunctionReturnType, Ident, IndexExpression, InfixExpression, Lambda, - LetStatement, MethodCallExpression, NoirTraitImpl, Path, Pattern, PrefixExpression, Statement, - StatementKind, TraitImplItem, UnaryOp, UnresolvedType, UnresolvedTypeData, +use noirc_frontend::ast::{ + BinaryOpKind, CallExpression, CastExpression, Expression, ExpressionKind, FunctionReturnType, + Ident, IndexExpression, InfixExpression, Lambda, LetStatement, MethodCallExpression, + NoirTraitImpl, Path, Pattern, PrefixExpression, Statement, StatementKind, TraitImplItem, + UnaryOp, UnresolvedType, UnresolvedTypeData, }; +use noirc_frontend::token::SecondaryAttribute; // // Helper macros for creating noir ast nodes @@ -66,6 +67,7 @@ pub fn mutable_assignment(name: &str, assigned_to: Expression) -> Statement { pattern: mutable(name), r#type: make_type(UnresolvedTypeData::Unspecified), expression: assigned_to, + comptime: false, attributes: vec![], })) } @@ -90,6 +92,7 @@ pub fn assignment_with_type( pattern: pattern(name), r#type: make_type(typ), expression: assigned_to, + comptime: false, attributes: vec![], })) } diff --git a/noir/noir-repo/aztec_macros/src/utils/errors.rs b/noir/noir-repo/aztec_macros/src/utils/errors.rs index 4c5411dfe0f..db86012a007 100644 --- a/noir/noir-repo/aztec_macros/src/utils/errors.rs +++ b/noir/noir-repo/aztec_macros/src/utils/errors.rs @@ -1,5 +1,6 @@ use noirc_errors::Span; -use noirc_frontend::{macros_api::MacroError, UnresolvedTypeData}; +use noirc_frontend::ast; +use noirc_frontend::macros_api::MacroError; use super::constants::MAX_CONTRACT_PRIVATE_FUNCTIONS; @@ -7,9 +8,9 @@ use super::constants::MAX_CONTRACT_PRIVATE_FUNCTIONS; pub enum AztecMacroError { AztecDepNotFound, ContractHasTooManyPrivateFunctions { span: Span }, - UnsupportedFunctionArgumentType { span: Span, typ: UnresolvedTypeData }, - UnsupportedFunctionReturnType { span: Span, typ: UnresolvedTypeData }, - UnsupportedStorageType { span: Option, typ: UnresolvedTypeData }, + UnsupportedFunctionArgumentType { span: Span, typ: ast::UnresolvedTypeData }, + UnsupportedFunctionReturnType { span: Span, typ: ast::UnresolvedTypeData }, + UnsupportedStorageType { span: Option, typ: ast::UnresolvedTypeData }, CouldNotAssignStorageSlots { secondary_message: Option }, CouldNotImplementComputeNoteHashAndNullifier { secondary_message: Option }, CouldNotImplementNoteInterface { span: Option, secondary_message: Option }, diff --git a/noir/noir-repo/aztec_macros/src/utils/hir_utils.rs b/noir/noir-repo/aztec_macros/src/utils/hir_utils.rs index 3b2f14fd87e..874640c01c8 100644 --- a/noir/noir-repo/aztec_macros/src/utils/hir_utils.rs +++ b/noir/noir-repo/aztec_macros/src/utils/hir_utils.rs @@ -1,5 +1,6 @@ use iter_extended::vecmap; use noirc_errors::Location; +use noirc_frontend::ast; use noirc_frontend::{ graph::CrateId, hir::{ @@ -12,7 +13,7 @@ use noirc_frontend::{ StructId, }, node_interner::{FuncId, TraitId, TraitImplKind}, - ItemVisibility, LetStatement, NoirFunction, Shared, Signedness, StructType, Type, + Shared, StructType, Type, }; use super::ast_utils::is_custom_attribute; @@ -68,8 +69,8 @@ pub fn collect_traits(context: &HirContext) -> Vec { /// Computes the aztec signature for a resolved type. pub fn signature_of_type(typ: &Type) -> String { match typ { - Type::Integer(Signedness::Signed, bit_size) => format!("i{}", bit_size), - Type::Integer(Signedness::Unsigned, bit_size) => format!("u{}", bit_size), + Type::Integer(ast::Signedness::Signed, bit_size) => format!("i{}", bit_size), + Type::Integer(ast::Signedness::Unsigned, bit_size) => format!("u{}", bit_size), Type::FieldElement => "Field".to_owned(), Type::Bool => "bool".to_owned(), Type::Array(len, typ) => { @@ -168,7 +169,7 @@ pub fn get_contract_module_data( pub fn inject_fn( crate_id: &CrateId, context: &mut HirContext, - func: NoirFunction, + func: ast::NoirFunction, location: Location, module_id: LocalModuleId, file_id: FileId, @@ -182,7 +183,7 @@ pub fn inject_fn( ); context.def_map_mut(crate_id).unwrap().modules_mut()[module_id.0] - .declare_function(func.name_ident().clone(), ItemVisibility::Public, func_id) + .declare_function(func.name_ident().clone(), ast::ItemVisibility::Public, func_id) .map_err(|err| MacroError { primary_message: format!("Failed to declare autogenerated {} function", func.name()), secondary_message: Some(format!("Duplicate definition found {}", err.0)), @@ -217,7 +218,7 @@ pub fn inject_fn( pub fn inject_global( crate_id: &CrateId, context: &mut HirContext, - global: LetStatement, + global: ast::LetStatement, module_id: LocalModuleId, file_id: FileId, ) { diff --git a/noir/noir-repo/compiler/fm/Cargo.toml b/noir/noir-repo/compiler/fm/Cargo.toml index 42e4b0c25d7..f556f305c78 100644 --- a/noir/noir-repo/compiler/fm/Cargo.toml +++ b/noir/noir-repo/compiler/fm/Cargo.toml @@ -3,6 +3,7 @@ name = "fm" version.workspace = true authors.workspace = true edition.workspace = true +rust-version.workspace = true license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/noir/noir-repo/compiler/noirc_driver/Cargo.toml b/noir/noir-repo/compiler/noirc_driver/Cargo.toml index a7fe0b4b610..495410b7b06 100644 --- a/noir/noir-repo/compiler/noirc_driver/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_driver/Cargo.toml @@ -3,6 +3,7 @@ name = "noirc_driver" version.workspace = true authors.workspace = true edition.workspace = true +rust-version.workspace = true license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 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 86f10818dbc..51fe4986845 100644 --- a/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs +++ b/noir/noir-repo/compiler/noirc_driver/src/abi_gen.rs @@ -3,12 +3,12 @@ use std::collections::BTreeMap; use acvm::acir::native_types::Witness; use iter_extended::{btree_map, vecmap}; use noirc_abi::{Abi, AbiParameter, AbiReturnType, AbiType, AbiValue}; +use noirc_frontend::ast::Visibility; use noirc_frontend::{ hir::Context, hir_def::{expr::HirArrayLiteral, function::Param, stmt::HirPattern}, macros_api::{HirExpression, HirLiteral}, node_interner::{FuncId, NodeInterner}, - Visibility, }; use std::ops::Range; diff --git a/noir/noir-repo/compiler/noirc_errors/Cargo.toml b/noir/noir-repo/compiler/noirc_errors/Cargo.toml index da18399971e..c9cb7e2709f 100644 --- a/noir/noir-repo/compiler/noirc_errors/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_errors/Cargo.toml @@ -3,6 +3,7 @@ name = "noirc_errors" version.workspace = true authors.workspace = true edition.workspace = true +rust-version.workspace = true license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml index fad7c3c309e..aa30eef9156 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_evaluator/Cargo.toml @@ -3,6 +3,7 @@ name = "noirc_evaluator" version.workspace = true authors.workspace = true edition.workspace = true +rust-version.workspace = true license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -15,7 +16,7 @@ fxhash.workspace = true iter-extended.workspace = true thiserror.workspace = true num-bigint = "0.4" -im = { version = "15.1", features = ["serde"] } +im.workspace = true serde.workspace = true tracing.workspace = true -chrono = "0.4.37" \ No newline at end of file +chrono = "0.4.37" diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs index 760340f1a88..3482a8c25c7 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa.rs @@ -22,9 +22,8 @@ use acvm::acir::{ use noirc_errors::debug_info::{DebugFunctions, DebugInfo, DebugTypes, DebugVariables}; -use noirc_frontend::{ - hir_def::function::FunctionSignature, monomorphization::ast::Program, Visibility, -}; +use noirc_frontend::ast::Visibility; +use noirc_frontend::{hir_def::function::FunctionSignature, monomorphization::ast::Program}; use tracing::{span, Level}; use self::{acir_gen::GeneratedAcir, ssa_gen::Ssa}; diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 02b381d79fc..8b16ad51ca1 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -39,7 +39,7 @@ use acvm::{ use fxhash::FxHashMap as HashMap; use im::Vector; use iter_extended::{try_vecmap, vecmap}; -use noirc_frontend::Distinctness; +use noirc_frontend::ast::Distinctness; #[derive(Default)] struct SharedContext { @@ -2595,7 +2595,7 @@ mod test { let ssa = builder.finish(); let (acir_functions, _) = ssa - .into_acir(&Brillig::default(), noirc_frontend::Distinctness::Distinct) + .into_acir(&Brillig::default(), noirc_frontend::ast::Distinctness::Distinct) .expect("Should compile manually written SSA into ACIR"); // Expected result: // main f0 @@ -2691,7 +2691,7 @@ mod test { let ssa = builder.finish(); let (acir_functions, _) = ssa - .into_acir(&Brillig::default(), noirc_frontend::Distinctness::Distinct) + .into_acir(&Brillig::default(), noirc_frontend::ast::Distinctness::Distinct) .expect("Should compile manually written SSA into ACIR"); // The expected result should look very similar to the abvoe test expect that the input witnesses of the `Call` // opcodes will be different. The changes can discerned from the checks below. @@ -2782,7 +2782,7 @@ mod test { let ssa = builder.finish(); let (acir_functions, _) = ssa - .into_acir(&Brillig::default(), noirc_frontend::Distinctness::Distinct) + .into_acir(&Brillig::default(), noirc_frontend::ast::Distinctness::Distinct) .expect("Should compile manually written SSA into ACIR"); assert_eq!(acir_functions.len(), 3, "Should have three ACIR functions"); @@ -2894,7 +2894,7 @@ mod test { println!("{}", ssa); let (acir_functions, brillig_functions) = ssa - .into_acir(&brillig, noirc_frontend::Distinctness::Distinct) + .into_acir(&brillig, noirc_frontend::ast::Distinctness::Distinct) .expect("Should compile manually written SSA into ACIR"); assert_eq!(acir_functions.len(), 1, "Should only have a `main` ACIR function"); diff --git a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs index e785212f8d2..5f0660f5a79 100644 --- a/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs +++ b/noir/noir-repo/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs @@ -3,6 +3,7 @@ use std::rc::Rc; use crate::ssa::ir::{types::Type, value::ValueId}; use acvm::FieldElement; use fxhash::FxHashMap as HashMap; +use noirc_frontend::ast; use noirc_frontend::hir_def::function::FunctionSignature; use super::FunctionBuilder; @@ -33,8 +34,8 @@ impl DataBusBuilder { for param in &main_signature.0 { let is_databus = match param.2 { - noirc_frontend::Visibility::Public | noirc_frontend::Visibility::Private => false, - noirc_frontend::Visibility::DataBus => true, + ast::Visibility::Public | ast::Visibility::Private => false, + ast::Visibility::DataBus => true, }; let len = param.1.field_count() as usize; params_is_databus.extend(vec![is_databus; len]); 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 569e543ce40..e591a3d478c 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 @@ -4,9 +4,9 @@ use std::sync::{Mutex, RwLock}; use acvm::FieldElement; use iter_extended::vecmap; use noirc_errors::Location; +use noirc_frontend::ast::{BinaryOpKind, Signedness}; use noirc_frontend::monomorphization::ast::{self, LocalId, Parameters}; use noirc_frontend::monomorphization::ast::{FuncId, Program}; -use noirc_frontend::{BinaryOpKind, Signedness}; use crate::errors::RuntimeError; use crate::ssa::function_builder::FunctionBuilder; @@ -562,7 +562,7 @@ impl<'a> FunctionContext<'a> { pub(super) fn insert_binary( &mut self, mut lhs: ValueId, - operator: noirc_frontend::BinaryOpKind, + operator: BinaryOpKind, mut rhs: ValueId, location: Location, ) -> Values { @@ -625,7 +625,7 @@ impl<'a> FunctionContext<'a> { fn insert_array_equality( &mut self, lhs: ValueId, - operator: noirc_frontend::BinaryOpKind, + operator: BinaryOpKind, rhs: ValueId, location: Location, ) -> Values { @@ -1090,23 +1090,23 @@ impl<'a> FunctionContext<'a> { /// True if the given operator cannot be encoded directly and needs /// to be represented as !(some other operator) -fn operator_requires_not(op: noirc_frontend::BinaryOpKind) -> bool { - use noirc_frontend::BinaryOpKind::*; +fn operator_requires_not(op: BinaryOpKind) -> bool { + use BinaryOpKind::*; matches!(op, NotEqual | LessEqual | GreaterEqual) } /// True if the given operator cannot be encoded directly and needs /// to have its lhs and rhs swapped to be represented with another operator. /// Example: (a > b) needs to be represented as (b < a) -fn operator_requires_swapped_operands(op: noirc_frontend::BinaryOpKind) -> bool { - use noirc_frontend::BinaryOpKind::*; +fn operator_requires_swapped_operands(op: BinaryOpKind) -> bool { + use BinaryOpKind::*; matches!(op, Greater | LessEqual) } /// If the operation requires its result to be truncated because it is an integer, the maximum /// number of bits that result may occupy is returned. fn operator_result_max_bit_size_to_truncate( - op: noirc_frontend::BinaryOpKind, + op: BinaryOpKind, lhs: ValueId, rhs: ValueId, dfg: &DataFlowGraph, @@ -1123,7 +1123,7 @@ fn operator_result_max_bit_size_to_truncate( let lhs_bit_size = get_bit_size(lhs_type)?; let rhs_bit_size = get_bit_size(rhs_type)?; - use noirc_frontend::BinaryOpKind::*; + use BinaryOpKind::*; match op { Add => Some(std::cmp::max(lhs_bit_size, rhs_bit_size) + 1), Subtract => Some(std::cmp::max(lhs_bit_size, rhs_bit_size) + 1), @@ -1173,7 +1173,7 @@ fn operator_result_max_bit_size_to_truncate( /// Take care when using this to insert a binary instruction: this requires /// checking operator_requires_not and operator_requires_swapped_operands /// to represent the full operation correctly. -fn convert_operator(op: noirc_frontend::BinaryOpKind) -> BinaryOp { +fn convert_operator(op: BinaryOpKind) -> BinaryOp { match op { BinaryOpKind::Add => BinaryOp::Add, BinaryOpKind::Subtract => BinaryOp::Sub, 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 2a4b5276547..79f7cda4ae2 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 @@ -7,10 +7,8 @@ pub(crate) use program::Ssa; use context::SharedContext; use iter_extended::{try_vecmap, vecmap}; use noirc_errors::Location; -use noirc_frontend::{ - monomorphization::ast::{self, Expression, Program}, - Visibility, -}; +use noirc_frontend::ast::{UnaryOp, Visibility}; +use noirc_frontend::monomorphization::ast::{self, Expression, Program}; use crate::{ errors::{InternalError, RuntimeError}, @@ -306,24 +304,24 @@ impl<'a> FunctionContext<'a> { fn codegen_unary(&mut self, unary: &ast::Unary) -> Result { match unary.operator { - noirc_frontend::UnaryOp::Not => { + UnaryOp::Not => { let rhs = self.codegen_expression(&unary.rhs)?; let rhs = rhs.into_leaf().eval(self); Ok(self.builder.insert_not(rhs).into()) } - noirc_frontend::UnaryOp::Minus => { + UnaryOp::Minus => { let rhs = self.codegen_expression(&unary.rhs)?; let rhs = rhs.into_leaf().eval(self); let typ = self.builder.type_of_value(rhs); let zero = self.builder.numeric_constant(0u128, typ); Ok(self.insert_binary( zero, - noirc_frontend::BinaryOpKind::Subtract, + noirc_frontend::ast::BinaryOpKind::Subtract, rhs, unary.location, )) } - noirc_frontend::UnaryOp::MutableReference => { + UnaryOp::MutableReference => { Ok(self.codegen_reference(&unary.rhs)?.map(|rhs| { match rhs { value::Value::Normal(value) => { @@ -338,7 +336,7 @@ impl<'a> FunctionContext<'a> { } })) } - noirc_frontend::UnaryOp::Dereference { .. } => { + UnaryOp::Dereference { .. } => { let rhs = self.codegen_expression(&unary.rhs)?; Ok(self.dereference(&rhs, &unary.result_type)) } diff --git a/noir/noir-repo/compiler/noirc_frontend/Cargo.toml b/noir/noir-repo/compiler/noirc_frontend/Cargo.toml index e39ab2fe106..84c9393fa37 100644 --- a/noir/noir-repo/compiler/noirc_frontend/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_frontend/Cargo.toml @@ -3,6 +3,7 @@ name = "noirc_frontend" version.workspace = true authors.workspace = true edition.workspace = true +rust-version.workspace = true license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -17,6 +18,7 @@ iter-extended.workspace = true chumsky.workspace = true thiserror.workspace = true smol_str.workspace = true +im.workspace = true serde_json.workspace = true serde.workspace = true rustc-hash = "1.1.0" 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 0e5919bf7db..92c1add80a6 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs @@ -1,11 +1,11 @@ use std::borrow::Cow; use std::fmt::Display; -use crate::token::{Attributes, Token}; -use crate::{ +use crate::ast::{ Distinctness, Ident, ItemVisibility, Path, Pattern, Recoverable, Statement, StatementKind, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, Visibility, }; +use crate::token::{Attributes, Token}; use acvm::FieldElement; use iter_extended::vecmap; use noirc_errors::{Span, Spanned}; @@ -387,6 +387,9 @@ pub struct FunctionDefinition { /// True if this function was defined with the 'unconstrained' keyword pub is_unconstrained: bool, + /// True if this function was defined with the 'comptime' keyword + pub is_comptime: bool, + /// Indicate if this function was defined with the 'pub' keyword pub visibility: ItemVisibility, @@ -679,10 +682,12 @@ impl FunctionDefinition { span: ident.span().merge(unresolved_type.span.unwrap()), }) .collect(); + FunctionDefinition { name: name.clone(), attributes: Attributes::empty(), is_unconstrained: false, + is_comptime: false, visibility: ItemVisibility::Private, generics: generics.clone(), parameters: p, 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 10d962a23f7..9816218c5f7 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/function.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/function.rs @@ -3,8 +3,8 @@ use std::fmt::Display; use noirc_errors::Span; use crate::{ + ast::{FunctionReturnType, Ident, Param, Visibility}, token::{Attributes, FunctionAttribute, SecondaryAttribute}, - FunctionReturnType, Ident, Param, Visibility, }; use super::{FunctionDefinition, UnresolvedType, UnresolvedTypeData}; 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 753b5a31d32..1831a046f5b 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/statement.rs @@ -1,17 +1,18 @@ use std::fmt::Display; use std::sync::atomic::{AtomicU32, Ordering}; +use acvm::FieldElement; +use iter_extended::vecmap; +use noirc_errors::{Span, Spanned}; + +use super::{ + BlockExpression, Expression, ExpressionKind, IndexExpression, MemberAccessExpression, + MethodCallExpression, UnresolvedType, +}; use crate::lexer::token::SpannedToken; use crate::macros_api::SecondaryAttribute; use crate::parser::{ParserError, ParserErrorReason}; use crate::token::Token; -use crate::{ - BlockExpression, Expression, ExpressionKind, IndexExpression, MemberAccessExpression, - MethodCallExpression, UnresolvedType, -}; -use acvm::FieldElement; -use iter_extended::vecmap; -use noirc_errors::{Span, Spanned}; /// This is used when an identifier fails to parse in the parser. /// Instead of failing the parse, we can often recover using this @@ -38,6 +39,8 @@ pub enum StatementKind { For(ForLoopStatement), Break, Continue, + /// This statement should be executed at compile-time + Comptime(Box), // This is an expression with a trailing semi-colon Semi(Expression), // This statement is the result of a recovered parse error. @@ -47,6 +50,19 @@ pub enum StatementKind { } impl Statement { + pub fn add_semicolon( + mut self, + semi: Option, + span: Span, + 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 + } +} + +impl StatementKind { pub fn add_semicolon( self, semi: Option, @@ -57,7 +73,7 @@ impl Statement { let missing_semicolon = ParserError::with_reason(ParserErrorReason::MissingSeparatingSemi, span); - let kind = match self.kind { + match self { StatementKind::Let(_) | StatementKind::Constrain(_) | StatementKind::Assign(_) @@ -69,10 +85,15 @@ impl Statement { if semi.is_none() { emit_error(missing_semicolon); } - self.kind + self + } + StatementKind::Comptime(mut statement) => { + *statement = + statement.add_semicolon(semi, span, last_statement_in_block, emit_error); + StatementKind::Comptime(statement) } // A semicolon on a for loop is optional and does nothing - StatementKind::For(_) => self.kind, + StatementKind::For(_) => self, StatementKind::Expression(expr) => { match (&expr.kind, semi, last_statement_in_block) { @@ -92,9 +113,7 @@ impl Statement { (_, None, true) => StatementKind::Expression(expr), } } - }; - - Statement { kind, span: self.span } + } } } @@ -108,7 +127,13 @@ impl StatementKind { pub fn new_let( ((pattern, r#type), expression): ((Pattern, UnresolvedType), Expression), ) -> StatementKind { - StatementKind::Let(LetStatement { pattern, r#type, expression, attributes: vec![] }) + StatementKind::Let(LetStatement { + pattern, + r#type, + expression, + comptime: false, + attributes: vec![], + }) } /// Create a Statement::Assign value, desugaring any combined operators like += if needed. @@ -124,7 +149,7 @@ impl StatementKind { let lvalue_expr = lvalue.as_expression(); let error_msg = "Token passed to Statement::assign is not a binary operator"; - let infix = crate::InfixExpression { + let infix = crate::ast::InfixExpression { lhs: lvalue_expr, operator: operator.try_into_binary_op(span).expect(error_msg), rhs: expression, @@ -407,17 +432,9 @@ pub struct LetStatement { pub r#type: UnresolvedType, pub expression: Expression, pub attributes: Vec, -} -impl LetStatement { - pub fn new_let( - (((pattern, r#type), expression), attributes): ( - ((Pattern, UnresolvedType), Expression), - Vec, - ), - ) -> LetStatement { - LetStatement { pattern, r#type, expression, attributes } - } + // True if this should only be run during compile-time + pub comptime: bool, } #[derive(Debug, PartialEq, Eq, Clone)] @@ -505,8 +522,8 @@ impl LValue { })) } LValue::Dereference(lvalue, _span) => { - ExpressionKind::Prefix(Box::new(crate::PrefixExpression { - operator: crate::UnaryOp::Dereference { implicitly_added: false }, + ExpressionKind::Prefix(Box::new(crate::ast::PrefixExpression { + operator: crate::ast::UnaryOp::Dereference { implicitly_added: false }, rhs: lvalue.as_expression(), })) } @@ -573,6 +590,7 @@ impl ForRange { pattern: Pattern::Identifier(array_ident.clone()), r#type: UnresolvedType::unspecified(), expression: array, + comptime: false, attributes: vec![], }), span: array_span, @@ -616,6 +634,7 @@ impl ForRange { pattern: Pattern::Identifier(identifier), r#type: UnresolvedType::unspecified(), expression: Expression::new(loop_element, array_span), + comptime: false, attributes: vec![], }), span: array_span, @@ -666,6 +685,7 @@ impl Display for StatementKind { StatementKind::For(for_loop) => for_loop.fmt(f), StatementKind::Break => write!(f, "break"), StatementKind::Continue => write!(f, "continue"), + StatementKind::Comptime(statement) => write!(f, "comptime {statement}"), StatementKind::Semi(semi) => write!(f, "{semi};"), StatementKind::Error => write!(f, "Error"), } 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 6a32fa717f3..bda6b8c0b11 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/structure.rs @@ -1,6 +1,8 @@ use std::fmt::Display; -use crate::{token::SecondaryAttribute, Ident, UnresolvedGenerics, UnresolvedType}; +use crate::ast::{Ident, UnresolvedGenerics, UnresolvedType}; +use crate::token::SecondaryAttribute; + use iter_extended::vecmap; use noirc_errors::Span; 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 775f0a5f2b4..772675723b5 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/traits.rs @@ -3,10 +3,11 @@ use std::fmt::Display; use iter_extended::vecmap; use noirc_errors::Span; -use crate::{ - node_interner::TraitId, BlockExpression, Expression, FunctionReturnType, Ident, NoirFunction, - Path, UnresolvedGenerics, UnresolvedType, +use crate::ast::{ + BlockExpression, Expression, FunctionReturnType, Ident, NoirFunction, Path, UnresolvedGenerics, + UnresolvedType, }; +use crate::node_interner::TraitId; /// AST node for trait definitions: /// `trait name { ... items ... }` 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 76a1e5a7e30..3228765170e 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,4 +1,4 @@ -use crate::{Ident, UnresolvedGenerics, UnresolvedType}; +use super::{Ident, UnresolvedGenerics, UnresolvedType}; use iter_extended::vecmap; use noirc_errors::Span; use std::fmt::Display; 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 67b52071d7b..3e7d123398b 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/debug/mod.rs @@ -145,6 +145,7 @@ impl DebugInstrumenter { pattern: ast::Pattern::Identifier(ident("__debug_expr", ret_expr.span)), r#type: ast::UnresolvedType::unspecified(), expression: ret_expr.clone(), + comptime: false, attributes: vec![], }), span: ret_expr.span, @@ -243,6 +244,7 @@ impl DebugInstrumenter { kind: ast::StatementKind::Let(ast::LetStatement { pattern: ast::Pattern::Tuple(vars_pattern, let_stmt.pattern.span()), r#type: ast::UnresolvedType::unspecified(), + comptime: false, expression: ast::Expression { kind: ast::ExpressionKind::Block(ast::BlockExpression { statements: block_stmts, @@ -275,6 +277,7 @@ impl DebugInstrumenter { pattern: ast::Pattern::Identifier(ident("__debug_expr", assign_stmt.expression.span)), r#type: ast::UnresolvedType::unspecified(), expression: assign_stmt.expression.clone(), + comptime: false, attributes: vec![], }); let expression_span = assign_stmt.expression.span; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs index 8ffcbce7d62..47ca7083ff0 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs @@ -1,18 +1,19 @@ use iter_extended::vecmap; use noirc_errors::{Span, Spanned}; +use crate::ast::{ + ArrayLiteral, AssignStatement, BlockExpression, CallExpression, CastExpression, ConstrainKind, + ConstructorExpression, ExpressionKind, ForLoopStatement, ForRange, Ident, IfExpression, + IndexExpression, InfixExpression, LValue, Lambda, LetStatement, Literal, + MemberAccessExpression, MethodCallExpression, Path, Pattern, PrefixExpression, UnresolvedType, + UnresolvedTypeData, UnresolvedTypeExpression, +}; use crate::ast::{ConstrainStatement, Expression, Statement, StatementKind}; use crate::hir_def::expr::{HirArrayLiteral, HirExpression, HirIdent}; use crate::hir_def::stmt::{HirLValue, HirPattern, HirStatement}; +use crate::hir_def::types::Type; use crate::macros_api::HirLiteral; use crate::node_interner::{ExprId, NodeInterner, StmtId}; -use crate::{ - ArrayLiteral, AssignStatement, BlockExpression, CallExpression, CastExpression, ConstrainKind, - ConstructorExpression, ExpressionKind, ForLoopStatement, ForRange, Ident, IfExpression, - IndexExpression, InfixExpression, LValue, Lambda, LetStatement, Literal, - MemberAccessExpression, MethodCallExpression, Path, Pattern, PrefixExpression, Type, - UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, -}; // TODO: // - Full path for idents & types @@ -36,6 +37,7 @@ impl StmtId { pattern, r#type, expression, + comptime: false, attributes: Vec::new(), }) } @@ -64,6 +66,9 @@ impl StmtId { HirStatement::Expression(expr) => StatementKind::Expression(expr.to_ast(interner)), HirStatement::Semi(expr) => StatementKind::Semi(expr.to_ast(interner)), HirStatement::Error => StatementKind::Error, + HirStatement::Comptime(statement) => { + StatementKind::Comptime(Box::new(statement.to_ast(interner).kind)) + } }; Statement { kind, span } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs new file mode 100644 index 00000000000..03839c2f0cd --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -0,0 +1,1294 @@ +use std::{borrow::Cow, collections::hash_map::Entry, rc::Rc}; + +use acvm::FieldElement; +use im::Vector; +use iter_extended::{try_vecmap, vecmap}; +use noirc_errors::Location; +use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet}; + +use crate::ast::{BinaryOpKind, BlockExpression, FunctionKind, IntegerBitSize, Signedness}; +use crate::{ + hir_def::{ + expr::{ + HirArrayLiteral, HirBlockExpression, HirCallExpression, HirCastExpression, + HirConstructorExpression, HirIdent, HirIfExpression, HirIndexExpression, + HirInfixExpression, HirLambda, HirMemberAccess, HirMethodCallExpression, + HirPrefixExpression, + }, + stmt::{ + HirAssignStatement, HirConstrainStatement, HirForStatement, HirLValue, HirLetStatement, + HirPattern, + }, + }, + macros_api::{HirExpression, HirLiteral, HirStatement, NodeInterner}, + node_interner::{DefinitionId, DefinitionKind, ExprId, FuncId, StmtId}, +}; +use crate::{Shared, Type, TypeBinding, TypeBindings, TypeVariableKind}; +#[allow(unused)] +pub(crate) struct Interpreter<'interner> { + /// To expand macros the Interpreter may mutate hir nodes within the NodeInterner + interner: &'interner mut NodeInterner, + + /// Each value currently in scope in the interpreter. + /// Each element of the Vec represents a scope with every scope together making + /// up all currently visible definitions. + scopes: Vec>, + + /// True if we've expanded any macros into any functions and will need + /// to redo name resolution & type checking for that function. + changed_functions: HashSet, + + /// True if we've expanded any macros into global scope and will need + /// to redo name resolution & type checking for everything. + changed_globally: bool, + + in_loop: bool, + + /// True if we're currently in a compile-time context. + /// If this is false code is skipped over instead of executed. + in_comptime_context: bool, +} + +#[allow(unused)] +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum Value { + Unit, + Bool(bool), + Field(FieldElement), + I8(i8), + I32(i32), + I64(i64), + U8(u8), + U32(u32), + U64(u64), + String(Rc), + Function(FuncId, Type), + Closure(HirLambda, Vec, Type), + Tuple(Vec), + Struct(HashMap, Value>, Type), + Pointer(Shared), + Array(Vector, Type), + Slice(Vector, Type), + Code(Rc), +} + +/// The possible errors that can halt the interpreter. +#[allow(unused)] +#[derive(Debug)] +pub(crate) enum InterpreterError { + ArgumentCountMismatch { expected: usize, actual: usize, call_location: Location }, + TypeMismatch { expected: Type, value: Value, location: Location }, + NoValueForId { id: DefinitionId, location: Location }, + IntegerOutOfRangeForType { value: FieldElement, typ: Type, location: Location }, + ErrorNodeEncountered { location: Location }, + NonFunctionCalled { value: Value, location: Location }, + NonBoolUsedInIf { value: Value, location: Location }, + NonBoolUsedInConstrain { value: Value, location: Location }, + FailingConstraint { message: Option, location: Location }, + NoMethodFound { object: Value, typ: Type, location: Location }, + NonIntegerUsedInLoop { value: Value, location: Location }, + NonPointerDereferenced { value: Value, location: Location }, + NonTupleOrStructInMemberAccess { value: Value, location: Location }, + NonArrayIndexed { value: Value, location: Location }, + NonIntegerUsedAsIndex { value: Value, location: Location }, + NonIntegerIntegerLiteral { typ: Type, location: Location }, + NonIntegerArrayLength { typ: Type, location: Location }, + NonNumericCasted { value: Value, location: Location }, + IndexOutOfBounds { index: usize, length: usize, location: Location }, + ExpectedStructToHaveField { value: Value, field_name: String, location: Location }, + TypeUnsupported { typ: Type, location: Location }, + InvalidValueForUnary { value: Value, operator: &'static str, location: Location }, + InvalidValuesForBinary { lhs: Value, rhs: Value, operator: &'static str, location: Location }, + CastToNonNumericType { typ: Type, location: Location }, + + // Perhaps this should be unreachable! due to type checking also preventing this error? + // Currently it and the Continue variant are the only interpreter errors without a Location field + BreakNotInLoop, + ContinueNotInLoop, + + // These cases are not errors but prevent us from running more code + // until the loop can be resumed properly. + Break, + Continue, +} + +#[allow(unused)] +type IResult = std::result::Result; + +#[allow(unused)] +impl<'a> Interpreter<'a> { + pub(crate) fn new(interner: &'a mut NodeInterner) -> Self { + Self { + interner, + scopes: vec![HashMap::default()], + changed_functions: HashSet::default(), + changed_globally: false, + in_loop: false, + in_comptime_context: false, + } + } + + pub(crate) fn call_function( + &mut self, + function: FuncId, + arguments: Vec<(Value, Location)>, + call_location: Location, + ) -> IResult { + let previous_state = self.enter_function(); + + let meta = self.interner.function_meta(&function); + if meta.kind != FunctionKind::Normal { + todo!("Evaluation for {:?} is unimplemented", meta.kind); + } + + if meta.parameters.len() != arguments.len() { + return Err(InterpreterError::ArgumentCountMismatch { + expected: meta.parameters.len(), + actual: arguments.len(), + call_location, + }); + } + + let parameters = meta.parameters.0.clone(); + for ((parameter, typ, _), (argument, arg_location)) in parameters.iter().zip(arguments) { + self.define_pattern(parameter, typ, argument, arg_location)?; + } + + let function_body = self.interner.function(&function).as_expr(); + let result = self.evaluate(function_body)?; + + self.exit_function(previous_state); + Ok(result) + } + + fn call_closure( + &mut self, + closure: HirLambda, + // TODO: How to define environment here? + _environment: Vec, + arguments: Vec<(Value, Location)>, + call_location: Location, + ) -> IResult { + let previous_state = self.enter_function(); + + if closure.parameters.len() != arguments.len() { + return Err(InterpreterError::ArgumentCountMismatch { + expected: closure.parameters.len(), + actual: arguments.len(), + call_location, + }); + } + + let parameters = closure.parameters.iter().zip(arguments); + for ((parameter, typ), (argument, arg_location)) in parameters { + self.define_pattern(parameter, typ, argument, arg_location)?; + } + + let result = self.evaluate(closure.body)?; + + self.exit_function(previous_state); + Ok(result) + } + + /// Enters a function, pushing a new scope and resetting any required state. + /// Returns the previous values of the internal state, to be reset when + /// `exit_function` is called. + fn enter_function(&mut self) -> (bool, Vec>) { + // Drain every scope except the global scope + let scope = self.scopes.drain(1..).collect(); + self.push_scope(); + (std::mem::take(&mut self.in_loop), scope) + } + + fn exit_function(&mut self, mut state: (bool, Vec>)) { + self.in_loop = state.0; + + // Keep only the global scope + self.scopes.truncate(1); + self.scopes.append(&mut state.1); + } + + fn push_scope(&mut self) { + self.scopes.push(HashMap::default()); + } + + fn pop_scope(&mut self) { + self.scopes.pop(); + } + + fn current_scope_mut(&mut self) -> &mut HashMap { + // the global scope is always at index zero, so this is always Some + self.scopes.last_mut().unwrap() + } + + fn define_pattern( + &mut self, + pattern: &HirPattern, + typ: &Type, + argument: Value, + location: Location, + ) -> IResult<()> { + match pattern { + HirPattern::Identifier(identifier) => { + self.define(identifier.id, typ, argument, location) + } + HirPattern::Mutable(pattern, _) => { + self.define_pattern(pattern, typ, argument, location) + } + HirPattern::Tuple(pattern_fields, _) => match (argument, typ) { + (Value::Tuple(fields), Type::Tuple(type_fields)) + if fields.len() == pattern_fields.len() => + { + for ((pattern, typ), argument) in + pattern_fields.iter().zip(type_fields).zip(fields) + { + self.define_pattern(pattern, typ, argument, location)?; + } + Ok(()) + } + (value, _) => { + Err(InterpreterError::TypeMismatch { expected: typ.clone(), value, location }) + } + }, + HirPattern::Struct(struct_type, pattern_fields, _) => { + self.type_check(typ, &argument, location)?; + self.type_check(struct_type, &argument, location)?; + + match argument { + Value::Struct(fields, struct_type) if fields.len() == pattern_fields.len() => { + for (field_name, field_pattern) in pattern_fields { + let field = fields.get(&field_name.0.contents).ok_or_else(|| { + InterpreterError::ExpectedStructToHaveField { + value: Value::Struct(fields.clone(), struct_type.clone()), + field_name: field_name.0.contents.clone(), + location, + } + })?; + + let field_type = field.get_type().into_owned(); + self.define_pattern( + field_pattern, + &field_type, + field.clone(), + location, + )?; + } + Ok(()) + } + value => Err(InterpreterError::TypeMismatch { + expected: typ.clone(), + value, + location, + }), + } + } + } + } + + /// Define a new variable in the current scope + fn define( + &mut self, + id: DefinitionId, + typ: &Type, + argument: Value, + location: Location, + ) -> IResult<()> { + self.type_check(typ, &argument, location)?; + self.current_scope_mut().insert(id, argument); + Ok(()) + } + + /// Mutate an existing variable, potentially from a prior scope. + /// Also type checks the value being assigned + fn checked_mutate( + &mut self, + id: DefinitionId, + typ: &Type, + argument: Value, + location: Location, + ) -> IResult<()> { + self.type_check(typ, &argument, location)?; + for scope in self.scopes.iter_mut().rev() { + if let Entry::Occupied(mut entry) = scope.entry(id) { + entry.insert(argument); + return Ok(()); + } + } + Err(InterpreterError::NoValueForId { id, location }) + } + + /// Mutate an existing variable, potentially from a prior scope + fn mutate(&mut self, id: DefinitionId, argument: Value, location: Location) -> IResult<()> { + for scope in self.scopes.iter_mut().rev() { + if let Entry::Occupied(mut entry) = scope.entry(id) { + entry.insert(argument); + return Ok(()); + } + } + Err(InterpreterError::NoValueForId { id, location }) + } + + fn lookup(&self, ident: &HirIdent) -> IResult { + for scope in self.scopes.iter().rev() { + if let Some(value) = scope.get(&ident.id) { + return Ok(value.clone()); + } + } + + Err(InterpreterError::NoValueForId { id: ident.id, location: ident.location }) + } + + fn lookup_id(&self, id: DefinitionId, location: Location) -> IResult { + for scope in self.scopes.iter().rev() { + if let Some(value) = scope.get(&id) { + return Ok(value.clone()); + } + } + + Err(InterpreterError::NoValueForId { id, location }) + } + + fn type_check(&self, typ: &Type, value: &Value, location: Location) -> IResult<()> { + let typ = typ.follow_bindings(); + let value_type = value.get_type(); + + typ.try_unify(&value_type, &mut TypeBindings::new()).map_err(|_| { + InterpreterError::TypeMismatch { expected: typ, value: value.clone(), location } + }) + } + + /// Evaluate an expression and return the result + fn evaluate(&mut self, id: ExprId) -> IResult { + match self.interner.expression(&id) { + HirExpression::Ident(ident) => self.evaluate_ident(ident, id), + HirExpression::Literal(literal) => self.evaluate_literal(literal, id), + HirExpression::Block(block) => self.evaluate_block(block), + HirExpression::Prefix(prefix) => self.evaluate_prefix(prefix, id), + HirExpression::Infix(infix) => self.evaluate_infix(infix, id), + HirExpression::Index(index) => self.evaluate_index(index, id), + HirExpression::Constructor(constructor) => self.evaluate_constructor(constructor, id), + HirExpression::MemberAccess(access) => self.evaluate_access(access, id), + HirExpression::Call(call) => self.evaluate_call(call, id), + HirExpression::MethodCall(call) => self.evaluate_method_call(call, id), + HirExpression::Cast(cast) => self.evaluate_cast(cast, id), + HirExpression::If(if_) => self.evaluate_if(if_, id), + HirExpression::Tuple(tuple) => self.evaluate_tuple(tuple), + HirExpression::Lambda(lambda) => self.evaluate_lambda(lambda, id), + HirExpression::Quote(block) => Ok(Value::Code(Rc::new(block))), + HirExpression::Error => { + let location = self.interner.expr_location(&id); + Err(InterpreterError::ErrorNodeEncountered { location }) + } + } + } + + fn evaluate_ident(&mut self, ident: HirIdent, id: ExprId) -> IResult { + let definition = self.interner.definition(ident.id); + + match &definition.kind { + DefinitionKind::Function(function_id) => { + let typ = self.interner.id_type(id); + Ok(Value::Function(*function_id, typ)) + } + DefinitionKind::Local(_) => dbg!(self.lookup(&ident)), + DefinitionKind::Global(global_id) => { + let let_ = self.interner.get_global_let_statement(*global_id).unwrap(); + self.evaluate_let(let_)?; + self.lookup(&ident) + } + DefinitionKind::GenericType(type_variable) => { + let value = match &*type_variable.borrow() { + TypeBinding::Unbound(_) => None, + TypeBinding::Bound(binding) => binding.evaluate_to_u64(), + }; + + if let Some(value) = value { + let typ = self.interner.id_type(id); + self.evaluate_integer((value as u128).into(), false, id) + } else { + let location = self.interner.expr_location(&id); + let typ = Type::TypeVariable(type_variable.clone(), TypeVariableKind::Normal); + Err(InterpreterError::NonIntegerArrayLength { typ, location }) + } + } + } + } + + fn evaluate_literal(&mut self, literal: HirLiteral, id: ExprId) -> IResult { + 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::Str(string) => Ok(Value::String(Rc::new(string))), + HirLiteral::FmtStr(_, _) => todo!("Evaluate format strings"), + HirLiteral::Array(array) => self.evaluate_array(array, id), + HirLiteral::Slice(array) => self.evaluate_slice(array, id), + } + } + + fn evaluate_integer( + &self, + value: FieldElement, + is_negative: bool, + id: ExprId, + ) -> IResult { + let typ = self.interner.id_type(id).follow_bindings(); + let location = self.interner.expr_location(&id); + + if let Type::FieldElement = &typ { + Ok(Value::Field(value)) + } 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 }; + Ok(Value::U8(value)) + } + (Signedness::Unsigned, IntegerBitSize::ThirtyTwo) => { + let value: u32 = + value.try_to_u64().and_then(|value| value.try_into().ok()).ok_or( + InterpreterError::IntegerOutOfRangeForType { value, typ, location }, + )?; + let value = if is_negative { 0u32.wrapping_sub(value) } else { value }; + 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 }; + Ok(Value::U64(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 }; + Ok(Value::I8(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 }; + 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 }; + Ok(Value::I64(value)) + } + } + } else { + Err(InterpreterError::NonIntegerIntegerLiteral { typ, location }) + } + } + + fn evaluate_block(&mut self, mut block: HirBlockExpression) -> IResult { + let last_statement = block.statements.pop(); + self.push_scope(); + + for statement in block.statements { + self.evaluate_statement(statement)?; + } + + let result = if let Some(statement) = last_statement { + self.evaluate_statement(statement) + } else { + Ok(Value::Unit) + }; + + self.pop_scope(); + result + } + + fn evaluate_array(&mut self, array: HirArrayLiteral, id: ExprId) -> IResult { + let typ = self.interner.id_type(id); + + match array { + HirArrayLiteral::Standard(elements) => { + let elements = elements + .into_iter() + .map(|id| self.evaluate(id)) + .collect::>>()?; + + Ok(Value::Array(elements, typ)) + } + HirArrayLiteral::Repeated { repeated_element, length } => { + let element = self.evaluate(repeated_element)?; + + if let Some(length) = length.evaluate_to_u64() { + let elements = (0..length).map(|_| element.clone()).collect(); + Ok(Value::Array(elements, typ)) + } else { + let location = self.interner.expr_location(&id); + Err(InterpreterError::NonIntegerArrayLength { typ: length, location }) + } + } + } + } + + fn evaluate_slice(&mut self, array: HirArrayLiteral, id: ExprId) -> IResult { + self.evaluate_array(array, id).map(|value| match value { + Value::Array(array, typ) => Value::Slice(array, typ), + other => unreachable!("Non-array value returned from evaluate array: {other:?}"), + }) + } + + fn evaluate_prefix(&mut self, prefix: HirPrefixExpression, id: ExprId) -> IResult { + let rhs = self.evaluate(prefix.rhs)?; + match prefix.operator { + crate::ast::UnaryOp::Minus => match rhs { + Value::Field(value) => Ok(Value::Field(FieldElement::zero() - value)), + Value::I8(value) => Ok(Value::I8(-value)), + Value::I32(value) => Ok(Value::I32(-value)), + Value::I64(value) => Ok(Value::I64(-value)), + Value::U8(value) => Ok(Value::U8(0 - value)), + Value::U32(value) => Ok(Value::U32(0 - value)), + Value::U64(value) => Ok(Value::U64(0 - value)), + value => { + let location = self.interner.expr_location(&id); + Err(InterpreterError::InvalidValueForUnary { + value, + location, + operator: "minus", + }) + } + }, + crate::ast::UnaryOp::Not => match rhs { + Value::Bool(value) => Ok(Value::Bool(!value)), + Value::I8(value) => Ok(Value::I8(!value)), + Value::I32(value) => Ok(Value::I32(!value)), + Value::I64(value) => Ok(Value::I64(!value)), + Value::U8(value) => Ok(Value::U8(!value)), + Value::U32(value) => Ok(Value::U32(!value)), + Value::U64(value) => Ok(Value::U64(!value)), + value => { + let location = self.interner.expr_location(&id); + Err(InterpreterError::InvalidValueForUnary { value, location, operator: "not" }) + } + }, + crate::ast::UnaryOp::MutableReference => Ok(Value::Pointer(Shared::new(rhs))), + crate::ast::UnaryOp::Dereference { implicitly_added: _ } => match rhs { + Value::Pointer(element) => Ok(element.borrow().clone()), + value => { + let location = self.interner.expr_location(&id); + Err(InterpreterError::NonPointerDereferenced { value, location }) + } + }, + } + } + + fn evaluate_infix(&mut self, infix: HirInfixExpression, id: ExprId) -> IResult { + let lhs = self.evaluate(infix.lhs)?; + let rhs = self.evaluate(infix.rhs)?; + + // TODO: Need to account for operator overloading + assert!( + self.interner.get_selected_impl_for_expression(id).is_none(), + "Operator overloading is unimplemented in the interpreter" + ); + + use InterpreterError::InvalidValuesForBinary; + match infix.operator.kind { + BinaryOpKind::Add => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs + rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs + rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs + rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs + rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs + rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs + rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs + rhs)), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "+" }) + } + }, + BinaryOpKind::Subtract => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs - rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs - rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs - rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs - rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs - rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs - rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs - rhs)), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "-" }) + } + }, + BinaryOpKind::Multiply => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs * rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs * rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs * rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs * rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs * rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs * rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs * rhs)), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "*" }) + } + }, + BinaryOpKind::Divide => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Field(lhs / rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs / rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs / rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs / rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs / rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs / rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs / rhs)), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "/" }) + } + }, + BinaryOpKind::Equal => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs == rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs == rhs)), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "==" }) + } + }, + BinaryOpKind::NotEqual => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs != rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs != rhs)), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "!=" }) + } + }, + BinaryOpKind::Less => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs < rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs < rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs < rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs < rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs < rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs < rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs < rhs)), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "<" }) + } + }, + BinaryOpKind::LessEqual => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs <= rhs)), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "<=" }) + } + }, + BinaryOpKind::Greater => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs > rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs > rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs > rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs > rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs > rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs > rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs > rhs)), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: ">" }) + } + }, + BinaryOpKind::GreaterEqual => match (lhs, rhs) { + (Value::Field(lhs), Value::Field(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::Bool(lhs >= rhs)), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: ">=" }) + } + }, + BinaryOpKind::And => match (lhs, rhs) { + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs & rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs & rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs & rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs & rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs & rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs & rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs & rhs)), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "&" }) + } + }, + BinaryOpKind::Or => match (lhs, rhs) { + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs | rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs | rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs | rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs | rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs | rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs | rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs | rhs)), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "|" }) + } + }, + BinaryOpKind::Xor => match (lhs, rhs) { + (Value::Bool(lhs), Value::Bool(rhs)) => Ok(Value::Bool(lhs ^ rhs)), + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs ^ rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs ^ rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs ^ rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs ^ rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs ^ rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs ^ rhs)), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "^" }) + } + }, + BinaryOpKind::ShiftRight => match (lhs, rhs) { + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs >> rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs >> rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs >> rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs >> rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs >> rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs >> rhs)), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: ">>" }) + } + }, + BinaryOpKind::ShiftLeft => match (lhs, rhs) { + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs << rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs << rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs << rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs << rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs << rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs << rhs)), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "<<" }) + } + }, + BinaryOpKind::Modulo => match (lhs, rhs) { + (Value::I8(lhs), Value::I8(rhs)) => Ok(Value::I8(lhs % rhs)), + (Value::I32(lhs), Value::I32(rhs)) => Ok(Value::I32(lhs % rhs)), + (Value::I64(lhs), Value::I64(rhs)) => Ok(Value::I64(lhs % rhs)), + (Value::U8(lhs), Value::U8(rhs)) => Ok(Value::U8(lhs % rhs)), + (Value::U32(lhs), Value::U32(rhs)) => Ok(Value::U32(lhs % rhs)), + (Value::U64(lhs), Value::U64(rhs)) => Ok(Value::U64(lhs % rhs)), + (lhs, rhs) => { + let location = self.interner.expr_location(&id); + Err(InvalidValuesForBinary { lhs, rhs, location, operator: "%" }) + } + }, + } + } + + fn evaluate_index(&mut self, index: HirIndexExpression, id: ExprId) -> IResult { + let array = self.evaluate(index.collection)?; + let index = self.evaluate(index.index)?; + + let location = self.interner.expr_location(&id); + let (array, index) = self.bounds_check(array, index, location)?; + + Ok(array[index].clone()) + } + + /// Bounds check the given array and index pair. + /// This will also ensure the given arguments are in fact an array and integer. + fn bounds_check( + &self, + array: Value, + index: Value, + location: Location, + ) -> IResult<(Vector, usize)> { + let collection = match array { + Value::Array(array, _) => array, + Value::Slice(array, _) => array, + value => { + return Err(InterpreterError::NonArrayIndexed { value, location }); + } + }; + + let index = match index { + Value::Field(value) => { + value.try_to_u64().expect("index could not fit into u64") as usize + } + Value::I8(value) => value as usize, + Value::I32(value) => value as usize, + Value::I64(value) => value as usize, + Value::U8(value) => value as usize, + Value::U32(value) => value as usize, + Value::U64(value) => value as usize, + value => { + return Err(InterpreterError::NonIntegerUsedAsIndex { value, location }); + } + }; + + if index >= collection.len() { + use InterpreterError::IndexOutOfBounds; + return Err(IndexOutOfBounds { index, location, length: collection.len() }); + } + + Ok((collection, index)) + } + + fn evaluate_constructor( + &mut self, + constructor: HirConstructorExpression, + id: ExprId, + ) -> IResult { + let fields = constructor + .fields + .into_iter() + .map(|(name, expr)| { + let field_value = self.evaluate(expr)?; + Ok((Rc::new(name.0.contents), field_value)) + }) + .collect::>()?; + + let typ = self.interner.id_type(id); + Ok(Value::Struct(fields, typ)) + } + + fn evaluate_access(&mut self, access: HirMemberAccess, id: ExprId) -> IResult { + let (fields, struct_type) = match self.evaluate(access.lhs)? { + Value::Struct(fields, typ) => (fields, typ), + value => { + let location = self.interner.expr_location(&id); + return Err(InterpreterError::NonTupleOrStructInMemberAccess { value, location }); + } + }; + + fields.get(&access.rhs.0.contents).cloned().ok_or_else(|| { + let location = self.interner.expr_location(&id); + let value = Value::Struct(fields, struct_type); + let field_name = access.rhs.0.contents; + InterpreterError::ExpectedStructToHaveField { value, field_name, location } + }) + } + + fn evaluate_call(&mut self, call: HirCallExpression, id: ExprId) -> IResult { + let function = self.evaluate(call.func)?; + let arguments = try_vecmap(call.arguments, |arg| { + Ok((self.evaluate(arg)?, self.interner.expr_location(&arg))) + })?; + let location = self.interner.expr_location(&id); + + match function { + Value::Function(function_id, _) => self.call_function(function_id, arguments, location), + Value::Closure(closure, env, _) => self.call_closure(closure, env, arguments, location), + value => Err(InterpreterError::NonFunctionCalled { value, location }), + } + } + + fn evaluate_method_call( + &mut self, + call: HirMethodCallExpression, + id: ExprId, + ) -> IResult { + let object = self.evaluate(call.object)?; + let arguments = try_vecmap(call.arguments, |arg| { + Ok((self.evaluate(arg)?, self.interner.expr_location(&arg))) + })?; + let location = self.interner.expr_location(&id); + + let typ = object.get_type().follow_bindings(); + let method_name = &call.method.0.contents; + + // TODO: Traits + let method = match &typ { + Type::Struct(struct_def, _) => { + self.interner.lookup_method(&typ, struct_def.borrow().id, method_name, false) + } + _ => self.interner.lookup_primitive_method(&typ, method_name), + }; + + if let Some(method) = method { + self.call_function(method, arguments, location) + } else { + Err(InterpreterError::NoMethodFound { object, typ, location }) + } + } + + fn evaluate_cast(&mut self, cast: HirCastExpression, id: ExprId) -> IResult { + macro_rules! signed_int_to_field { + ($x:expr) => {{ + // Need to convert the signed integer to an i128 before + // we negate it to preserve the MIN value. + let mut value = $x as i128; + let is_negative = value < 0; + if is_negative { + value = -value; + } + ((value as u128).into(), is_negative) + }}; + } + + let (mut lhs, lhs_is_negative) = match self.evaluate(cast.lhs)? { + Value::Field(value) => (value, false), + Value::U8(value) => ((value as u128).into(), false), + Value::U32(value) => ((value as u128).into(), false), + Value::U64(value) => ((value as u128).into(), false), + Value::I8(value) => signed_int_to_field!(value), + Value::I32(value) => signed_int_to_field!(value), + Value::I64(value) => signed_int_to_field!(value), + Value::Bool(value) => { + (if value { FieldElement::one() } else { FieldElement::zero() }, false) + } + value => { + let location = self.interner.expr_location(&id); + return Err(InterpreterError::NonNumericCasted { value, location }); + } + }; + + macro_rules! cast_to_int { + ($x:expr, $method:ident, $typ:ty, $f:ident) => {{ + let mut value = $x.$method() as $typ; + if lhs_is_negative { + value = 0 - value; + } + Ok(Value::$f(value)) + }}; + } + + // Now actually cast the lhs, bit casting and wrapping as necessary + match cast.r#type.follow_bindings() { + Type::FieldElement => { + if lhs_is_negative { + lhs = FieldElement::zero() - lhs; + } + Ok(Value::Field(lhs)) + } + Type::Integer(sign, bit_size) => match (sign, bit_size) { + (Signedness::Unsigned, IntegerBitSize::One) => { + let location = self.interner.expr_location(&id); + Err(InterpreterError::TypeUnsupported { typ: cast.r#type, location }) + } + (Signedness::Unsigned, IntegerBitSize::Eight) => cast_to_int!(lhs, to_u128, u8, U8), + (Signedness::Unsigned, IntegerBitSize::ThirtyTwo) => { + cast_to_int!(lhs, to_u128, u32, U32) + } + (Signedness::Unsigned, IntegerBitSize::SixtyFour) => { + cast_to_int!(lhs, to_u128, u64, U64) + } + (Signedness::Signed, IntegerBitSize::One) => { + let location = self.interner.expr_location(&id); + Err(InterpreterError::TypeUnsupported { typ: cast.r#type, location }) + } + (Signedness::Signed, IntegerBitSize::Eight) => cast_to_int!(lhs, to_i128, i8, I8), + (Signedness::Signed, IntegerBitSize::ThirtyTwo) => { + cast_to_int!(lhs, to_i128, i32, I32) + } + (Signedness::Signed, IntegerBitSize::SixtyFour) => { + cast_to_int!(lhs, to_i128, i64, I64) + } + }, + Type::Bool => Ok(Value::Bool(!lhs.is_zero() || lhs_is_negative)), + typ => { + let location = self.interner.expr_location(&id); + Err(InterpreterError::CastToNonNumericType { typ, location }) + } + } + } + + fn evaluate_if(&mut self, if_: HirIfExpression, id: ExprId) -> IResult { + let condition = match self.evaluate(if_.condition)? { + Value::Bool(value) => value, + value => { + let location = self.interner.expr_location(&id); + return Err(InterpreterError::NonBoolUsedInIf { value, location }); + } + }; + + self.push_scope(); + + let result = if condition { + if if_.alternative.is_some() { + self.evaluate(if_.consequence) + } else { + self.evaluate(if_.consequence)?; + Ok(Value::Unit) + } + } else { + match if_.alternative { + Some(alternative) => self.evaluate(alternative), + None => Ok(Value::Unit), + } + }; + + self.pop_scope(); + result + } + + fn evaluate_tuple(&mut self, tuple: Vec) -> IResult { + let fields = try_vecmap(tuple, |field| self.evaluate(field))?; + Ok(Value::Tuple(fields)) + } + + fn evaluate_lambda(&mut self, lambda: HirLambda, id: ExprId) -> IResult { + let location = self.interner.expr_location(&id); + let environment = + try_vecmap(&lambda.captures, |capture| self.lookup_id(capture.ident.id, location))?; + + let typ = self.interner.id_type(id); + Ok(Value::Closure(lambda, environment, typ)) + } + + fn evaluate_statement(&mut self, statement: StmtId) -> IResult { + match self.interner.statement(&statement) { + HirStatement::Let(let_) => self.evaluate_let(let_), + HirStatement::Constrain(constrain) => self.evaluate_constrain(constrain), + HirStatement::Assign(assign) => self.evaluate_assign(assign), + HirStatement::For(for_) => self.evaluate_for(for_), + HirStatement::Break => self.evaluate_break(), + HirStatement::Continue => self.evaluate_continue(), + HirStatement::Expression(expression) => self.evaluate(expression), + HirStatement::Comptime(statement) => self.evaluate_comptime(statement), + HirStatement::Semi(expression) => { + self.evaluate(expression)?; + Ok(Value::Unit) + } + HirStatement::Error => { + let location = self.interner.id_location(statement); + Err(InterpreterError::ErrorNodeEncountered { location }) + } + } + } + + fn evaluate_let(&mut self, let_: HirLetStatement) -> IResult { + let rhs = self.evaluate(let_.expression)?; + let location = self.interner.expr_location(&let_.expression); + self.define_pattern(&let_.pattern, &let_.r#type, rhs, location)?; + Ok(Value::Unit) + } + + fn evaluate_constrain(&mut self, constrain: HirConstrainStatement) -> IResult { + match self.evaluate(constrain.0)? { + Value::Bool(true) => Ok(Value::Unit), + Value::Bool(false) => { + let location = self.interner.expr_location(&constrain.0); + let message = constrain.2.and_then(|expr| self.evaluate(expr).ok()); + Err(InterpreterError::FailingConstraint { location, message }) + } + value => { + let location = self.interner.expr_location(&constrain.0); + Err(InterpreterError::NonBoolUsedInConstrain { value, location }) + } + } + } + + fn evaluate_assign(&mut self, assign: HirAssignStatement) -> IResult { + let rhs = self.evaluate(assign.expression)?; + self.store_lvalue(assign.lvalue, rhs)?; + Ok(Value::Unit) + } + + fn store_lvalue(&mut self, lvalue: HirLValue, rhs: Value) -> IResult<()> { + match lvalue { + HirLValue::Ident(ident, typ) => { + self.checked_mutate(ident.id, &typ, rhs, ident.location) + } + HirLValue::Dereference { lvalue, element_type: _, location } => { + match self.evaluate_lvalue(&lvalue)? { + Value::Pointer(value) => { + *value.borrow_mut() = rhs; + Ok(()) + } + value => Err(InterpreterError::NonPointerDereferenced { value, location }), + } + } + HirLValue::MemberAccess { object, field_name, field_index, typ: _, location } => { + let index = field_index.expect("The field index should be set after type checking"); + match self.evaluate_lvalue(&object)? { + Value::Tuple(mut fields) => { + fields[index] = rhs; + self.store_lvalue(*object, Value::Tuple(fields)) + } + Value::Struct(mut fields, typ) => { + fields.insert(Rc::new(field_name.0.contents), rhs); + self.store_lvalue(*object, Value::Struct(fields, typ)) + } + value => { + Err(InterpreterError::NonTupleOrStructInMemberAccess { value, location }) + } + } + } + HirLValue::Index { array, index, typ: _, location } => { + let array_value = self.evaluate_lvalue(&array)?; + let index = self.evaluate(index)?; + + let constructor = match &array_value { + Value::Array(..) => Value::Array, + _ => Value::Slice, + }; + + let typ = array_value.get_type().into_owned(); + let (elements, index) = self.bounds_check(array_value, index, location)?; + + let new_array = constructor(elements.update(index, rhs), typ); + self.store_lvalue(*array, new_array) + } + } + } + + fn evaluate_lvalue(&mut self, lvalue: &HirLValue) -> IResult { + match lvalue { + HirLValue::Ident(ident, _) => self.lookup(ident), + HirLValue::Dereference { lvalue, element_type: _, location } => { + match self.evaluate_lvalue(lvalue)? { + Value::Pointer(value) => Ok(value.borrow().clone()), + value => { + Err(InterpreterError::NonPointerDereferenced { value, location: *location }) + } + } + } + HirLValue::MemberAccess { object, field_name, field_index, typ: _, location } => { + let index = field_index.expect("The field index should be set after type checking"); + + match self.evaluate_lvalue(object)? { + Value::Tuple(mut values) => Ok(values.swap_remove(index)), + Value::Struct(fields, _) => Ok(fields[&field_name.0.contents].clone()), + value => Err(InterpreterError::NonTupleOrStructInMemberAccess { + value, + location: *location, + }), + } + } + HirLValue::Index { array, index, typ: _, location } => { + let array = self.evaluate_lvalue(array)?; + let index = self.evaluate(*index)?; + let (elements, index) = self.bounds_check(array, index, *location)?; + Ok(elements[index].clone()) + } + } + } + + fn evaluate_for(&mut self, for_: HirForStatement) -> IResult { + // i128 can store all values from i8 - u64 + let get_index = |this: &mut Self, expr| -> IResult<(_, fn(_) -> _)> { + match this.evaluate(expr)? { + Value::I8(value) => Ok((value as i128, |i| Value::I8(i as i8))), + Value::I32(value) => Ok((value as i128, |i| Value::I32(i as i32))), + Value::I64(value) => Ok((value as i128, |i| Value::I64(i as i64))), + Value::U8(value) => Ok((value as i128, |i| Value::U8(i as u8))), + Value::U32(value) => Ok((value as i128, |i| Value::U32(i as u32))), + Value::U64(value) => Ok((value as i128, |i| Value::U64(i as u64))), + value => { + let location = this.interner.expr_location(&expr); + Err(InterpreterError::NonIntegerUsedInLoop { value, location }) + } + } + }; + + let (start, make_value) = get_index(self, for_.start_range)?; + let (end, _) = get_index(self, for_.end_range)?; + let was_in_loop = std::mem::replace(&mut self.in_loop, true); + + for i in start..end { + self.push_scope(); + self.current_scope_mut().insert(for_.identifier.id, make_value(i)); + + match self.evaluate(for_.block) { + Ok(_) => (), + Err(InterpreterError::Break) => break, + Err(InterpreterError::Continue) => continue, + Err(other) => return Err(other), + } + self.pop_scope(); + } + + self.in_loop = was_in_loop; + Ok(Value::Unit) + } + + fn evaluate_break(&mut self) -> IResult { + if self.in_loop { + Err(InterpreterError::Break) + } else { + Err(InterpreterError::BreakNotInLoop) + } + } + + fn evaluate_continue(&mut self) -> IResult { + if self.in_loop { + Err(InterpreterError::Continue) + } else { + Err(InterpreterError::ContinueNotInLoop) + } + } + + fn evaluate_comptime(&mut self, statement: StmtId) -> IResult { + let was_in_comptime = std::mem::replace(&mut self.in_comptime_context, true); + let result = self.evaluate_statement(statement); + self.in_comptime_context = was_in_comptime; + result + } +} + +impl Value { + fn get_type(&self) -> Cow { + Cow::Owned(match self { + Value::Unit => Type::Unit, + Value::Bool(_) => Type::Bool, + Value::Field(_) => Type::FieldElement, + Value::I8(_) => Type::Integer(Signedness::Signed, IntegerBitSize::Eight), + Value::I32(_) => Type::Integer(Signedness::Signed, IntegerBitSize::ThirtyTwo), + Value::I64(_) => Type::Integer(Signedness::Signed, IntegerBitSize::SixtyFour), + Value::U8(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::Eight), + Value::U32(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::ThirtyTwo), + Value::U64(_) => Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour), + Value::String(value) => { + let length = Type::Constant(value.len() as u64); + Type::String(Box::new(length)) + } + Value::Function(_, typ) => return Cow::Borrowed(typ), + Value::Closure(_, _, typ) => return Cow::Borrowed(typ), + Value::Tuple(fields) => { + Type::Tuple(vecmap(fields, |field| field.get_type().into_owned())) + } + Value::Struct(_, typ) => return Cow::Borrowed(typ), + Value::Array(_, typ) => return Cow::Borrowed(typ), + Value::Slice(_, typ) => return Cow::Borrowed(typ), + Value::Code(_) => Type::Code, + Value::Pointer(element) => { + let element = element.borrow().get_type().into_owned(); + Type::MutableReference(Box::new(element)) + } + }) + } +} 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 91621c857cf..83aaddaa405 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/mod.rs @@ -1 +1,3 @@ mod hir_to_ast; +mod interpreter; +mod tests; 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 new file mode 100644 index 00000000000..016e7079886 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/tests.rs @@ -0,0 +1,166 @@ +#![cfg(test)] + +use noirc_errors::Location; + +use super::interpreter::{Interpreter, InterpreterError, Value}; +use crate::hir::type_check::test::type_check_src_code; + +fn interpret_helper(src: &str, func_namespace: Vec) -> Result { + let (mut interner, main_id) = type_check_src_code(src, func_namespace); + let mut interpreter = Interpreter::new(&mut interner); + + let no_location = Location::dummy(); + interpreter.call_function(main_id, Vec::new(), no_location) +} + +fn interpret(src: &str, func_namespace: Vec) -> Value { + interpret_helper(src, func_namespace).unwrap_or_else(|error| { + panic!("Expected interpreter to exit successfully, but found {error:?}") + }) +} + +fn interpret_expect_error(src: &str, func_namespace: Vec) -> InterpreterError { + interpret_helper(src, func_namespace).expect_err("Expected interpreter to error") +} + +#[test] +fn interpreter_works() { + let program = "fn main() -> pub Field { 3 }"; + let result = interpret(program, vec!["main".into()]); + assert_eq!(result, Value::Field(3u128.into())); +} + +#[test] +fn mutation_works() { + let program = "fn main() -> pub i8 { + let mut x = 3; + x = 4; + x + }"; + let result = interpret(program, vec!["main".into()]); + assert_eq!(result, Value::I8(4)); +} + +#[test] +fn mutating_references() { + let program = "fn main() -> pub i32 { + let x = &mut 3; + *x = 4; + *x + }"; + let result = interpret(program, vec!["main".into()]); + assert_eq!(result, Value::I32(4)); +} + +#[test] +fn mutating_mutable_references() { + let program = "fn main() -> pub i64 { + let mut x = &mut 3; + *x = 4; + *x + }"; + let result = interpret(program, vec!["main".into()]); + assert_eq!(result, Value::I64(4)); +} + +#[test] +fn mutating_arrays() { + let program = "fn main() -> pub u8 { + let mut a1 = [1, 2, 3, 4]; + a1[1] = 22; + a1[1] + }"; + let result = interpret(program, vec!["main".into()]); + assert_eq!(result, Value::U8(22)); +} + +#[test] +fn for_loop() { + let program = "fn main() -> pub u8 { + let mut x = 0; + for i in 0 .. 6 { + x += i; + } + x + }"; + let result = interpret(program, vec!["main".into()]); + assert_eq!(result, Value::U8(15)); +} + +#[test] +fn for_loop_with_break() { + let program = "unconstrained fn main() -> pub u32 { + let mut x = 0; + for i in 0 .. 6 { + if i == 4 { + break; + } + x += i; + } + x + }"; + let result = interpret(program, vec!["main".into()]); + assert_eq!(result, Value::U32(6)); +} + +#[test] +fn for_loop_with_continue() { + let program = "unconstrained fn main() -> pub u64 { + let mut x = 0; + for i in 0 .. 6 { + if i == 4 { + continue; + } + x += i; + } + x + }"; + let result = interpret(program, vec!["main".into()]); + assert_eq!(result, Value::U64(11)); +} + +#[test] +fn assert() { + let program = "fn main() { + assert(1 == 1); + }"; + let result = interpret(program, vec!["main".into()]); + assert_eq!(result, Value::Unit); +} + +#[test] +fn assert_fail() { + let program = "fn main() { + assert(1 == 2); + }"; + let result = interpret_expect_error(program, vec!["main".into()]); + assert!(matches!(result, InterpreterError::FailingConstraint { .. })); +} + +#[test] +fn lambda() { + let program = "fn main() -> pub u8 { + let f = |x: u8| x + 1; + f(1) + }"; + let result = interpret(program, vec!["main".into()]); + assert!(matches!(result, Value::U8(2))); +} + +#[test] +fn non_deterministic_recursion() { + let program = " + fn main() -> pub u64 { + fib(10) + } + + fn fib(x: u64) -> u64 { + if x <= 1 { + x + } else { + fib(x - 1) + fib(x - 2) + } + }"; + let result = interpret(program, vec!["main".into(), "fib".into()]); + assert_eq!(result, Value::U64(55)); +} 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 463b8a4b329..4c6b5ab5885 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 @@ -18,11 +18,11 @@ use crate::hir::Context; use crate::macros_api::{MacroError, MacroProcessor}; use crate::node_interner::{FuncId, GlobalId, NodeInterner, StructId, TraitId, TypeAliasId}; -use crate::parser::{ParserError, SortedModule}; -use crate::{ +use crate::ast::{ ExpressionKind, Ident, LetStatement, Literal, NoirFunction, NoirStruct, NoirTrait, NoirTypeAlias, Path, PathKind, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, }; +use crate::parser::{ParserError, SortedModule}; use fm::FileId; use iter_extended::vecmap; use noirc_errors::{CustomDiagnostic, Span}; @@ -398,11 +398,11 @@ fn inject_prelude( ) { let segments: Vec<_> = "std::prelude" .split("::") - .map(|segment| crate::Ident::new(segment.into(), Span::default())) + .map(|segment| crate::ast::Ident::new(segment.into(), Span::default())) .collect(); let path = - Path { segments: segments.clone(), kind: crate::PathKind::Dep, span: Span::default() }; + Path { segments: segments.clone(), kind: crate::ast::PathKind::Dep, span: Span::default() }; if !crate_id.is_stdlib() { if let Ok(PathResolution { module_def_id, error }) = path_resolver::resolve_path( 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 6fbb3b67546..baedb109967 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,14 +4,16 @@ use acvm::acir::acir_field::FieldOptions; use fm::{FileId, FileManager, FILE_EXTENSION}; use noirc_errors::Location; +use crate::ast::{ + FunctionDefinition, Ident, ItemVisibility, LetStatement, ModuleDeclaration, NoirFunction, + NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, TraitImplItem, TraitItem, TypeImpl, +}; use crate::{ graph::CrateId, hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait}, macros_api::MacroProcessor, node_interner::{FunctionModifiers, TraitId, TypeAliasId}, parser::{SortedModule, SortedSubModule}, - FunctionDefinition, Ident, ItemVisibility, LetStatement, ModuleDeclaration, NoirFunction, - NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, TraitImplItem, TraitItem, TypeImpl, }; use super::{ @@ -416,6 +418,7 @@ impl<'a> ModCollector<'a> { // TODO(Maddiaa): Investigate trait implementations with attributes see: https://github.com/noir-lang/noir/issues/2629 attributes: crate::token::Attributes::empty(), is_unconstrained: false, + is_comptime: false, }; let location = Location::new(name.span(), self.file_id); 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 29daf5d6369..59a3051ac70 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 @@ -1,6 +1,5 @@ +use crate::ast::{Ident, Path}; use crate::hir::resolution::import::PathResolutionError; -use crate::Ident; -use crate::Path; use noirc_errors::CustomDiagnostic as Diagnostic; use noirc_errors::FileDiagnostic; 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 cd4eafdf669..3ca89e56bbc 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,7 @@ use super::{namespace::PerNs, ModuleDefId, ModuleId}; -use crate::{ - node_interner::{FuncId, TraitId}, - Ident, ItemVisibility, -}; +use crate::ast::{Ident, ItemVisibility}; +use crate::node_interner::{FuncId, TraitId}; + use std::collections::{hash_map::Entry, HashMap}; type Scope = HashMap, (ModuleDefId, ItemVisibility, bool /*is_prelude*/)>; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_data.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_data.rs index 4dd38f0e3e5..488ccc476d7 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_data.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/module_data.rs @@ -2,12 +2,9 @@ use std::collections::HashMap; use noirc_errors::Location; -use crate::{ - node_interner::{FuncId, GlobalId, StructId, TraitId, TypeAliasId}, - Ident, ItemVisibility, -}; - use super::{ItemScope, LocalModuleId, ModuleDefId, ModuleId, PerNs}; +use crate::ast::{Ident, ItemVisibility}; +use crate::node_interner::{FuncId, GlobalId, StructId, TraitId, TypeAliasId}; /// Contains the actual contents of a module: its parent (if one exists), /// children, and scope with all definitions defined within the scope. diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/namespace.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/namespace.rs index 5e349f46e14..6fac6d2b991 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/namespace.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/def_map/namespace.rs @@ -1,5 +1,5 @@ use super::ModuleDefId; -use crate::ItemVisibility; +use crate::ast::ItemVisibility; // This works exactly the same as in r-a, just simplified #[derive(Debug, PartialEq, Eq, Copy, Clone)] 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 71e3f3482fc..0fac6f96086 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 @@ -2,7 +2,7 @@ pub use noirc_errors::Span; use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic}; use thiserror::Error; -use crate::{parser::ParserError, Ident, Type}; +use crate::{ast::Ident, parser::ParserError, Type}; use super::import::PathResolutionError; @@ -49,7 +49,7 @@ pub enum ResolverError { #[error("Integer too large to be evaluated in an array length context")] IntegerTooLarge { span: Span }, #[error("No global or generic type parameter found with the given name")] - NoSuchNumericTypeVariable { path: crate::Path }, + NoSuchNumericTypeVariable { path: crate::ast::Path }, #[error("Closures cannot capture mutable variables")] CapturedMutableVariable { span: Span }, #[error("Test functions are not allowed to have any parameters")] diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/impls.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/impls.rs index 72f6adc3770..7efd1eed86e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/impls.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/impls.rs @@ -2,6 +2,7 @@ use std::collections::BTreeMap; use fm::FileId; +use crate::ast::ItemVisibility; use crate::{ graph::CrateId, hir::{ @@ -13,7 +14,7 @@ use crate::{ Context, }, node_interner::{FuncId, NodeInterner}, - ItemVisibility, Type, + Type, }; use super::{ 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 ade97e2cf42..77e67567f8c 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 @@ -4,8 +4,8 @@ use thiserror::Error; use crate::graph::CrateId; use std::collections::BTreeMap; +use crate::ast::{Ident, ItemVisibility, Path, PathKind}; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleDefId, ModuleId, PerNs}; -use crate::{Ident, ItemVisibility, Path, PathKind}; #[derive(Debug, Clone)] pub struct ImportDirective { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/path_resolver.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/path_resolver.rs index e19af3c732f..e423e10b712 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/path_resolver.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/path_resolver.rs @@ -1,5 +1,5 @@ use super::import::{resolve_import, ImportDirective, PathResolution, PathResolutionResult}; -use crate::Path; +use crate::ast::Path; use std::collections::BTreeMap; use crate::graph::CrateId; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 9180201fe17..0e69b3bdeba 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -25,25 +25,22 @@ use regex::Regex; use std::collections::{BTreeMap, HashSet}; use std::rc::Rc; +use crate::ast::{ + ArrayLiteral, BinaryOpKind, BlockExpression, Distinctness, Expression, ExpressionKind, + ForRange, FunctionDefinition, FunctionKind, FunctionReturnType, Ident, ItemVisibility, LValue, + LetStatement, Literal, NoirFunction, NoirStruct, NoirTypeAlias, Param, Path, PathKind, Pattern, + Statement, StatementKind, UnaryOp, UnresolvedGenerics, UnresolvedTraitConstraint, + UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, Visibility, ERROR_IDENT, +}; use crate::graph::CrateId; use crate::hir::def_map::{ModuleDefId, TryFromModuleDefId, MAIN_FUNCTION}; +use crate::hir::{def_map::CrateDefMap, resolution::path_resolver::PathResolver}; use crate::hir_def::stmt::{HirAssignStatement, HirForStatement, HirLValue, HirPattern}; use crate::node_interner::{ DefinitionId, DefinitionKind, DependencyId, ExprId, FuncId, GlobalId, NodeInterner, StmtId, StructId, TraitId, TraitImplId, TraitMethodId, TypeAliasId, }; -use crate::{ - hir::{def_map::CrateDefMap, resolution::path_resolver::PathResolver}, - BlockExpression, Expression, ExpressionKind, FunctionKind, Ident, Literal, NoirFunction, - StatementKind, -}; -use crate::{ - ArrayLiteral, BinaryOpKind, Distinctness, ForRange, FunctionDefinition, FunctionReturnType, - Generics, ItemVisibility, LValue, NoirStruct, NoirTypeAlias, Param, Path, PathKind, Pattern, - Shared, Statement, StructType, Type, TypeAlias, TypeVariable, TypeVariableKind, UnaryOp, - UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, - UnresolvedTypeExpression, Visibility, ERROR_IDENT, -}; +use crate::{Generics, Shared, StructType, Type, TypeAlias, TypeVariable, TypeVariableKind}; use fm::FileId; use iter_extended::vecmap; use noirc_errors::{Location, Span, Spanned}; @@ -246,6 +243,7 @@ impl<'a> Resolver<'a> { name: name.clone(), attributes: Attributes::empty(), is_unconstrained: false, + is_comptime: false, visibility: ItemVisibility::Public, // Trait functions are always public generics: generics.clone(), parameters: vecmap(parameters, |(name, typ)| Param { @@ -477,7 +475,7 @@ impl<'a> Resolver<'a> { /// Translates an UnresolvedType into a Type and appends any /// freshly created TypeVariables created to new_variables. fn resolve_type_inner(&mut self, typ: UnresolvedType, new_variables: &mut Generics) -> Type { - use UnresolvedTypeData::*; + use crate::ast::UnresolvedTypeData::*; let resolved_type = match typ.typ { FieldElement => Type::FieldElement, @@ -928,6 +926,7 @@ impl<'a> Resolver<'a> { let name_ident = HirIdent::non_trait_method(id, location); let attributes = func.attributes().clone(); + let should_fold = attributes.is_foldable(); let mut generics = vecmap(&self.generics, |(_, typevar, _)| typevar.clone()); let mut parameters = vec![]; @@ -1008,8 +1007,6 @@ impl<'a> Resolver<'a> { .map(|(name, typevar, _span)| (name.clone(), typevar.clone())) .collect(); - let should_fold = attributes.is_foldable(); - FuncMeta { name: name_ident, kind: func.kind, @@ -1038,7 +1035,7 @@ impl<'a> Resolver<'a> { /// True if the 'pub' keyword is allowed on parameters in this function /// 'pub' on function parameters is only allowed for entry point functions fn pub_allowed(&self, func: &NoirFunction) -> bool { - self.is_entry_point_function(func) + self.is_entry_point_function(func) || func.attributes().is_foldable() } fn is_entry_point_function(&self, func: &NoirFunction) -> bool { @@ -1171,7 +1168,7 @@ impl<'a> Resolver<'a> { pub fn resolve_global_let( &mut self, - let_stmt: crate::LetStatement, + let_stmt: LetStatement, global_id: GlobalId, ) -> HirStatement { self.current_item = Some(DependencyId::Global(global_id)); @@ -1275,6 +1272,10 @@ impl<'a> Resolver<'a> { HirStatement::Continue } StatementKind::Error => HirStatement::Error, + StatementKind::Comptime(statement) => { + let statement = self.resolve_stmt(*statement, span); + HirStatement::Comptime(self.interner.push_stmt(statement)) + } } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/structs.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/structs.rs index ed7aa86e718..f62e5589d74 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/structs.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/structs.rs @@ -3,6 +3,7 @@ use std::collections::BTreeMap; use fm::FileId; use iter_extended::vecmap; +use crate::ast::Ident; use crate::{ graph::CrateId, hir::{ @@ -11,7 +12,7 @@ use crate::{ Context, }, node_interner::StructId, - Generics, Ident, Type, + Generics, Type, }; use super::{errors::ResolverError, path_resolver::StandardPathResolver, resolver::Resolver}; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/traits.rs index a7669f57e33..ccae8c6dd03 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/resolution/traits.rs @@ -4,6 +4,7 @@ use fm::FileId; use iter_extended::vecmap; use noirc_errors::Location; +use crate::ast::{ItemVisibility, Path, TraitItem}; use crate::{ graph::CrateId, hir::{ @@ -16,7 +17,7 @@ use crate::{ }, hir_def::traits::{TraitConstant, TraitFunction, TraitImpl, TraitType}, node_interner::{FuncId, NodeInterner, TraitId}, - Generics, ItemVisibility, Path, Shared, TraitItem, Type, TypeVariable, TypeVariableKind, + Generics, Shared, Type, TypeVariable, TypeVariableKind, }; use super::{ 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 6c28aabe0fb..6c2a1945283 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 @@ -3,13 +3,10 @@ use noirc_errors::CustomDiagnostic as Diagnostic; use noirc_errors::Span; use thiserror::Error; +use crate::ast::{BinaryOpKind, FunctionReturnType, IntegerBitSize, Signedness}; use crate::hir::resolution::errors::ResolverError; use crate::hir_def::expr::HirBinaryOp; use crate::hir_def::types::Type; -use crate::BinaryOpKind; -use crate::FunctionReturnType; -use crate::IntegerBitSize; -use crate::Signedness; #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum Source { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/expr.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/expr.rs index b56e2dce2a9..62330732be4 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -1,6 +1,7 @@ use iter_extended::vecmap; use noirc_errors::Span; +use crate::ast::{BinaryOpKind, UnaryOp}; use crate::{ hir::{resolution::resolver::verify_mutable_reference, type_check::errors::Source}, hir_def::{ @@ -11,7 +12,7 @@ use crate::{ types::Type, }, node_interner::{DefinitionKind, ExprId, FuncId, TraitId, TraitImplKind, TraitMethodId}, - BinaryOpKind, TypeBinding, TypeBindings, TypeVariableKind, UnaryOp, + TypeBinding, TypeBindings, TypeVariableKind, }; use super::{errors::TypeCheckError, TypeChecker}; @@ -717,7 +718,7 @@ impl<'interner> TypeChecker<'interner> { let dereference_lhs = |this: &mut Self, lhs_type, element| { let old_lhs = *access_lhs; *access_lhs = this.interner.push_expr(HirExpression::Prefix(HirPrefixExpression { - operator: crate::UnaryOp::Dereference { implicitly_added: true }, + operator: crate::ast::UnaryOp::Dereference { implicitly_added: true }, rhs: old_lhs, })); this.interner.push_expr_type(old_lhs, lhs_type); @@ -1110,7 +1111,7 @@ impl<'interner> TypeChecker<'interner> { if !op.kind.is_valid_for_field_type() && lhs_type.is_numeric() { let target = Type::polymorphic_integer(self.interner); - use BinaryOpKind::*; + use crate::ast::BinaryOpKind::*; use TypeCheckError::*; self.unify(lhs_type, &target, || match op.kind { Less | LessEqual | Greater | GreaterEqual => FieldComparison { span }, @@ -1202,7 +1203,7 @@ impl<'interner> TypeChecker<'interner> { fn type_check_prefix_operand( &mut self, - op: &crate::UnaryOp, + op: &crate::ast::UnaryOp, rhs_type: &Type, span: Span, ) -> Type { @@ -1216,7 +1217,7 @@ impl<'interner> TypeChecker<'interner> { }; match op { - crate::UnaryOp::Minus => { + crate::ast::UnaryOp::Minus => { if rhs_type.is_unsigned() { self.errors .push(TypeCheckError::InvalidUnaryOp { kind: rhs_type.to_string(), span }); @@ -1228,7 +1229,7 @@ impl<'interner> TypeChecker<'interner> { }); expected } - crate::UnaryOp::Not => { + crate::ast::UnaryOp::Not => { let rhs_type = rhs_type.follow_bindings(); // `!` can work on booleans or integers @@ -1238,10 +1239,10 @@ impl<'interner> TypeChecker<'interner> { unify(Type::Bool) } - crate::UnaryOp::MutableReference => { + crate::ast::UnaryOp::MutableReference => { Type::MutableReference(Box::new(rhs_type.follow_bindings())) } - crate::UnaryOp::Dereference { implicitly_added: _ } => { + crate::ast::UnaryOp::Dereference { implicitly_added: _ } => { let element_type = self.interner.next_type_variable(); unify(Type::MutableReference(Box::new(element_type.clone()))); element_type 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 cdfc19b3a33..44dab6dee3d 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 @@ -51,8 +51,7 @@ pub fn type_check_func(interner: &mut NodeInterner, func_id: FuncId) -> Vec, ) { let meta = type_checker.interner.function_meta(&func_id); - if (meta.is_entry_point || meta.should_fold) && !param.1.is_valid_for_program_input() { + if (meta.is_entry_point && !param.1.is_valid_for_program_input()) + || (meta.should_fold && !param.1.is_valid_non_inlined_function_input()) + { let span = param.0.span(); errors.push(TypeCheckError::InvalidTypeForEntryPoint { span }); } @@ -424,14 +425,17 @@ impl<'interner> TypeChecker<'interner> { // XXX: These tests are all manual currently. /// We can either build a test apparatus or pass raw code through the resolver #[cfg(test)] -mod test { +pub mod test { use std::collections::{BTreeMap, HashMap}; use std::vec; use fm::FileId; - use iter_extended::vecmap; + use iter_extended::btree_map; use noirc_errors::{Location, Span}; + use crate::ast::{ + BinaryOpKind, Distinctness, FunctionKind, FunctionReturnType, Path, Visibility, + }; use crate::graph::CrateId; use crate::hir::def_map::{ModuleData, ModuleId}; use crate::hir::resolution::import::{ @@ -452,9 +456,8 @@ mod test { def_map::{CrateDefMap, LocalModuleId, ModuleDefId}, resolution::{path_resolver::PathResolver, resolver::Resolver}, }, - parse_program, FunctionKind, Path, + parse_program, }; - use crate::{BinaryOpKind, Distinctness, FunctionReturnType, Visibility}; #[test] fn basic_let() { @@ -601,7 +604,7 @@ mod test { "#; - type_check_src_code(src, vec![String::from("main"), String::from("foo")]); + type_check_src_code(src, vec![String::from("main")]); } #[test] fn basic_closure() { @@ -612,7 +615,7 @@ mod test { } "#; - type_check_src_code(src, vec![String::from("main"), String::from("foo")]); + type_check_src_code(src, vec![String::from("main")]); } #[test] @@ -633,12 +636,23 @@ mod test { #[fold] fn fold(x: &mut Field) -> Field { *x - } + } "#; type_check_src_code_errors_expected(src, vec![String::from("fold")], 1); } + #[test] + fn fold_numeric_generic() { + let src = r#" + #[fold] + fn fold(x: T) -> T { + x + } + "#; + + type_check_src_code(src, vec![String::from("fold")]); + } // This is the same Stub that is in the resolver, maybe we can pull this out into a test module and re-use? struct TestPathResolver(HashMap); @@ -672,8 +686,8 @@ mod test { } } - fn type_check_src_code(src: &str, func_namespace: Vec) { - type_check_src_code_errors_expected(src, func_namespace, 0); + pub fn type_check_src_code(src: &str, func_namespace: Vec) -> (NodeInterner, FuncId) { + type_check_src_code_errors_expected(src, func_namespace, 0) } // This function assumes that there is only one function and this is the @@ -682,7 +696,7 @@ mod test { src: &str, func_namespace: Vec, expected_num_type_check_errs: usize, - ) { + ) -> (NodeInterner, FuncId) { let (program, errors) = parse_program(src); let mut interner = NodeInterner::default(); interner.populate_dummy_operator_traits(); @@ -695,14 +709,16 @@ mod test { errors ); - let main_id = interner.push_test_function_definition("main".into()); + let func_ids = btree_map(&func_namespace, |name| { + (name.to_string(), interner.push_test_function_definition(name.into())) + }); - let func_ids = - vecmap(&func_namespace, |name| interner.push_test_function_definition(name.into())); + let main_id = + *func_ids.get("main").unwrap_or_else(|| func_ids.first_key_value().unwrap().1); let mut path_resolver = TestPathResolver(HashMap::new()); - for (name, id) in func_namespace.into_iter().zip(func_ids.clone()) { - path_resolver.insert_func(name.to_owned(), id); + for (name, id) in func_ids.iter() { + path_resolver.insert_func(name.to_owned(), *id); } let mut def_maps = BTreeMap::new(); @@ -722,20 +738,24 @@ mod test { }, ); - let func_meta = vecmap(program.into_sorted().functions, |nf| { + for nf in program.into_sorted().functions { let resolver = Resolver::new(&mut interner, &path_resolver, &def_maps, file); - let (hir_func, func_meta, resolver_errors) = resolver.resolve_function(nf, main_id); - assert_eq!(resolver_errors, vec![]); - (hir_func, func_meta) - }); - for ((hir_func, meta), func_id) in func_meta.into_iter().zip(func_ids.clone()) { - interner.update_fn(func_id, hir_func); - interner.push_fn_meta(meta, func_id); + let function_id = *func_ids.get(nf.name()).unwrap(); + let (hir_func, func_meta, resolver_errors) = resolver.resolve_function(nf, function_id); + + interner.push_fn_meta(func_meta, function_id); + interner.update_fn(function_id, hir_func); + assert_eq!(resolver_errors, vec![]); } // Type check section - let errors = super::type_check_func(&mut interner, func_ids.first().cloned().unwrap()); + let mut errors = Vec::new(); + + for function in func_ids.values() { + errors.extend(super::type_check_func(&mut interner, *function)); + } + assert_eq!( errors.len(), expected_num_type_check_errs, @@ -744,5 +764,7 @@ mod test { errors.len(), errors ); + + (interner, main_id) } } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/stmt.rs index fb57aa75f89..f5f6e1e8180 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -1,6 +1,7 @@ use iter_extended::vecmap; use noirc_errors::Span; +use crate::ast::UnaryOp; use crate::hir_def::expr::{HirExpression, HirIdent, HirLiteral}; use crate::hir_def::stmt::{ HirAssignStatement, HirConstrainStatement, HirForStatement, HirLValue, HirLetStatement, @@ -8,7 +9,6 @@ use crate::hir_def::stmt::{ }; use crate::hir_def::types::Type; use crate::node_interner::{DefinitionId, ExprId, StmtId}; -use crate::UnaryOp; use super::errors::{Source, TypeCheckError}; use super::TypeChecker; @@ -51,6 +51,7 @@ impl<'interner> TypeChecker<'interner> { HirStatement::Constrain(constrain_stmt) => self.check_constrain_stmt(constrain_stmt), HirStatement::Assign(assign_stmt) => self.check_assign_stmt(assign_stmt, stmt_id), HirStatement::For(for_loop) => self.check_for_loop(for_loop), + HirStatement::Comptime(statement) => return self.check_statement(&statement), HirStatement::Break | HirStatement::Continue | HirStatement::Error => (), } Type::Unit 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 c2f6031bf6d..d88b65d1fce 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 @@ -2,8 +2,9 @@ use acvm::FieldElement; use fm::FileId; use noirc_errors::Location; +use crate::ast::{BinaryOp, BinaryOpKind, Ident, UnaryOp}; use crate::node_interner::{DefinitionId, ExprId, FuncId, NodeInterner, StmtId, TraitMethodId}; -use crate::{BinaryOp, BinaryOpKind, Ident, Shared, UnaryOp}; +use crate::Shared; use super::stmt::HirPattern; use super::traits::TraitConstraint; @@ -31,7 +32,7 @@ pub enum HirExpression { Tuple(Vec), Lambda(HirLambda), Error, - Quote(crate::BlockExpression), + Quote(crate::ast::BlockExpression), } impl HirExpression { @@ -260,7 +261,7 @@ impl HirBlockExpression { } /// A variable captured inside a closure -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct HirCapturedVar { pub ident: HirIdent, @@ -274,7 +275,7 @@ pub struct HirCapturedVar { pub transitive_capture_index: Option, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct HirLambda { pub parameters: Vec<(HirPattern, Type)>, pub return_type: Type, 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 a3bbc9445a8..67b6412a21c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir_def/function.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir_def/function.rs @@ -6,9 +6,9 @@ use std::rc::Rc; use super::expr::{HirBlockExpression, HirExpression, HirIdent}; use super::stmt::HirPattern; use super::traits::TraitConstraint; +use crate::ast::{Distinctness, FunctionKind, FunctionReturnType, Visibility}; use crate::node_interner::{ExprId, NodeInterner, TraitImplId}; -use crate::FunctionKind; -use crate::{Distinctness, FunctionReturnType, Type, TypeVariable, Visibility}; +use crate::{Type, TypeVariable}; /// A Hir function is a block expression /// with a list of statements @@ -24,8 +24,8 @@ impl HirFunction { HirFunction(expr_id) } - pub const fn as_expr(&self) -> &ExprId { - &self.0 + pub const fn as_expr(&self) -> ExprId { + self.0 } pub fn block(&self, interner: &NodeInterner) -> HirBlockExpression { 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 4c9a33d3dc0..7e22e5ee9c0 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,7 +1,8 @@ use super::expr::HirIdent; +use crate::ast::Ident; use crate::macros_api::SecondaryAttribute; -use crate::node_interner::ExprId; -use crate::{Ident, Type}; +use crate::node_interner::{ExprId, StmtId}; +use crate::Type; use fm::FileId; use noirc_errors::{Location, Span}; @@ -19,6 +20,7 @@ pub enum HirStatement { Continue, Expression(ExprId), Semi(ExprId), + Comptime(StmtId), Error, } @@ -61,7 +63,7 @@ pub struct HirAssignStatement { #[derive(Debug, Clone)] pub struct HirConstrainStatement(pub ExprId, pub FileId, pub Option); -#[derive(Debug, Clone, Hash)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum HirPattern { Identifier(HirIdent), Mutable(Box, Location), 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 16b9899039f..e4959cb3dd9 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,9 +1,10 @@ use std::collections::HashMap; +use crate::ast::{Ident, NoirFunction}; use crate::{ graph::CrateId, node_interner::{FuncId, TraitId, TraitMethodId}, - Generics, Ident, NoirFunction, Type, TypeBindings, TypeVariable, TypeVariableId, + Generics, Type, TypeBindings, TypeVariable, TypeVariableId, }; use fm::FileId; use noirc_errors::{Location, Span}; 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 ec8b54c33b8..8c6e3d48fca 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 @@ -6,15 +6,18 @@ use std::{ }; use crate::{ + ast::IntegerBitSize, hir::type_check::TypeCheckError, node_interner::{ExprId, NodeInterner, TraitId, TypeAliasId}, - IntegerBitSize, }; use iter_extended::vecmap; use noirc_errors::{Location, Span}; use noirc_printable_type::PrintableType; -use crate::{node_interner::StructId, Ident, Signedness}; +use crate::{ + ast::{Ident, Signedness}, + node_interner::StructId, +}; use super::expr::{HirCallExpression, HirExpression, HirIdent}; @@ -726,6 +729,54 @@ impl Type { } } + /// True if this type can be used as a parameter to an ACIR function that is not `main` or a contract function. + /// This encapsulates functions for which we may not want to inline during compilation. + /// + /// The inputs allowed for a function entry point differ from those allowed as input to a program as there are + /// certain types which through compilation we know what their size should be. + /// This includes types such as numeric generics. + pub(crate) fn is_valid_non_inlined_function_input(&self) -> bool { + match self { + // Type::Error is allowed as usual since it indicates an error was already issued and + // we don't need to issue further errors about this likely unresolved type + Type::FieldElement + | Type::Integer(_, _) + | Type::Bool + | Type::Unit + | Type::Constant(_) + | Type::TypeVariable(_, _) + | Type::NamedGeneric(_, _) + | Type::Error => true, + + Type::FmtString(_, _) + // To enable this we would need to determine the size of the closure outputs at compile-time. + // This is possible as long as the output size is not dependent upon a witness condition. + | Type::Function(_, _, _) + | Type::Slice(_) + | Type::MutableReference(_) + | Type::Forall(_, _) + // TODO: probably can allow code as it is all compile time + | Type::Code + | Type::TraitAsType(..) => false, + + Type::Alias(alias, generics) => { + let alias = alias.borrow(); + alias.get_type(generics).is_valid_non_inlined_function_input() + } + + Type::Array(length, element) => { + length.is_valid_non_inlined_function_input() && element.is_valid_non_inlined_function_input() + } + Type::String(length) => length.is_valid_non_inlined_function_input(), + Type::Tuple(elements) => elements.iter().all(|elem| elem.is_valid_non_inlined_function_input()), + Type::Struct(definition, generics) => definition + .borrow() + .get_fields(generics) + .into_iter() + .all(|(_, field)| field.is_valid_non_inlined_function_input()), + } + } + /// Returns the number of `Forall`-quantified type variables on this type. /// Returns 0 if this is not a Type::Forall pub fn generic_count(&self) -> usize { 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 86f26fd1c97..ec513d55299 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lexer/token.rs @@ -412,8 +412,8 @@ impl Token { [Plus, Minus, Star, Slash, Percent, Ampersand, Caret, ShiftLeft, ShiftRight, Pipe] } - pub fn try_into_binary_op(self, span: Span) -> Option> { - use crate::BinaryOpKind::*; + pub fn try_into_binary_op(self, span: Span) -> Option> { + use crate::ast::BinaryOpKind::*; let binary_op = match self { Token::Plus => Add, Token::Ampersand => And, @@ -659,7 +659,6 @@ impl Attribute { ["contract_library_method"] => { Attribute::Secondary(SecondaryAttribute::ContractLibraryMethod) } - ["event"] => Attribute::Secondary(SecondaryAttribute::Event), ["abi", tag] => Attribute::Secondary(SecondaryAttribute::Abi(tag.to_string())), ["export"] => Attribute::Secondary(SecondaryAttribute::Export), ["deprecated", name] => { @@ -751,7 +750,6 @@ pub enum SecondaryAttribute { // is a helper method for a contract and should not be seen as // the entry point. ContractLibraryMethod, - Event, Export, Field(String), Custom(String), @@ -767,7 +765,6 @@ impl fmt::Display for SecondaryAttribute { } SecondaryAttribute::Custom(ref k) => write!(f, "#[{k}]"), SecondaryAttribute::ContractLibraryMethod => write!(f, "#[contract_library_method]"), - SecondaryAttribute::Event => write!(f, "#[event]"), SecondaryAttribute::Export => write!(f, "#[export]"), SecondaryAttribute::Field(ref k) => write!(f, "#[field({k})]"), SecondaryAttribute::Abi(ref k) => write!(f, "#[abi({k})]"), @@ -797,7 +794,7 @@ impl AsRef for SecondaryAttribute { | SecondaryAttribute::Field(string) | SecondaryAttribute::Abi(string) => string, SecondaryAttribute::ContractLibraryMethod => "", - SecondaryAttribute::Event | SecondaryAttribute::Export => "", + SecondaryAttribute::Export => "", } } } @@ -839,6 +836,7 @@ pub enum Keyword { ReturnData, String, Struct, + Super, Trait, Type, Unchecked, @@ -883,6 +881,7 @@ impl fmt::Display for Keyword { Keyword::ReturnData => write!(f, "return_data"), Keyword::String => write!(f, "str"), Keyword::Struct => write!(f, "struct"), + Keyword::Super => write!(f, "super"), Keyword::Trait => write!(f, "trait"), Keyword::Type => write!(f, "type"), Keyword::Unchecked => write!(f, "unchecked"), @@ -930,6 +929,7 @@ impl Keyword { "return_data" => Keyword::ReturnData, "str" => Keyword::String, "struct" => Keyword::Struct, + "super" => Keyword::Super, "trait" => Keyword::Trait, "type" => Keyword::Type, "unchecked" => Keyword::Unchecked, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/lib.rs b/noir/noir-repo/compiler/noirc_frontend/src/lib.rs index 93d7960faf5..6c77e3d0545 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/lib.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/lib.rs @@ -28,9 +28,6 @@ pub use lexer::token; // Parser API pub use parser::{parse_program, ParsedModule}; -// AST API -pub use ast::*; - // Type API pub use hir_def::types::*; @@ -52,17 +49,18 @@ pub mod macros_api { pub use crate::parser::{parse_program, SortedModule}; pub use crate::token::SecondaryAttribute; - pub use crate::hir::def_map::ModuleDefId; - pub use crate::{ - hir::Context as HirContext, BlockExpression, CallExpression, CastExpression, Distinctness, - Expression, ExpressionKind, FunctionReturnType, Ident, IndexExpression, ItemVisibility, - LetStatement, Literal, MemberAccessExpression, MethodCallExpression, NoirFunction, Path, - PathKind, Pattern, Statement, UnresolvedType, UnresolvedTypeData, Visibility, + pub use crate::ast::{ + BlockExpression, CallExpression, CastExpression, Distinctness, Expression, ExpressionKind, + FunctionReturnType, Ident, IndexExpression, ItemVisibility, LetStatement, Literal, + MemberAccessExpression, MethodCallExpression, NoirFunction, Path, PathKind, Pattern, + Statement, UnresolvedType, UnresolvedTypeData, Visibility, }; - pub use crate::{ + pub use crate::ast::{ ForLoopStatement, ForRange, FunctionDefinition, ImportStatement, NoirStruct, Param, - PrefixExpression, Signedness, StatementKind, StructType, Type, TypeImpl, UnaryOp, + PrefixExpression, Signedness, StatementKind, TypeImpl, UnaryOp, }; + pub use crate::hir::{def_map::ModuleDefId, Context as HirContext}; + pub use crate::{StructType, Type}; /// Methods to process the AST before and after type checking pub trait MacroProcessor { 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 d9c33d8604e..434cc922a05 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -5,10 +5,8 @@ use noirc_errors::{ Location, }; -use crate::{ - hir_def::function::FunctionSignature, BinaryOpKind, Distinctness, IntegerBitSize, Signedness, - Visibility, -}; +use crate::ast::{BinaryOpKind, Distinctness, IntegerBitSize, Signedness, Visibility}; +use crate::hir_def::function::FunctionSignature; /// The monomorphized AST is expression-based, all statements are also /// folded into this expression enum. Compared to the HIR, the monomorphized @@ -99,7 +97,7 @@ pub enum Literal { #[derive(Debug, Clone, Hash)] pub struct Unary { - pub operator: crate::UnaryOp, + pub operator: crate::ast::UnaryOp, pub rhs: Box, pub result_type: Type, pub location: 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 4e779244d30..74a0dd855c0 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -8,6 +8,7 @@ //! //! The entry point to this pass is the `monomorphize` function which, starting from a given //! function, will monomorphize the entire reachable program. +use crate::ast::{FunctionKind, IntegerBitSize, Signedness, UnaryOp, Visibility}; use crate::{ debug::DebugInstrumenter, hir_def::{ @@ -18,8 +19,7 @@ use crate::{ }, node_interner::{self, DefinitionKind, NodeInterner, StmtId, TraitImplKind, TraitMethodId}, token::FunctionAttribute, - FunctionKind, IntegerBitSize, Signedness, Type, TypeBinding, TypeBindings, TypeVariable, - TypeVariableKind, UnaryOp, Visibility, + Type, TypeBinding, TypeBindings, TypeVariable, TypeVariableKind, }; use acvm::FieldElement; use iter_extended::{btree_map, try_vecmap, vecmap}; @@ -283,12 +283,18 @@ impl<'interner> Monomorphizer<'interner> { } let meta = self.interner.function_meta(&f).clone(); - let func_sig = meta.function_signature(); + let mut func_sig = meta.function_signature(); + // Follow the bindings of the function signature for entry points + // which are not `main` such as foldable functions. + for param in func_sig.0.iter_mut() { + param.1 = param.1.follow_bindings(); + } + func_sig.1 = func_sig.1.map(|return_type| return_type.follow_bindings()); let modifiers = self.interner.function_modifiers(&f); let name = self.interner.function_name(&f).to_owned(); - let body_expr_id = *self.interner.function(&f).as_expr(); + let body_expr_id = self.interner.function(&f).as_expr(); let body_return_type = self.interner.id_type(body_expr_id); let return_type = match meta.return_type() { Type::TraitAsType(..) => &body_return_type, @@ -454,7 +460,7 @@ impl<'interner> Monomorphizer<'interner> { // If this is a comparison operator, the result is a boolean but // the actual method call returns an Ordering - use crate::BinaryOpKind::*; + use crate::ast::BinaryOpKind::*; let ret = if matches!(operator, Less | LessEqual | Greater | GreaterEqual) { self.interner.ordering_type() } else { @@ -618,6 +624,9 @@ impl<'interner> Monomorphizer<'interner> { HirStatement::Break => Ok(ast::Expression::Break), HirStatement::Continue => Ok(ast::Expression::Continue), HirStatement::Error => unreachable!(), + + // All `comptime` statements & expressions should be removed before runtime. + HirStatement::Comptime(_) => unreachable!("comptime statement in runtime code"), } } @@ -1259,7 +1268,7 @@ impl<'interner> Monomorphizer<'interner> { ) -> ast::Expression { use ast::*; - let int_type = Type::Integer(crate::Signedness::Unsigned, arr_elem_bits); + 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(), int_type.clone(), location)) @@ -1588,7 +1597,7 @@ impl<'interner> Monomorphizer<'interner> { })) } ast::Type::MutableReference(element) => { - use crate::UnaryOp::MutableReference; + use crate::ast::UnaryOp::MutableReference; let rhs = Box::new(self.zeroed_value_of_type(element, location)); let result_type = typ.clone(); ast::Expression::Unary(ast::Unary { @@ -1673,7 +1682,7 @@ impl<'interner> Monomorphizer<'interner> { let mut result = ast::Expression::Call(ast::Call { func, arguments, return_type, location }); - use crate::BinaryOpKind::*; + use crate::ast::BinaryOpKind::*; match operator.kind { // Negate the result of the == operation NotEqual => { 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 153c7e45d4a..b0e68be4868 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -16,6 +16,7 @@ use crate::hir::def_collector::dc_crate::CompilationError; use crate::hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait, UnresolvedTypeAlias}; use crate::hir::def_map::{LocalModuleId, ModuleId}; +use crate::ast::{BinaryOpKind, FunctionDefinition, ItemVisibility}; use crate::hir::resolution::errors::ResolverError; use crate::hir_def::stmt::HirLetStatement; use crate::hir_def::traits::TraitImpl; @@ -28,8 +29,7 @@ use crate::hir_def::{ }; use crate::token::{Attributes, SecondaryAttribute}; use crate::{ - BinaryOpKind, FunctionDefinition, Generics, ItemVisibility, Shared, TypeAlias, TypeBindings, - TypeVariable, TypeVariableId, TypeVariableKind, + Generics, Shared, TypeAlias, TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind, }; /// An arbitrary number to limit the recursion depth when searching for trait impls. @@ -242,6 +242,8 @@ pub struct FunctionModifiers { pub attributes: Attributes, pub is_unconstrained: bool, + + pub is_comptime: bool, } impl FunctionModifiers { @@ -254,6 +256,7 @@ impl FunctionModifiers { visibility: ItemVisibility::Public, attributes: Attributes::empty(), is_unconstrained: false, + is_comptime: false, } } } @@ -759,6 +762,7 @@ impl NodeInterner { visibility: function.visibility, attributes: function.attributes.clone(), is_unconstrained: function.is_unconstrained, + is_comptime: function.is_comptime, }; self.push_function_definition(id, modifiers, module, location) } @@ -818,10 +822,10 @@ impl NodeInterner { self.func_meta.get(func_id) } - pub fn function_ident(&self, func_id: &FuncId) -> crate::Ident { + 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::Ident(Spanned::from(span, name)) + crate::ast::Ident(Spanned::from(span, name)) } pub fn function_name(&self, func_id: &FuncId) -> &str { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/noir_parser.lalrpop b/noir/noir-repo/compiler/noirc_frontend/src/noir_parser.lalrpop index c8d293fb72f..ec2b4c8ab46 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/noir_parser.lalrpop +++ b/noir/noir-repo/compiler/noirc_frontend/src/noir_parser.lalrpop @@ -4,7 +4,7 @@ use crate::lexer::token::BorrowedToken; use crate::lexer::token as noir_token; use crate::lexer::errors::LexerErrorKind; use crate::parser::TopLevelStatement; -use crate::{Ident, Path, PathKind, UseTree, UseTreeKind}; +use crate::ast::{Ident, Path, PathKind, UseTree, UseTreeKind}; use lalrpop_util::ErrorRecovery; 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 895d4e07bbd..407b69c818b 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/errors.rs @@ -1,7 +1,6 @@ +use crate::ast::{Expression, IntegerBitSize}; use crate::lexer::errors::LexerErrorKind; use crate::lexer::token::Token; -use crate::Expression; -use crate::IntegerBitSize; use small_ord_set::SmallOrdSet; use thiserror::Error; 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 80c5f47f07b..9e60383afee 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/mod.rs @@ -11,12 +11,11 @@ mod labels; #[allow(clippy::module_inception)] mod parser; -use crate::token::{Keyword, Token}; -use crate::{ast::ImportStatement, Expression, NoirStruct}; -use crate::{ - Ident, LetStatement, ModuleDeclaration, NoirFunction, NoirTrait, NoirTraitImpl, NoirTypeAlias, - Recoverable, StatementKind, TypeImpl, UseTree, +use crate::ast::{ + Expression, Ident, ImportStatement, LetStatement, ModuleDeclaration, NoirFunction, NoirStruct, + NoirTrait, NoirTraitImpl, NoirTypeAlias, Recoverable, StatementKind, TypeImpl, UseTree, }; +use crate::token::{Keyword, Token}; use chumsky::prelude::*; use chumsky::primitive::Container; 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 5706c3ef12f..603193d1593 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser.rs @@ -33,17 +33,17 @@ use super::{ }; use super::{spanned, Item, ItemKind}; use crate::ast::{ - Expression, ExpressionKind, LetStatement, StatementKind, UnresolvedType, UnresolvedTypeData, -}; -use crate::lexer::{lexer::from_spanned_token_result, Lexer}; -use crate::parser::{force, ignore_then_commit, statement_recovery}; -use crate::token::{Keyword, Token, TokenKind}; -use crate::{ BinaryOp, BinaryOpKind, BlockExpression, Distinctness, ForLoopStatement, ForRange, FunctionReturnType, Ident, IfExpression, InfixExpression, LValue, Literal, ModuleDeclaration, NoirTypeAlias, Param, Path, Pattern, Recoverable, Statement, TraitBound, TypeImpl, UnresolvedTraitConstraint, UnresolvedTypeExpression, UseTree, UseTreeKind, Visibility, }; +use crate::ast::{ + Expression, ExpressionKind, LetStatement, StatementKind, UnresolvedType, UnresolvedTypeData, +}; +use crate::lexer::{lexer::from_spanned_token_result, Lexer}; +use crate::parser::{force, ignore_then_commit, statement_recovery}; +use crate::token::{Keyword, Token, TokenKind}; use chumsky::prelude::*; use iter_extended::vecmap; @@ -234,14 +234,16 @@ fn implementation() -> impl NoirParser { /// global_declaration: 'global' ident global_type_annotation '=' literal fn global_declaration() -> impl NoirParser { let p = attributes::attributes() + .then(maybe_comp_time()) .then_ignore(keyword(Keyword::Global).labelled(ParsingRuleLabel::Global)) .then(ident().map(Pattern::Identifier)); + let p = then_commit(p, optional_type_annotation()); let p = then_commit_ignore(p, just(Token::Assign)); let p = then_commit(p, expression()); - p.validate(|(((attributes, pattern), r#type), expression), span, emit| { + p.validate(|((((attributes, comptime), pattern), r#type), expression), span, emit| { let global_attributes = attributes::validate_secondary_attributes(attributes, span, emit); - LetStatement { pattern, r#type, expression, attributes: global_attributes } + LetStatement { pattern, r#type, comptime, expression, attributes: global_attributes } }) .map(TopLevelStatement::Global) } @@ -498,10 +500,11 @@ where assertion::assertion_eq(expr_parser.clone()), declaration(expr_parser.clone()), assignment(expr_parser.clone()), - for_loop(expr_no_constructors, statement), + for_loop(expr_no_constructors.clone(), statement.clone()), break_statement(), continue_statement(), return_statement(expr_parser.clone()), + comptime_statement(expr_parser.clone(), expr_no_constructors, statement), expr_parser.map(StatementKind::Expression), )) }) @@ -519,6 +522,35 @@ fn continue_statement() -> impl NoirParser { keyword(Keyword::Continue).to(StatementKind::Continue) } +fn comptime_statement<'a, P1, P2, S>( + expr: P1, + expr_no_constructors: P2, + statement: S, +) -> impl NoirParser + 'a +where + P1: ExprParser + 'a, + P2: ExprParser + 'a, + S: NoirParser + 'a, +{ + keyword(Keyword::CompTime) + .ignore_then(choice(( + declaration(expr), + for_loop(expr_no_constructors, statement.clone()), + block(statement).map_with_span(|block, span| { + StatementKind::Expression(Expression::new(ExpressionKind::Block(block), span)) + }), + ))) + .map(|statement| StatementKind::Comptime(Box::new(statement))) +} + +/// Comptime in an expression position only accepts entire blocks +fn comptime_expr<'a, S>(statement: S) -> impl NoirParser + 'a +where + S: NoirParser + 'a, +{ + keyword(Keyword::CompTime).ignore_then(block(statement)).map(ExpressionKind::Block) +} + fn declaration<'a, P>(expr_parser: P) -> impl NoirParser + 'a where P: ExprParser + 'a, @@ -700,24 +732,25 @@ fn optional_distinctness() -> impl NoirParser { }) } -fn maybe_comp_time() -> impl NoirParser<()> { +fn maybe_comp_time() -> impl NoirParser { keyword(Keyword::CompTime).or_not().validate(|opt, span, emit| { if opt.is_some() { - emit(ParserError::with_reason(ParserErrorReason::ComptimeDeprecated, span)); + emit(ParserError::with_reason( + ParserErrorReason::ExperimentalFeature("comptime"), + span, + )); } + opt.is_some() }) } fn field_type() -> impl NoirParser { - maybe_comp_time() - .then_ignore(keyword(Keyword::Field)) + keyword(Keyword::Field) .map_with_span(|_, span| UnresolvedTypeData::FieldElement.with_span(span)) } fn bool_type() -> impl NoirParser { - maybe_comp_time() - .then_ignore(keyword(Keyword::Bool)) - .map_with_span(|_, span| UnresolvedTypeData::Bool.with_span(span)) + keyword(Keyword::Bool).map_with_span(|_, span| UnresolvedTypeData::Bool.with_span(span)) } fn string_type() -> impl NoirParser { @@ -744,21 +777,20 @@ fn format_string_type( } fn int_type() -> impl NoirParser { - maybe_comp_time() - .then(filter_map(|span, token: Token| match token { - Token::IntType(int_type) => Ok(int_type), - unexpected => { - Err(ParserError::expected_label(ParsingRuleLabel::IntegerType, unexpected, span)) - } - })) - .validate(|(_, token), span, emit| { - UnresolvedTypeData::from_int_token(token) - .map(|data| data.with_span(span)) - .unwrap_or_else(|err| { - emit(ParserError::with_reason(ParserErrorReason::InvalidBitSize(err.0), span)); - UnresolvedType::error(span) - }) - }) + filter_map(|span, token: Token| match token { + Token::IntType(int_type) => Ok(int_type), + unexpected => { + Err(ParserError::expected_label(ParsingRuleLabel::IntegerType, unexpected, span)) + } + }) + .validate(|token, span, emit| { + UnresolvedTypeData::from_int_token(token).map(|data| data.with_span(span)).unwrap_or_else( + |err| { + emit(ParserError::with_reason(ParserErrorReason::InvalidBitSize(err.0), span)); + UnresolvedType::error(span) + }, + ) + }) } fn named_type(type_parser: impl NoirParser) -> impl NoirParser { @@ -1236,6 +1268,7 @@ where }, lambdas::lambda(expr_parser.clone()), block(statement.clone()).map(ExpressionKind::Block), + comptime_expr(statement.clone()), quote(statement), variable(), literal(), @@ -1320,7 +1353,7 @@ where mod test { use super::test_helpers::*; use super::*; - use crate::ArrayLiteral; + use crate::ast::ArrayLiteral; #[test] fn parse_infix() { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/assertion.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/assertion.rs index f9c8d7aa46b..ed08a4c9922 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/assertion.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/assertion.rs @@ -4,8 +4,8 @@ use crate::parser::{ ParserError, ParserErrorReason, }; +use crate::ast::{BinaryOpKind, ConstrainKind, ConstrainStatement, InfixExpression, Recoverable}; use crate::token::{Keyword, Token}; -use crate::{BinaryOpKind, ConstrainKind, ConstrainStatement, InfixExpression, Recoverable}; use chumsky::prelude::*; use noirc_errors::Spanned; @@ -74,11 +74,11 @@ where mod test { use super::*; use crate::{ + ast::Literal, parser::parser::{ expression, test_helpers::{parse_all, parse_all_failing, parse_with}, }, - Literal, }; /// Deprecated constrain usage test 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 06e1a958eb1..f39b2ad6292 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 @@ -1,16 +1,16 @@ use super::{ attributes::{attributes, validate_attributes}, - block, fresh_statement, ident, keyword, nothing, optional_distinctness, optional_visibility, - parameter_name_recovery, parameter_recovery, parenthesized, parse_type, pattern, - self_parameter, where_clause, NoirParser, + block, fresh_statement, ident, keyword, maybe_comp_time, nothing, optional_distinctness, + optional_visibility, parameter_name_recovery, parameter_recovery, parenthesized, parse_type, + pattern, self_parameter, where_clause, NoirParser, }; -use crate::parser::labels::ParsingRuleLabel; -use crate::parser::spanned; -use crate::token::{Keyword, Token}; -use crate::{ +use crate::ast::{ Distinctness, FunctionDefinition, FunctionReturnType, Ident, ItemVisibility, NoirFunction, Param, Visibility, }; +use crate::parser::labels::ParsingRuleLabel; +use crate::parser::spanned; +use crate::token::{Keyword, Token}; use chumsky::prelude::*; @@ -37,6 +37,7 @@ pub(super) fn function_definition(allow_self: bool) -> impl NoirParser impl NoirParser { /// function_modifiers: 'unconstrained'? (visibility)? /// /// returns (is_unconstrained, visibility) for whether each keyword was present -fn function_modifiers() -> impl NoirParser<(bool, ItemVisibility)> { +fn function_modifiers() -> impl NoirParser<(bool, ItemVisibility, bool)> { keyword(Keyword::Unconstrained) .or_not() .then(visibility_modifier()) - .map(|(unconstrained, visibility)| (unconstrained.is_some(), visibility)) + .then(maybe_comp_time()) + .map(|((unconstrained, visibility), comptime)| { + (unconstrained.is_some(), visibility, comptime) + }) } /// non_empty_ident_list: ident ',' non_empty_ident_list @@ -171,8 +175,8 @@ mod test { "fn f(f: pub Field, y : T, z : Field) -> u8 { x + a }", "fn func_name(x: [Field], y : [Field;2],y : pub [Field;2], z : pub [u8;5]) {}", "fn main(x: pub u8, y: pub u8) -> distinct pub [u8; 2] { [x, y] }", - "fn f(f: pub Field, y : Field, z : comptime Field) -> u8 { x + a }", - "fn f(f: pub Field, y : T, z : comptime Field) -> u8 { x + a }", + "fn f(f: pub Field, y : Field, z : Field) -> u8 { x + a }", + "fn f(f: pub Field, y : T, z : Field) -> u8 { x + a }", "fn func_name(f: Field, y : T) where T: SomeTrait {}", "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", "fn func_name(f: Field, y : T) where T: SomeTrait, T: SomeTrait2 {}", diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/lambdas.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/lambdas.rs index 48ddd41ab44..2b4a1d547c0 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/lambdas.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/lambdas.rs @@ -1,13 +1,12 @@ use chumsky::{primitive::just, Parser}; +use super::{parse_type, pattern}; +use crate::ast::{Expression, ExpressionKind, Lambda, Pattern, UnresolvedType}; use crate::{ parser::{labels::ParsingRuleLabel, parameter_name_recovery, parameter_recovery, NoirParser}, token::Token, - Expression, ExpressionKind, Lambda, Pattern, UnresolvedType, }; -use super::{parse_type, pattern}; - pub(super) fn lambda<'a>( expr_parser: impl NoirParser + 'a, ) -> impl NoirParser + 'a { diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/literals.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/literals.rs index 83d7b832d27..584224fda46 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/literals.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/literals.rs @@ -1,9 +1,9 @@ use chumsky::Parser; use crate::{ + ast::ExpressionKind, parser::NoirParser, token::{Token, TokenKind}, - ExpressionKind, }; use super::primitives::token_kind; @@ -22,10 +22,10 @@ pub(super) fn literal() -> impl NoirParser { #[cfg(test)] mod test { use super::*; + use crate::ast::Literal; use crate::parser::parser::{ expression, expression_no_constructors, fresh_statement, term, test_helpers::*, }; - use crate::Literal; fn expr_to_lit(expr: ExpressionKind) -> Literal { match expr { 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 ab812c07dce..47bb11991fa 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/path.rs @@ -1,5 +1,5 @@ +use crate::ast::{Path, PathKind}; use crate::parser::NoirParser; -use crate::{Path, PathKind}; use crate::token::{Keyword, Token}; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/primitives.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/primitives.rs index 34927278038..8413f14ae4d 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/primitives.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/primitives.rs @@ -1,9 +1,9 @@ use chumsky::prelude::*; +use crate::ast::{ExpressionKind, Ident, UnaryOp}; use crate::{ parser::{labels::ParsingRuleLabel, ExprParser, NoirParser, ParserError}, token::{Keyword, Token, TokenKind}, - ExpressionKind, Ident, UnaryOp, }; use super::path; 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 87e58f69efb..7da956bdfea 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,5 +1,6 @@ use chumsky::prelude::*; +use crate::ast::{Ident, NoirStruct, UnresolvedType}; use crate::{ parser::{ parser::{ @@ -10,7 +11,6 @@ use crate::{ NoirParser, TopLevelStatement, }, token::{Keyword, Token}, - Ident, NoirStruct, UnresolvedType, }; pub(super) fn struct_definition() -> impl NoirParser { 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 1e2a6b4d65d..0507dbdbc71 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 @@ -5,14 +5,16 @@ use super::{ function_return_type, }; +use crate::ast::{ + Expression, ItemVisibility, NoirTrait, NoirTraitImpl, TraitBound, TraitImplItem, TraitItem, + UnresolvedTraitConstraint, UnresolvedType, +}; use crate::{ parser::{ ignore_then_commit, parenthesized, parser::primitives::keyword, NoirParser, ParserError, ParserErrorReason, TopLevelStatement, }, token::{Keyword, Token}, - Expression, ItemVisibility, NoirTrait, NoirTraitImpl, TraitBound, TraitImplItem, TraitItem, - UnresolvedTraitConstraint, UnresolvedType, }; use super::{generic_type_args, parse_type, path, primitives::ident}; diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs index e4d308fbb6b..31bf2245b1f 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs @@ -66,7 +66,7 @@ mod test { // Allocate a default Module for the root, giving it a ModuleId let mut modules: Arena = Arena::default(); let location = Location::new(Default::default(), root_file_id); - let root = modules.insert(ModuleData::new(None, None, location, false)); + let root = modules.insert(ModuleData::new(None, location, false)); let def_map = CrateDefMap { root: LocalModuleId(root), @@ -780,6 +780,7 @@ mod test { HirStatement::Error => panic!("Invalid HirStatement!"), HirStatement::Break => panic!("Unexpected break"), HirStatement::Continue => panic!("Unexpected continue"), + HirStatement::Comptime(_) => panic!("Unexpected comptime"), }; let expr = interner.expression(&expr_id); diff --git a/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml b/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml index fbbe778e561..5140f5a5a8c 100644 --- a/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_printable_type/Cargo.toml @@ -3,6 +3,7 @@ name = "noirc_printable_type" version.workspace = true authors.workspace = true edition.workspace = true +rust-version.workspace = true license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/noir/noir-repo/compiler/utils/arena/Cargo.toml b/noir/noir-repo/compiler/utils/arena/Cargo.toml index 41c6ebc9a8b..f6bd764ee62 100644 --- a/noir/noir-repo/compiler/utils/arena/Cargo.toml +++ b/noir/noir-repo/compiler/utils/arena/Cargo.toml @@ -3,4 +3,5 @@ name = "arena" version.workspace = true authors.workspace = true edition.workspace = true +rust-version.workspace = true license.workspace = true diff --git a/noir/noir-repo/compiler/utils/iter-extended/Cargo.toml b/noir/noir-repo/compiler/utils/iter-extended/Cargo.toml index c91e5ea6d77..4343311506e 100644 --- a/noir/noir-repo/compiler/utils/iter-extended/Cargo.toml +++ b/noir/noir-repo/compiler/utils/iter-extended/Cargo.toml @@ -3,6 +3,7 @@ name = "iter-extended" version.workspace = true authors.workspace = true edition.workspace = true +rust-version.workspace = true license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/noir/noir-repo/compiler/wasm/Cargo.toml b/noir/noir-repo/compiler/wasm/Cargo.toml index a20efeeed8a..31ef5161014 100644 --- a/noir/noir-repo/compiler/wasm/Cargo.toml +++ b/noir/noir-repo/compiler/wasm/Cargo.toml @@ -3,6 +3,7 @@ name = "noir_wasm" version.workspace = true authors.workspace = true edition.workspace = true +rust-version.workspace = true license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/noir/noir-repo/docs/docs/noir/concepts/data_types/slices.mdx b/noir/noir-repo/docs/docs/noir/concepts/data_types/slices.mdx index 828faf4a8f8..4eccc677b80 100644 --- a/noir/noir-repo/docs/docs/noir/concepts/data_types/slices.mdx +++ b/noir/noir-repo/docs/docs/noir/concepts/data_types/slices.mdx @@ -168,3 +168,28 @@ fn main() { 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]); +} +``` diff --git a/noir/noir-repo/docs/docs/noir/standard_library/containers/hashmap.md b/noir/noir-repo/docs/docs/noir/standard_library/containers/hashmap.md index 093b6d38d11..2b9f4895722 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/containers/hashmap.md +++ b/noir/noir-repo/docs/docs/noir/standard_library/containers/hashmap.md @@ -20,8 +20,9 @@ Example: ```rust // Create a mapping from Fields to u32s with a maximum length of 12 -// using a pedersen hash -let mut map: HashMap> = HashMap::default(); +// using a poseidon2 hasher +use dep::std::hash::poseidon2::Poseidon2Hasher; +let mut map: HashMap> = HashMap::default(); map.insert(1, 2); map.insert(3, 4); 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 f98c90a97c8..7329880c7a7 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 @@ -13,7 +13,6 @@ import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; ## sha256 Given an array of bytes, returns the resulting sha256 hash. -See sha256_slice for a version that works directly on slices. #include_code sha256 noir_stdlib/src/hash.nr rust @@ -28,18 +27,9 @@ fn main() { -## sha256_slice - -A version of sha256 specialized to slices: - -#include_code sha256_slice noir_stdlib/src/hash.nr rust - - - ## blake2s Given an array of bytes, returns an array with the Blake2 hash -See blake2s_slice for a version that works directly on slices. #include_code blake2s noir_stdlib/src/hash.nr rust @@ -54,18 +44,9 @@ fn main() { -## blake2s_slice - -A version of blake2s specialized to slices: - -#include_code blake2s_slice noir_stdlib/src/hash.nr rust - - - ## blake3 Given an array of bytes, returns an array with the Blake3 hash -See blake3_slice for a version that works directly on slices. #include_code blake3 noir_stdlib/src/hash.nr rust @@ -80,18 +61,9 @@ fn main() { -## blake3_slice - -A version of blake3 specialized to slices: - -#include_code blake3_slice noir_stdlib/src/hash.nr rust - - - ## pedersen_hash Given an array of Fields, returns the Pedersen hash. -See pedersen_hash_slice for a version that works directly on slices. #include_code pedersen_hash noir_stdlib/src/hash.nr rust @@ -101,18 +73,9 @@ example: -## pedersen_hash_slice - -Given a slice of Fields, returns the Pedersen hash. - -#include_code pedersen_hash_slice noir_stdlib/src/hash.nr rust - - - ## pedersen_commitment Given an array of Fields, returns the Pedersen commitment. -See pedersen_commitment_slice for a version that works directly on slices. #include_code pedersen_commitment noir_stdlib/src/hash.nr rust @@ -122,20 +85,11 @@ example: -## pedersen_commitment_slice - -Given a slice of Fields, returns the Pedersen commitment. - -#include_code pedersen_commitment_slice noir_stdlib/src/hash.nr rust - - - ## 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. See keccak256_slice for a version that works -directly on slices. +`message_size` bytes of the input. #include_code keccak256 noir_stdlib/src/hash.nr rust @@ -145,15 +99,6 @@ example: -## keccak256_slice - -Given a slice of bytes (`u8`), returns the resulting keccak hash as an array of -32 bytes (`[u8; 32]`). - -#include_code keccak256_slice noir_stdlib/src/hash.nr rust - - - ## poseidon Given an array of Fields, returns a new Field with the Poseidon Hash. Mind that you need to specify 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 e6e7e6d40cb..2536d9a943f 100644 --- a/noir/noir-repo/docs/docs/noir/standard_library/traits.md +++ b/noir/noir-repo/docs/docs/noir/standard_library/traits.md @@ -140,6 +140,8 @@ impl Eq for (A, B, C, D, E) 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 diff --git a/noir/noir-repo/docs/docs/tutorials/noirjs_app.md b/noir/noir-repo/docs/docs/tutorials/noirjs_app.md index 12beb476994..6446e0b2a76 100644 --- a/noir/noir-repo/docs/docs/tutorials/noirjs_app.md +++ b/noir/noir-repo/docs/docs/tutorials/noirjs_app.md @@ -14,9 +14,9 @@ You can find the complete app code for this guide [here](https://github.com/noir :::note -Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.19.x matches `noir_js@0.19.x`, etc. +Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.27.x matches `noir_js@0.27.x`, etc. -In this guide, we will be pinned to 0.19.4. +In this guide, we will be pinned to 0.27.0. ::: @@ -34,7 +34,7 @@ Easy enough. Onwards! ## Our project -ZK is a powerful technology. An app that doesn't reveal one of the inputs to *anyone* is almost unbelievable, yet Noir makes it as easy as a single line of code. +ZK is a powerful technology. An app that doesn't reveal one of the inputs to _anyone_ is almost unbelievable, yet Noir makes it as easy as a single line of code. In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! @@ -42,13 +42,13 @@ In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! Run: -```nargo new circuit``` +`nargo new circuit` And... That's about it. Your program is ready to be compiled and run. To compile, let's `cd` into the `circuit` folder to enter our project, and call: -```nargo compile``` +`nargo compile` This compiles our circuit into `json` format and add it to a new `target` folder. @@ -77,10 +77,55 @@ Vite is a powerful tool to generate static websites. While it provides all kinds To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". -You should see `vite-project` appear in your root folder. This seems like a good time to `cd` into it and install our NoirJS packages: +A wild `vite-project` directory should now appear in your root folder! Let's not waste any time and dive right in: ```bash -npm i @noir-lang/backend_barretenberg@0.19.4 @noir-lang/noir_js@0.19.4 +cd vite-project +``` + +### Setting Up Vite and Configuring the Project + +Before we proceed with any coding, let's get our environment tailored for Noir. We'll start by laying down the foundations with a `vite.config.js` file. This little piece of configuration is our secret sauce for making sure everything meshes well with the NoirJS libraries and other special setups we might need, like handling WebAssembly modules. Here’s how you get that going: + +#### Creating the vite.config.js + +In your freshly minted `vite-project` folder, create a new file named `vite.config.js` and open it in your code editor. Paste the following to set the stage: + +```javascript +import { defineConfig } from "vite"; +import copy from "rollup-plugin-copy"; + +export default defineConfig({ + esbuild: { + target: "esnext", + }, + optimizeDeps: { + esbuildOptions: { + target: "esnext", + }, + }, + plugins: [ + copy({ + targets: [ + { src: "node_modules/**/*.wasm", dest: "node_modules/.vite/dist" }, + ], + copySync: true, + hook: "buildStart", + }), + ], + server: { + port: 3000, + }, +}); +``` + +#### Install Dependencies + +Now that our stage is set, install the necessary NoirJS packages along with our other dependencies: + +```bash +npm install && npm install @noir-lang/backend_barretenberg@0.27.0 @noir-lang/noir_js@0.27.0 +npm install rollup-plugin-copy --save-dev ``` :::info @@ -99,7 +144,7 @@ At this point in the tutorial, your folder structure should look like this: #### Some cleanup -`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `index.html`, `main.js` and `package.json`. I feel lighter already. +`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `vite.config.js`, `index.html`, `main.js` and `package.json`. I feel lighter already. ![my heart is ready for you, noir.js](@site/static/img/memes/titanic.jpeg) @@ -139,7 +184,7 @@ Our app won't run like this, of course. We need some working HTML, at least. Let ``` -It *could* be a beautiful UI... Depending on which universe you live in. +It _could_ be a beautiful UI... Depending on which universe you live in. ## Some good old vanilla Javascript @@ -150,14 +195,14 @@ Start by pasting in this boilerplate code: ```js const setup = async () => { await Promise.all([ - import("@noir-lang/noirc_abi").then(module => - module.default(new URL("@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm", import.meta.url).toString()) + import('@noir-lang/noirc_abi').then((module) => + module.default(new URL('@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm', import.meta.url).toString()), + ), + import('@noir-lang/acvm_js').then((module) => + module.default(new URL('@noir-lang/acvm_js/web/acvm_js_bg.wasm', import.meta.url).toString()), ), - import("@noir-lang/acvm_js").then(module => - module.default(new URL("@noir-lang/acvm_js/web/acvm_js_bg.wasm", import.meta.url).toString()) - ) ]); -} +}; function display(container, msg) { const c = document.getElementById(container); @@ -169,11 +214,10 @@ function display(container, msg) { document.getElementById('submitGuess').addEventListener('click', async () => { try { // here's where love happens - } catch(err) { - display("logs", "Oh πŸ’” Wrong guess") + } catch (err) { + display('logs', 'Oh πŸ’” Wrong guess'); } }); - ``` The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😒 @@ -189,6 +233,7 @@ At this point in the tutorial, your folder structure should look like this: └── circuit └── ...same as above └── vite-project + β”œβ”€β”€ vite.config.js β”œβ”€β”€ main.js β”œβ”€β”€ package.json └── index.html @@ -209,7 +254,7 @@ import circuit from '../circuit/target/circuit.json'; [Noir is backend-agnostic](../index.mdx#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: ```js -import { BarretenbergBackend } from '@noir-lang/backend_barretenberg'; +import { BarretenbergBackend, BarretenbergVerifier as Verifier } from '@noir-lang/backend_barretenberg'; import { Noir } from '@noir-lang/noir_js'; ``` @@ -264,8 +309,10 @@ Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add th ```js display('logs', 'Verifying proof... βŒ›'); -const verification = await noir.verifyProof(proof); -if (verification) display('logs', 'Verifying proof... βœ…'); +const verificationKey = await backend.getVerificationKey(); +const verifier = new Verifier(); +const isValid = await verifier.verifyProof(proof, verificationKey); +if (isValid) display('logs', 'Verifying proof... βœ…'); ``` You have successfully generated a client-side Noir web app! diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.22.0/getting_started/tooling/index.md b/noir/noir-repo/docs/versioned_docs/version-v0.22.0/getting_started/tooling/index.md index 55df833005a..16f5f3ca45e 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.22.0/getting_started/tooling/index.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.22.0/getting_started/tooling/index.md @@ -1,15 +1,11 @@ --- title: Tooling -Description: This section provides information about the various tools and utilities available for Noir development. It covers the Noir playground, IDE tools, Codespaces, and community projects. +Description: This section provides information about the various tools and utilities available for Noir development. It covers IDE tools, Codespaces, and community projects. Keywords: [Noir, Development, Playground, IDE Tools, Language Service Provider, VS Code Extension, Codespaces, noir-starter, Community Projects, Awesome Noir Repository, Developer Tooling] --- Noir is meant to be easy to develop with. For that reason, a number of utilities have been put together to ease the development process as much as feasible in the zero-knowledge world. -## Playground - -The Noir playground is an easy way to test small ideas, share snippets, and integrate in other websites. You can access it at [play.noir-lang.org](https://play.noir-lang.org). - ## IDE tools When you install Nargo, you're also installing a Language Service Provider (LSP), which can be used by IDEs to provide syntax highlighting, codelens, warnings, and more. diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.23.0/getting_started/tooling/index.mdx b/noir/noir-repo/docs/versioned_docs/version-v0.23.0/getting_started/tooling/index.mdx index ac480f3c9f5..ec9ccea4115 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.23.0/getting_started/tooling/index.mdx +++ b/noir/noir-repo/docs/versioned_docs/version-v0.23.0/getting_started/tooling/index.mdx @@ -1,15 +1,11 @@ --- title: Tooling -Description: This section provides information about the various tools and utilities available for Noir development. It covers the Noir playground, IDE tools, Codespaces, and community projects. +Description: This section provides information about the various tools and utilities available for Noir development. It covers IDE tools, Codespaces, and community projects. Keywords: [Noir, Development, Playground, IDE Tools, Language Service Provider, VS Code Extension, Codespaces, noir-starter, Community Projects, Awesome Noir Repository, Developer Tooling] --- Noir is meant to be easy to develop with. For that reason, a number of utilities have been put together to ease the development process as much as feasible in the zero-knowledge world. -## Playground - -The Noir playground is an easy way to test small ideas, share snippets, and integrate in other websites. You can access it at [play.noir-lang.org](https://play.noir-lang.org). - ## IDE tools When you install Nargo, you're also installing a Language Service Provider (LSP), which can be used by IDEs to provide syntax highlighting, codelens, warnings, and more. diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.24.0/getting_started/tooling/index.mdx b/noir/noir-repo/docs/versioned_docs/version-v0.24.0/getting_started/tooling/index.mdx index ac480f3c9f5..ec9ccea4115 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.24.0/getting_started/tooling/index.mdx +++ b/noir/noir-repo/docs/versioned_docs/version-v0.24.0/getting_started/tooling/index.mdx @@ -1,15 +1,11 @@ --- title: Tooling -Description: This section provides information about the various tools and utilities available for Noir development. It covers the Noir playground, IDE tools, Codespaces, and community projects. +Description: This section provides information about the various tools and utilities available for Noir development. It covers IDE tools, Codespaces, and community projects. Keywords: [Noir, Development, Playground, IDE Tools, Language Service Provider, VS Code Extension, Codespaces, noir-starter, Community Projects, Awesome Noir Repository, Developer Tooling] --- Noir is meant to be easy to develop with. For that reason, a number of utilities have been put together to ease the development process as much as feasible in the zero-knowledge world. -## Playground - -The Noir playground is an easy way to test small ideas, share snippets, and integrate in other websites. You can access it at [play.noir-lang.org](https://play.noir-lang.org). - ## IDE tools When you install Nargo, you're also installing a Language Service Provider (LSP), which can be used by IDEs to provide syntax highlighting, codelens, warnings, and more. diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.25.0/getting_started/tooling/index.mdx b/noir/noir-repo/docs/versioned_docs/version-v0.25.0/getting_started/tooling/index.mdx index ac480f3c9f5..ec9ccea4115 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.25.0/getting_started/tooling/index.mdx +++ b/noir/noir-repo/docs/versioned_docs/version-v0.25.0/getting_started/tooling/index.mdx @@ -1,15 +1,11 @@ --- title: Tooling -Description: This section provides information about the various tools and utilities available for Noir development. It covers the Noir playground, IDE tools, Codespaces, and community projects. +Description: This section provides information about the various tools and utilities available for Noir development. It covers IDE tools, Codespaces, and community projects. Keywords: [Noir, Development, Playground, IDE Tools, Language Service Provider, VS Code Extension, Codespaces, noir-starter, Community Projects, Awesome Noir Repository, Developer Tooling] --- Noir is meant to be easy to develop with. For that reason, a number of utilities have been put together to ease the development process as much as feasible in the zero-knowledge world. -## Playground - -The Noir playground is an easy way to test small ideas, share snippets, and integrate in other websites. You can access it at [play.noir-lang.org](https://play.noir-lang.org). - ## IDE tools When you install Nargo, you're also installing a Language Service Provider (LSP), which can be used by IDEs to provide syntax highlighting, codelens, warnings, and more. diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md b/noir/noir-repo/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md index 12beb476994..02044cd2ff6 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md @@ -14,9 +14,9 @@ You can find the complete app code for this guide [here](https://github.com/noir :::note -Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.19.x matches `noir_js@0.19.x`, etc. +Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.25.x matches `noir_js@0.25.x`, etc. -In this guide, we will be pinned to 0.19.4. +In this guide, we will be pinned to 0.25.0. ::: @@ -34,7 +34,7 @@ Easy enough. Onwards! ## Our project -ZK is a powerful technology. An app that doesn't reveal one of the inputs to *anyone* is almost unbelievable, yet Noir makes it as easy as a single line of code. +ZK is a powerful technology. An app that doesn't reveal one of the inputs to _anyone_ is almost unbelievable, yet Noir makes it as easy as a single line of code. In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! @@ -42,13 +42,13 @@ In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! Run: -```nargo new circuit``` +`nargo new circuit` And... That's about it. Your program is ready to be compiled and run. To compile, let's `cd` into the `circuit` folder to enter our project, and call: -```nargo compile``` +`nargo compile` This compiles our circuit into `json` format and add it to a new `target` folder. @@ -77,10 +77,55 @@ Vite is a powerful tool to generate static websites. While it provides all kinds To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". -You should see `vite-project` appear in your root folder. This seems like a good time to `cd` into it and install our NoirJS packages: +A wild `vite-project` directory should now appear in your root folder! Let's not waste any time and dive right in: ```bash -npm i @noir-lang/backend_barretenberg@0.19.4 @noir-lang/noir_js@0.19.4 +cd vite-project +``` + +### Setting Up Vite and Configuring the Project + +Before we proceed with any coding, let's get our environment tailored for Noir. We'll start by laying down the foundations with a `vite.config.js` file. This little piece of configuration is our secret sauce for making sure everything meshes well with the NoirJS libraries and other special setups we might need, like handling WebAssembly modules. Here’s how you get that going: + +#### Creating the vite.config.js + +In your freshly minted `vite-project` folder, create a new file named `vite.config.js` and open it in your code editor. Paste the following to set the stage: + +```javascript +import { defineConfig } from "vite"; +import copy from "rollup-plugin-copy"; + +export default defineConfig({ + esbuild: { + target: "esnext", + }, + optimizeDeps: { + esbuildOptions: { + target: "esnext", + }, + }, + plugins: [ + copy({ + targets: [ + { src: "node_modules/**/*.wasm", dest: "node_modules/.vite/dist" }, + ], + copySync: true, + hook: "buildStart", + }), + ], + server: { + port: 3000, + }, +}); +``` + +#### Install Dependencies + +Now that our stage is set, install the necessary NoirJS packages along with our other dependencies: + +```bash +npm install && npm install @noir-lang/backend_barretenberg@0.25.0 @noir-lang/noir_js@0.25.0 +npm install rollup-plugin-copy --save-dev ``` :::info @@ -99,7 +144,7 @@ At this point in the tutorial, your folder structure should look like this: #### Some cleanup -`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `index.html`, `main.js` and `package.json`. I feel lighter already. +`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `vite.config.js`, `index.html`, `main.js` and `package.json`. I feel lighter already. ![my heart is ready for you, noir.js](@site/static/img/memes/titanic.jpeg) @@ -139,7 +184,7 @@ Our app won't run like this, of course. We need some working HTML, at least. Let ``` -It *could* be a beautiful UI... Depending on which universe you live in. +It _could_ be a beautiful UI... Depending on which universe you live in. ## Some good old vanilla Javascript @@ -150,14 +195,14 @@ Start by pasting in this boilerplate code: ```js const setup = async () => { await Promise.all([ - import("@noir-lang/noirc_abi").then(module => - module.default(new URL("@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm", import.meta.url).toString()) + import('@noir-lang/noirc_abi').then((module) => + module.default(new URL('@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm', import.meta.url).toString()), + ), + import('@noir-lang/acvm_js').then((module) => + module.default(new URL('@noir-lang/acvm_js/web/acvm_js_bg.wasm', import.meta.url).toString()), ), - import("@noir-lang/acvm_js").then(module => - module.default(new URL("@noir-lang/acvm_js/web/acvm_js_bg.wasm", import.meta.url).toString()) - ) ]); -} +}; function display(container, msg) { const c = document.getElementById(container); @@ -169,11 +214,10 @@ function display(container, msg) { document.getElementById('submitGuess').addEventListener('click', async () => { try { // here's where love happens - } catch(err) { - display("logs", "Oh πŸ’” Wrong guess") + } catch (err) { + display('logs', 'Oh πŸ’” Wrong guess'); } }); - ``` The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😒 @@ -189,6 +233,7 @@ At this point in the tutorial, your folder structure should look like this: └── circuit └── ...same as above └── vite-project + └── vite.config.js β”œβ”€β”€ main.js β”œβ”€β”€ package.json └── index.html diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.26.0/getting_started/tooling/index.mdx b/noir/noir-repo/docs/versioned_docs/version-v0.26.0/getting_started/tooling/index.mdx index ac480f3c9f5..ec9ccea4115 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.26.0/getting_started/tooling/index.mdx +++ b/noir/noir-repo/docs/versioned_docs/version-v0.26.0/getting_started/tooling/index.mdx @@ -1,15 +1,11 @@ --- title: Tooling -Description: This section provides information about the various tools and utilities available for Noir development. It covers the Noir playground, IDE tools, Codespaces, and community projects. +Description: This section provides information about the various tools and utilities available for Noir development. It covers IDE tools, Codespaces, and community projects. Keywords: [Noir, Development, Playground, IDE Tools, Language Service Provider, VS Code Extension, Codespaces, noir-starter, Community Projects, Awesome Noir Repository, Developer Tooling] --- Noir is meant to be easy to develop with. For that reason, a number of utilities have been put together to ease the development process as much as feasible in the zero-knowledge world. -## Playground - -The Noir playground is an easy way to test small ideas, share snippets, and integrate in other websites. You can access it at [play.noir-lang.org](https://play.noir-lang.org). - ## IDE tools When you install Nargo, you're also installing a Language Service Provider (LSP), which can be used by IDEs to provide syntax highlighting, codelens, warnings, and more. diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.26.0/tutorials/noirjs_app.md b/noir/noir-repo/docs/versioned_docs/version-v0.26.0/tutorials/noirjs_app.md index 12beb476994..d098827e6d9 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.26.0/tutorials/noirjs_app.md +++ b/noir/noir-repo/docs/versioned_docs/version-v0.26.0/tutorials/noirjs_app.md @@ -14,9 +14,9 @@ You can find the complete app code for this guide [here](https://github.com/noir :::note -Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.19.x matches `noir_js@0.19.x`, etc. +Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.26.x matches `noir_js@0.26.x`, etc. -In this guide, we will be pinned to 0.19.4. +In this guide, we will be pinned to 0.26.0. ::: @@ -34,7 +34,7 @@ Easy enough. Onwards! ## Our project -ZK is a powerful technology. An app that doesn't reveal one of the inputs to *anyone* is almost unbelievable, yet Noir makes it as easy as a single line of code. +ZK is a powerful technology. An app that doesn't reveal one of the inputs to _anyone_ is almost unbelievable, yet Noir makes it as easy as a single line of code. In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! @@ -42,13 +42,13 @@ In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! Run: -```nargo new circuit``` +`nargo new circuit` And... That's about it. Your program is ready to be compiled and run. To compile, let's `cd` into the `circuit` folder to enter our project, and call: -```nargo compile``` +`nargo compile` This compiles our circuit into `json` format and add it to a new `target` folder. @@ -77,10 +77,55 @@ Vite is a powerful tool to generate static websites. While it provides all kinds To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". -You should see `vite-project` appear in your root folder. This seems like a good time to `cd` into it and install our NoirJS packages: +A wild `vite-project` directory should now appear in your root folder! Let's not waste any time and dive right in: ```bash -npm i @noir-lang/backend_barretenberg@0.19.4 @noir-lang/noir_js@0.19.4 +cd vite-project +``` + +### Setting Up Vite and Configuring the Project + +Before we proceed with any coding, let's get our environment tailored for Noir. We'll start by laying down the foundations with a `vite.config.js` file. This little piece of configuration is our secret sauce for making sure everything meshes well with the NoirJS libraries and other special setups we might need, like handling WebAssembly modules. Here’s how you get that going: + +#### Creating the vite.config.js + +In your freshly minted `vite-project` folder, create a new file named `vite.config.js` and open it in your code editor. Paste the following to set the stage: + +```javascript +import { defineConfig } from "vite"; +import copy from "rollup-plugin-copy"; + +export default defineConfig({ + esbuild: { + target: "esnext", + }, + optimizeDeps: { + esbuildOptions: { + target: "esnext", + }, + }, + plugins: [ + copy({ + targets: [ + { src: "node_modules/**/*.wasm", dest: "node_modules/.vite/dist" }, + ], + copySync: true, + hook: "buildStart", + }), + ], + server: { + port: 3000, + }, +}); +``` + +#### Install Dependencies + +Now that our stage is set, install the necessary NoirJS packages along with our other dependencies: + +```bash +npm install && npm install @noir-lang/backend_barretenberg@0.26.0 @noir-lang/noir_js@0.26.0 +npm install rollup-plugin-copy --save-dev ``` :::info @@ -99,7 +144,7 @@ At this point in the tutorial, your folder structure should look like this: #### Some cleanup -`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `index.html`, `main.js` and `package.json`. I feel lighter already. +`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `vite.config.js`, `index.html`, `main.js` and `package.json`. I feel lighter already. ![my heart is ready for you, noir.js](@site/static/img/memes/titanic.jpeg) @@ -139,7 +184,7 @@ Our app won't run like this, of course. We need some working HTML, at least. Let ``` -It *could* be a beautiful UI... Depending on which universe you live in. +It _could_ be a beautiful UI... Depending on which universe you live in. ## Some good old vanilla Javascript @@ -150,14 +195,14 @@ Start by pasting in this boilerplate code: ```js const setup = async () => { await Promise.all([ - import("@noir-lang/noirc_abi").then(module => - module.default(new URL("@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm", import.meta.url).toString()) + import('@noir-lang/noirc_abi').then((module) => + module.default(new URL('@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm', import.meta.url).toString()), + ), + import('@noir-lang/acvm_js').then((module) => + module.default(new URL('@noir-lang/acvm_js/web/acvm_js_bg.wasm', import.meta.url).toString()), ), - import("@noir-lang/acvm_js").then(module => - module.default(new URL("@noir-lang/acvm_js/web/acvm_js_bg.wasm", import.meta.url).toString()) - ) ]); -} +}; function display(container, msg) { const c = document.getElementById(container); @@ -169,11 +214,10 @@ function display(container, msg) { document.getElementById('submitGuess').addEventListener('click', async () => { try { // here's where love happens - } catch(err) { - display("logs", "Oh πŸ’” Wrong guess") + } catch (err) { + display('logs', 'Oh πŸ’” Wrong guess'); } }); - ``` The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😒 @@ -189,6 +233,7 @@ At this point in the tutorial, your folder structure should look like this: └── circuit └── ...same as above └── vite-project + └── vite.config.js β”œβ”€β”€ main.js β”œβ”€β”€ package.json └── index.html diff --git a/noir/noir-repo/docs/versioned_docs/version-v0.27.0/getting_started/tooling/index.mdx b/noir/noir-repo/docs/versioned_docs/version-v0.27.0/getting_started/tooling/index.mdx index ac480f3c9f5..ec9ccea4115 100644 --- a/noir/noir-repo/docs/versioned_docs/version-v0.27.0/getting_started/tooling/index.mdx +++ b/noir/noir-repo/docs/versioned_docs/version-v0.27.0/getting_started/tooling/index.mdx @@ -1,15 +1,11 @@ --- title: Tooling -Description: This section provides information about the various tools and utilities available for Noir development. It covers the Noir playground, IDE tools, Codespaces, and community projects. +Description: This section provides information about the various tools and utilities available for Noir development. It covers IDE tools, Codespaces, and community projects. Keywords: [Noir, Development, Playground, IDE Tools, Language Service Provider, VS Code Extension, Codespaces, noir-starter, Community Projects, Awesome Noir Repository, Developer Tooling] --- Noir is meant to be easy to develop with. For that reason, a number of utilities have been put together to ease the development process as much as feasible in the zero-knowledge world. -## Playground - -The Noir playground is an easy way to test small ideas, share snippets, and integrate in other websites. You can access it at [play.noir-lang.org](https://play.noir-lang.org). - ## IDE tools When you install Nargo, you're also installing a Language Service Provider (LSP), which can be used by IDEs to provide syntax highlighting, codelens, warnings, and more. diff --git a/noir/noir-repo/noir-logo.png b/noir/noir-repo/noir-logo.png new file mode 100644 index 00000000000..eabb163ad73 Binary files /dev/null and b/noir/noir-repo/noir-logo.png differ diff --git a/noir/noir-repo/noir_stdlib/src/cmp.nr b/noir/noir-repo/noir_stdlib/src/cmp.nr index dde29d7ee87..457b2cfa167 100644 --- a/noir/noir-repo/noir_stdlib/src/cmp.nr +++ b/noir/noir-repo/noir_stdlib/src/cmp.nr @@ -314,3 +314,55 @@ impl Ord for (A, B, C, D, E) where A: Ord, B: Ord, C: Ord, D: Ord result } } + +// Compares and returns the maximum of two values. +// +// Returns the second argument if the comparison determines them to be equal. +// +// # Examples +// +// ``` +// use std::cmp; +// +// assert_eq(cmp::max(1, 2), 2); +// assert_eq(cmp::max(2, 2), 2); +// ``` +pub fn max(v1: T, v2: T) -> T where T: Ord { + if v1 > v2 { v1 } else { v2 } +} + +// Compares and returns the minimum of two values. +// +// Returns the first argument if the comparison determines them to be equal. +// +// # Examples +// +// ``` +// use std::cmp; +// +// assert_eq(cmp::min(1, 2), 1); +// assert_eq(cmp::min(2, 2), 2); +// ``` +pub fn min(v1: T, v2: T) -> T where T: Ord { + if v1 > v2 { v2 } else { v1 } +} + +mod cmp_tests { + use crate::cmp::{min, max}; + + #[test] + fn sanity_check_min() { + assert_eq(min(0 as u64, 1 as u64), 0); + assert_eq(min(0 as u64, 0 as u64), 0); + assert_eq(min(1 as u64, 1 as u64), 1); + assert_eq(min(255 as u8, 0 as u8), 0); + } + + #[test] + fn sanity_check_max() { + assert_eq(max(0 as u64, 1 as u64), 1); + assert_eq(max(0 as u64, 0 as u64), 0); + assert_eq(max(1 as u64, 1 as u64), 1); + assert_eq(max(255 as u8, 0 as u8), 255); + } +} diff --git a/noir/noir-repo/noir_stdlib/src/collections/bounded_vec.nr b/noir/noir-repo/noir_stdlib/src/collections/bounded_vec.nr index c789bc386ef..c6a3365a979 100644 --- a/noir/noir-repo/noir_stdlib/src/collections/bounded_vec.nr +++ b/noir/noir-repo/noir_stdlib/src/collections/bounded_vec.nr @@ -1,3 +1,5 @@ +use crate::cmp::Eq; + struct BoundedVec { storage: [T; MaxLen], len: u64, @@ -93,3 +95,37 @@ impl BoundedVec { ret } } + +impl Eq for BoundedVec where T: Eq { + fn eq(self, other: BoundedVec) -> bool { + // TODO: https://github.com/noir-lang/noir/issues/4837 + // + // We make the assumption that the user has used the proper interface for working with `BoundedVec`s + // rather than directly manipulating the internal fields as this can result in an inconsistent internal state. + + (self.len == other.len) & (self.storage == other.storage) + } +} + +mod bounded_vec_tests { + // TODO: Allow imports from "super" + use crate::collections::bounded_vec::BoundedVec; + + #[test] + fn empty_equality() { + let mut bounded_vec1: BoundedVec = BoundedVec::new(); + let mut bounded_vec2: BoundedVec = BoundedVec::new(); + + assert_eq(bounded_vec1, bounded_vec2); + } + + #[test] + fn inequality() { + let mut bounded_vec1: BoundedVec = BoundedVec::new(); + let mut bounded_vec2: BoundedVec = BoundedVec::new(); + bounded_vec1.push(1); + bounded_vec2.push(2); + + assert(bounded_vec1 != bounded_vec2); + } +} diff --git a/noir/noir-repo/noir_stdlib/src/convert.nr b/noir/noir-repo/noir_stdlib/src/convert.nr index 00ac0a0fd8c..d3537df3c5e 100644 --- a/noir/noir-repo/noir_stdlib/src/convert.nr +++ b/noir/noir-repo/noir_stdlib/src/convert.nr @@ -12,12 +12,12 @@ impl From for T { // docs:start:into-trait trait Into { - fn into(input: Self) -> T; + fn into(self) -> T; } impl Into for U where T: From { - fn into(input: U) -> T { - T::from(input) + fn into(self) -> T { + T::from(self) } } // docs:end:into-trait diff --git a/noir/noir-repo/noir_stdlib/src/hash.nr b/noir/noir-repo/noir_stdlib/src/hash.nr index 1a61b5e084e..26a9fa6c2c0 100644 --- a/noir/noir-repo/noir_stdlib/src/hash.nr +++ b/noir/noir-repo/noir_stdlib/src/hash.nr @@ -1,7 +1,6 @@ mod poseidon; mod mimc; mod poseidon2; -mod pedersen; use crate::default::Default; use crate::uint128::U128; @@ -12,36 +11,18 @@ pub fn sha256(input: [u8; N]) -> [u8; 32] // docs:end:sha256 {} -#[foreign(sha256)] -// docs:start:sha256_slice -pub fn sha256_slice(input: [u8]) -> [u8; 32] -// docs:end:sha256_slice -{} - #[foreign(blake2s)] // docs:start:blake2s pub fn blake2s(input: [u8; N]) -> [u8; 32] // docs:end:blake2s {} -#[foreign(blake2s)] -// docs:start:blake2s_slice -pub fn blake2s_slice(input: [u8]) -> [u8; 32] -// docs:end:blake2s_slice -{} - #[foreign(blake3)] // docs:start:blake3 pub fn blake3(input: [u8; N]) -> [u8; 32] // docs:end:blake3 {} -#[foreign(blake3)] -// docs:start:blake3_slice -pub fn blake3_slice(input: [u8]) -> [u8; 32] -// docs:end:blake3_slice -{} - // docs:start:pedersen_commitment struct PedersenPoint { x : Field, @@ -53,28 +34,14 @@ pub fn pedersen_commitment(input: [Field; N]) -> PedersenPoint { pedersen_commitment_with_separator(input, 0) } -// docs:start:pedersen_commitment_slice -pub fn pedersen_commitment_slice(input: [Field]) -> PedersenPoint { - pedersen_commitment_with_separator_slice(input, 0) -} -// docs:end:pedersen_commitment_slice - #[foreign(pedersen_commitment)] pub fn __pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> [Field; 2] {} -#[foreign(pedersen_commitment)] -pub fn __pedersen_commitment_with_separator_slice(input: [Field], separator: u32) -> [Field; 2] {} - pub fn pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> PedersenPoint { let values = __pedersen_commitment_with_separator(input, separator); PedersenPoint { x: values[0], y: values[1] } } -pub fn pedersen_commitment_with_separator_slice(input: [Field], separator: u32) -> PedersenPoint { - let values = __pedersen_commitment_with_separator_slice(input, separator); - PedersenPoint { x: values[0], y: values[1] } -} - // docs:start:pedersen_hash pub fn pedersen_hash(input: [Field; N]) -> Field // docs:end:pedersen_hash @@ -82,31 +49,18 @@ pub fn pedersen_hash(input: [Field; N]) -> Field pedersen_hash_with_separator(input, 0) } -// docs:start:pedersen_hash_slice -pub fn pedersen_hash_slice(input: [Field]) -> Field -// docs:end:pedersen_hash_slice -{ - pedersen_hash_with_separator_slice(input, 0) -} - #[foreign(pedersen_hash)] pub fn pedersen_hash_with_separator(input: [Field; N], separator: u32) -> Field {} -#[foreign(pedersen_hash)] -pub fn pedersen_hash_with_separator_slice(input: [Field], separator: u32) -> Field {} - pub fn hash_to_field(inputs: [Field]) -> Field { - let mut inputs_as_bytes = &[]; + let mut sum = 0; for input in inputs { - let input_bytes = input.to_le_bytes(32); - for i in 0..32 { - inputs_as_bytes = inputs_as_bytes.push_back(input_bytes[i]); - } + let input_bytes: [u8; 32] = input.to_le_bytes(32).as_array(); + sum += crate::field::bytes32_to_field(blake2s(input_bytes)); } - let hashed_input = blake2s_slice(inputs_as_bytes); - crate::field::bytes32_to_field(hashed_input) + sum } #[foreign(keccak256)] @@ -115,12 +69,6 @@ pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] // docs:end:keccak256 {} -#[foreign(keccak256)] -// docs:start:keccak256_slice -pub fn keccak256_slice(input: [u8], message_size: u32) -> [u8; 32] -// docs:end:keccak256_slice -{} - #[foreign(poseidon2_permutation)] pub fn poseidon2_permutation(_input: [Field; N], _state_length: u32) -> [Field; N] {} @@ -140,7 +88,7 @@ trait Hash{ trait Hasher{ fn finish(self) -> Field; - fn write(&mut self, input: [Field]); + fn write(&mut self, input: Field); } // BuildHasher is a factory trait, responsible for production of specific Hasher. @@ -170,49 +118,49 @@ where impl Hash for Field { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, &[self]); + H::write(state, self); } } impl Hash for u8 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, &[self as Field]); + H::write(state, self as Field); } } impl Hash for u32 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, &[self as Field]); + H::write(state, self as Field); } } impl Hash for u64 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, &[self as Field]); + H::write(state, self as Field); } } impl Hash for i8 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, &[self as Field]); + H::write(state, self as Field); } } impl Hash for i32 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, &[self as Field]); + H::write(state, self as Field); } } impl Hash for i64 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, &[self as Field]); + H::write(state, self as Field); } } impl Hash for bool { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, &[self as Field]); + H::write(state, self as Field); } } @@ -222,7 +170,8 @@ impl Hash for () { impl Hash for U128 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, &[self.lo as Field, self.hi as Field]); + H::write(state, self.lo as Field); + H::write(state, self.hi as Field); } } diff --git a/noir/noir-repo/noir_stdlib/src/hash/mimc.nr b/noir/noir-repo/noir_stdlib/src/hash/mimc.nr index 1fb53701013..6c5502c2fbf 100644 --- a/noir/noir-repo/noir_stdlib/src/hash/mimc.nr +++ b/noir/noir-repo/noir_stdlib/src/hash/mimc.nr @@ -126,9 +126,8 @@ pub fn mimc_bn254(array: [Field; N]) -> Field { r } -struct MimcHasher{ +struct MimcHasher { _state: [Field], - _len: u64, } impl Hasher for MimcHasher { @@ -136,24 +135,22 @@ impl Hasher for MimcHasher { fn finish(self) -> Field { let exponent = 7; let mut r = 0; - for i in 0..self._len { + for i in 0..self._state.len() { let h = mimc(self._state[i], r, MIMC_BN254_CONSTANTS, exponent); r = r + self._state[i] + h; } r } - fn write(&mut self, input: [Field]){ - self._state = self._state.append(input); - self._len += input.len(); + fn write(&mut self, input: Field){ + self._state = self._state.push_back(input); } } impl Default for MimcHasher{ fn default() -> Self{ - MimcHasher{ + MimcHasher { _state: &[], - _len: 0, } } } diff --git a/noir/noir-repo/noir_stdlib/src/hash/pedersen.nr b/noir/noir-repo/noir_stdlib/src/hash/pedersen.nr deleted file mode 100644 index ad21e728945..00000000000 --- a/noir/noir-repo/noir_stdlib/src/hash/pedersen.nr +++ /dev/null @@ -1,24 +0,0 @@ -use crate::hash::{Hasher, pedersen_hash_slice}; -use crate::default::Default; - -struct PedersenHasher{ - _state: [Field] -} - -impl Hasher for PedersenHasher { - fn finish(self) -> Field { - pedersen_hash_slice(self._state) - } - - fn write(&mut self, input: [Field]){ - self._state = self._state.append(input); - } -} - -impl Default for PedersenHasher{ - fn default() -> Self{ - PedersenHasher{ - _state: &[] - } - } -} diff --git a/noir/noir-repo/noir_stdlib/src/hash/poseidon.nr b/noir/noir-repo/noir_stdlib/src/hash/poseidon.nr index 85a0802f630..742bfcaf804 100644 --- a/noir/noir-repo/noir_stdlib/src/hash/poseidon.nr +++ b/noir/noir-repo/noir_stdlib/src/hash/poseidon.nr @@ -105,66 +105,65 @@ fn apply_matrix(a: [Field; M], x: [Field; N]) -> [Field; N] { struct PoseidonHasher{ _state: [Field], - _len: u64, } impl Hasher for PoseidonHasher { #[field(bn254)] fn finish(self) -> Field { let mut result = 0; - assert(self._len < 16); - if self._len == 1 { + let len = self._state.len(); + assert(len < 16); + if len == 1 { result = bn254::hash_1([self._state[0]]); } - if self._len == 2 { + if len == 2 { result = bn254::hash_2([self._state[0],self._state[1]]); } - if self._len == 3 { + if len == 3 { result = bn254::hash_3([self._state[0],self._state[1],self._state[2]]); } - if self._len == 4 { + if len == 4 { result = bn254::hash_4([self._state[0],self._state[1],self._state[2],self._state[3]]); } - if self._len == 5 { + if len == 5 { result = bn254::hash_5([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4]]); } - if self._len == 6 { + if len == 6 { result = bn254::hash_6([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5]]); } - if self._len == 7 { + if len == 7 { result = bn254::hash_7([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6]]); } - if self._len == 8 { + if len == 8 { result = bn254::hash_8([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7]]); } - if self._len == 9 { + if len == 9 { result = bn254::hash_9([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8]]); } - if self._len == 10 { + if len == 10 { result = bn254::hash_10([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9]]); } - if self._len == 11 { + if len == 11 { result = bn254::hash_11([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10]]); } - if self._len == 12 { + if len == 12 { result = bn254::hash_12([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10], self._state[11]]); } - if self._len == 13 { + if len == 13 { result = bn254::hash_13([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10], self._state[11], self._state[12]]); } - if self._len == 14 { + if len == 14 { result = bn254::hash_14([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10], self._state[11], self._state[12], self._state[13]]); } - if self._len == 15 { + if len == 15 { result = bn254::hash_15([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10], self._state[11], self._state[12], self._state[13], self._state[14]]); } result } - fn write(&mut self, input: [Field]){ - self._state = self._state.append(input); - self._len += input.len(); + fn write(&mut self, input: Field){ + self._state = self._state.push_back(input); } } @@ -172,7 +171,6 @@ impl Default for PoseidonHasher{ fn default() -> Self{ PoseidonHasher{ _state: &[], - _len: 0, } } } diff --git a/noir/noir-repo/noir_stdlib/src/hash/poseidon2.nr b/noir/noir-repo/noir_stdlib/src/hash/poseidon2.nr index 12bf373e671..e5a82a596c6 100644 --- a/noir/noir-repo/noir_stdlib/src/hash/poseidon2.nr +++ b/noir/noir-repo/noir_stdlib/src/hash/poseidon2.nr @@ -117,30 +117,27 @@ impl Poseidon2 { struct Poseidon2Hasher{ _state: [Field], - _len: u64, } impl Hasher for Poseidon2Hasher { fn finish(self) -> Field { let iv : Field = (self._state.len() as Field)*18446744073709551616; // iv = (self._state.len() << 64) let mut sponge = Poseidon2::new(iv); - for i in 0..self._len { + for i in 0..self._state.len() { sponge.absorb(self._state[i]); } sponge.squeeze() } - fn write(&mut self, input: [Field]){ - self._state = self._state.append(input); - self._len += input.len(); + fn write(&mut self, input: Field){ + self._state = self._state.push_back(input); } } -impl Default for Poseidon2Hasher{ - fn default() -> Self{ - Poseidon2Hasher{ +impl Default for Poseidon2Hasher { + fn default() -> Self { + Poseidon2Hasher { _state: &[], - _len: 0, } } } diff --git a/noir/noir-repo/noir_stdlib/src/slice.nr b/noir/noir-repo/noir_stdlib/src/slice.nr index 164b4f96cf6..ac542a960ed 100644 --- a/noir/noir-repo/noir_stdlib/src/slice.nr +++ b/noir/noir-repo/noir_stdlib/src/slice.nr @@ -43,4 +43,14 @@ impl [T] { } self } + + pub fn as_array(self) -> [T; N] { + assert(self.len() == N); + + let mut array = [crate::unsafe::zeroed(); N]; + for i in 0..N { + array[i] = self[i]; + } + array + } } diff --git a/noir/noir-repo/rust-toolchain.toml b/noir/noir-repo/rust-toolchain.toml index 0e5ac891ce9..fe2949c8458 100644 --- a/noir/noir-repo/rust-toolchain.toml +++ b/noir/noir-repo/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.73.0" +channel = "1.74.1" components = [ "rust-src" ] targets = [ "wasm32-unknown-unknown", "wasm32-wasi", "aarch64-apple-darwin" ] profile = "default" diff --git a/noir/noir-repo/scripts/benchmark_start.sh b/noir/noir-repo/scripts/benchmark_start.sh new file mode 100755 index 00000000000..3e69b3d2c65 --- /dev/null +++ b/noir/noir-repo/scripts/benchmark_start.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid diff --git a/noir/noir-repo/scripts/benchmark_stop.sh b/noir/noir-repo/scripts/benchmark_stop.sh new file mode 100755 index 00000000000..964e5291817 --- /dev/null +++ b/noir/noir-repo/scripts/benchmark_stop.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +echo 4 | sudo tee /proc/sys/kernel/perf_event_paranoid diff --git a/noir/noir-repo/test_programs/execution_failure/hashmap_load_factor/src/main.nr b/noir/noir-repo/test_programs/execution_failure/hashmap_load_factor/src/main.nr index ade43f898e1..907c3628142 100644 --- a/noir/noir-repo/test_programs/execution_failure/hashmap_load_factor/src/main.nr +++ b/noir/noir-repo/test_programs/execution_failure/hashmap_load_factor/src/main.nr @@ -1,6 +1,6 @@ use dep::std::collections::map::HashMap; use dep::std::hash::BuildHasherDefault; -use dep::std::hash::pedersen::PedersenHasher; +use dep::std::hash::poseidon2::Poseidon2Hasher; struct Entry{ key: Field, @@ -10,7 +10,7 @@ struct Entry{ global HASHMAP_CAP = 8; global HASHMAP_LEN = 6; -fn allocate_hashmap() -> HashMap> { +fn allocate_hashmap() -> HashMap> { HashMap::default() } diff --git a/noir/noir-repo/test_programs/execution_success/eddsa/src/main.nr b/noir/noir-repo/test_programs/execution_success/eddsa/src/main.nr index fd1a95ee5fb..012c8466f2f 100644 --- a/noir/noir-repo/test_programs/execution_success/eddsa/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/eddsa/src/main.nr @@ -4,7 +4,6 @@ use dep::std::ec::tecurve::affine::Point as TEPoint; use dep::std::hash; use dep::std::eddsa::{eddsa_to_pub, eddsa_poseidon_verify, eddsa_verify_with_hasher}; use dep::std::hash::poseidon2::Poseidon2Hasher; -use dep::std::hash::pedersen::PedersenHasher; fn main(msg: pub Field, _priv_key_a: Field, _priv_key_b: Field) { // Skip this test for non-bn254 backends @@ -53,8 +52,5 @@ fn main(msg: pub Field, _priv_key_a: Field, _priv_key_b: Field) { // Using a different hash should fail let mut hasher = Poseidon2Hasher::default(); assert(!eddsa_verify_with_hasher(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg, &mut hasher)); - // Using a different hash should fail - let mut hasher = PedersenHasher::default(); - assert(!eddsa_verify_with_hasher(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg, &mut hasher)); } } diff --git a/noir/noir-repo/test_programs/execution_success/fold_numeric_generic_poseidon/Nargo.toml b/noir/noir-repo/test_programs/execution_success/fold_numeric_generic_poseidon/Nargo.toml new file mode 100644 index 00000000000..8c2bc79ea8d --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/fold_numeric_generic_poseidon/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "fold_numeric_generic_poseidon" +type = "bin" +authors = [""] +compiler_version = ">=0.27.0" + +[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/fold_numeric_generic_poseidon/Prover.toml b/noir/noir-repo/test_programs/execution_success/fold_numeric_generic_poseidon/Prover.toml new file mode 100644 index 00000000000..00e821cf89d --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/fold_numeric_generic_poseidon/Prover.toml @@ -0,0 +1,2 @@ +enable = [true, false] +to_hash = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] diff --git a/noir/noir-repo/test_programs/execution_success/fold_numeric_generic_poseidon/src/main.nr b/noir/noir-repo/test_programs/execution_success/fold_numeric_generic_poseidon/src/main.nr new file mode 100644 index 00000000000..f9f3e75789b --- /dev/null +++ b/noir/noir-repo/test_programs/execution_success/fold_numeric_generic_poseidon/src/main.nr @@ -0,0 +1,33 @@ +use dep::std::hash::{pedersen_hash_with_separator, poseidon2::Poseidon2}; + +global NUM_HASHES = 2; +global HASH_LENGTH = 10; + +#[fold] +pub fn poseidon_hash(inputs: [Field; N]) -> Field { + Poseidon2::hash(inputs, inputs.len()) +} + +fn main( + to_hash: [[Field; HASH_LENGTH]; NUM_HASHES], + enable: [bool; NUM_HASHES] +) -> pub [Field; NUM_HASHES + 1] { + let mut result = [0; NUM_HASHES + 1]; + for i in 0..NUM_HASHES { + let enable = enable[i]; + let to_hash = to_hash[i]; + if enable { + result[i] = poseidon_hash(to_hash); + } + } + + // We want to make sure that the foldable function with a numeric generic + // is monomorphized correctly. + let mut double_preimage = [0; 20]; + for i in 0..HASH_LENGTH * 2 { + double_preimage[i] = to_hash[0][i % HASH_LENGTH]; + } + result[NUM_HASHES] = poseidon_hash(double_preimage); + + result +} diff --git a/noir/noir-repo/test_programs/execution_success/hashmap/src/main.nr b/noir/noir-repo/test_programs/execution_success/hashmap/src/main.nr index 4d2cbd45993..76daa594a89 100644 --- a/noir/noir-repo/test_programs/execution_success/hashmap/src/main.nr +++ b/noir/noir-repo/test_programs/execution_success/hashmap/src/main.nr @@ -2,7 +2,7 @@ mod utils; use dep::std::collections::map::HashMap; use dep::std::hash::BuildHasherDefault; -use dep::std::hash::pedersen::PedersenHasher; +use dep::std::hash::poseidon2::Poseidon2Hasher; use dep::std::cmp::Eq; use utils::cut; @@ -25,7 +25,7 @@ global K_CMP = FIELD_CMP; global V_CMP = FIELD_CMP; global KV_CMP = |a: (K, V), b: (K, V)| a.0.lt(b.0); -global ALLOCATE_HASHMAP = || -> HashMap> +global ALLOCATE_HASHMAP = || -> HashMap> HashMap::default(); fn main(input: [Entry; HASHMAP_LEN]) { @@ -194,24 +194,24 @@ fn test_mut_iterators() { } // docs:start:type_alias -type MyMap = HashMap>; +type MyMap = HashMap>; // docs:end:type_alias /// Tests examples from the stdlib hashmap documentation fn doc_tests() { // docs:start:default_example - let hashmap: HashMap> = HashMap::default(); + let hashmap: HashMap> = HashMap::default(); assert(hashmap.is_empty()); // docs:end:default_example // docs:start:with_hasher_example - let my_hasher: BuildHasherDefault = Default::default(); - let hashmap: HashMap> = HashMap::with_hasher(my_hasher); + let my_hasher: BuildHasherDefault = Default::default(); + let hashmap: HashMap> = HashMap::with_hasher(my_hasher); assert(hashmap.is_empty()); // docs:end:with_hasher_example // docs:start:insert_example - let mut map: HashMap> = HashMap::default(); + let mut map: HashMap> = HashMap::default(); map.insert(12, 42); assert(map.len() == 1); // docs:end:insert_example @@ -255,7 +255,7 @@ fn doc_tests() { // docs:end:len_example // docs:start:capacity_example - let empty_map: HashMap> = HashMap::default(); + let empty_map: HashMap> = HashMap::default(); assert(empty_map.len() == 0); assert(empty_map.capacity() == 42); // docs:end:capacity_example @@ -283,8 +283,8 @@ fn doc_tests() { // docs:end:retain_example // docs:start:eq_example - let mut map1: HashMap> = HashMap::default(); - let mut map2: HashMap> = HashMap::default(); + let mut map1: HashMap> = HashMap::default(); + let mut map2: HashMap> = HashMap::default(); map1.insert(1, 2); map1.insert(3, 4); @@ -297,7 +297,7 @@ fn doc_tests() { } // docs:start:get_example -fn get_example(map: HashMap>) { +fn get_example(map: HashMap>) { let x = map.get(12); if x.is_some() { @@ -306,7 +306,7 @@ fn get_example(map: HashMap> } // docs:end:get_example -fn entries_examples(map: HashMap>) { +fn entries_examples(map: HashMap>) { // docs:start:entries_example let entries = map.entries(); @@ -344,7 +344,7 @@ fn entries_examples(map: HashMap>) { +fn iter_examples(mut map: HashMap>) { // docs:start:iter_mut_example // 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)); diff --git a/noir/noir-repo/test_programs/execution_success/regression_sha256_slice/Nargo.toml b/noir/noir-repo/test_programs/execution_success/regression_sha256_slice/Nargo.toml deleted file mode 100644 index 759c3b20ba8..00000000000 --- a/noir/noir-repo/test_programs/execution_success/regression_sha256_slice/Nargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "regression_sha256_slice" -type = "bin" -authors = [""] -compiler_version = ">=0.26.0" - -[dependencies] \ No newline at end of file diff --git a/noir/noir-repo/test_programs/execution_success/regression_sha256_slice/Prover.toml b/noir/noir-repo/test_programs/execution_success/regression_sha256_slice/Prover.toml deleted file mode 100644 index 8a027e9eca9..00000000000 --- a/noir/noir-repo/test_programs/execution_success/regression_sha256_slice/Prover.toml +++ /dev/null @@ -1 +0,0 @@ -x = ["5", "10"] diff --git a/noir/noir-repo/test_programs/execution_success/regression_sha256_slice/src/main.nr b/noir/noir-repo/test_programs/execution_success/regression_sha256_slice/src/main.nr deleted file mode 100644 index 60b0911cf09..00000000000 --- a/noir/noir-repo/test_programs/execution_success/regression_sha256_slice/src/main.nr +++ /dev/null @@ -1,12 +0,0 @@ -use dep::std; - -fn main(x: [u8; 2]) { - let mut y = x.as_slice(); - let digest1 = std::hash::sha256_slice(y); - let mut v = y; - if x[0] != 0 { - v = y.push_back(x[0]); - } - let digest2 = std::hash::sha256_slice(v); - assert(digest1 != digest2); -} diff --git a/noir/noir-repo/tooling/backend_interface/Cargo.toml b/noir/noir-repo/tooling/backend_interface/Cargo.toml index 2d991f9ae6c..b731c138c7d 100644 --- a/noir/noir-repo/tooling/backend_interface/Cargo.toml +++ b/noir/noir-repo/tooling/backend_interface/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "backend-interface" description = "The definition of the backend CLI interface which Nargo uses for proving/verifying ACIR circuits." -version = "0.11.0" +version.workspace = true authors.workspace = true edition.workspace = true rust-version.workspace = true diff --git a/noir/noir-repo/tooling/backend_interface/src/cli/info.rs b/noir/noir-repo/tooling/backend_interface/src/cli/info.rs index 8ca3d4dd0a3..6e6603ce53e 100644 --- a/noir/noir-repo/tooling/backend_interface/src/cli/info.rs +++ b/noir/noir-repo/tooling/backend_interface/src/cli/info.rs @@ -56,7 +56,7 @@ fn info_command() -> Result<(), BackendError> { let expression_width = InfoCommand { crs_path }.run(backend.binary_path())?; - assert!(matches!(expression_width, ExpressionWidth::Bounded { width: 3 })); + assert!(matches!(expression_width, ExpressionWidth::Bounded { width: 4 })); Ok(()) } diff --git a/noir/noir-repo/tooling/bb_abstraction_leaks/build.rs b/noir/noir-repo/tooling/bb_abstraction_leaks/build.rs index 0f9770c805d..b3dfff9e94c 100644 --- a/noir/noir-repo/tooling/bb_abstraction_leaks/build.rs +++ b/noir/noir-repo/tooling/bb_abstraction_leaks/build.rs @@ -10,7 +10,7 @@ use const_format::formatcp; const USERNAME: &str = "AztecProtocol"; const REPO: &str = "aztec-packages"; -const VERSION: &str = "0.34.0"; +const VERSION: &str = "0.35.1"; const TAG: &str = formatcp!("aztec-packages-v{}", VERSION); const API_URL: &str = diff --git a/noir/noir-repo/tooling/debugger/Cargo.toml b/noir/noir-repo/tooling/debugger/Cargo.toml index 30d11db8cf3..a3bf12f5368 100644 --- a/noir/noir-repo/tooling/debugger/Cargo.toml +++ b/noir/noir-repo/tooling/debugger/Cargo.toml @@ -4,6 +4,7 @@ description = "Debugger for Noir" version.workspace = true authors.workspace = true edition.workspace = true +rust-version.workspace = true license.workspace = true [build-dependencies] diff --git a/noir/noir-repo/tooling/debugger/ignored-tests.txt b/noir/noir-repo/tooling/debugger/ignored-tests.txt index 4507aeb8545..3b63f8d5542 100644 --- a/noir/noir-repo/tooling/debugger/ignored-tests.txt +++ b/noir/noir-repo/tooling/debugger/ignored-tests.txt @@ -15,3 +15,6 @@ to_bytes_integration fold_basic fold_basic_nested_call fold_call_witness_condition +fold_after_inlined_calls +fold_numeric_generic_poseidon + diff --git a/noir/noir-repo/tooling/lsp/Cargo.toml b/noir/noir-repo/tooling/lsp/Cargo.toml index 750e85694e2..a599b096e52 100644 --- a/noir/noir-repo/tooling/lsp/Cargo.toml +++ b/noir/noir-repo/tooling/lsp/Cargo.toml @@ -3,7 +3,8 @@ name = "noir_lsp" description = "Language server for Noir" version.workspace = true authors.workspace = true -edition.workspace = true +edition.workspace = true# +rust-version.workspace = true license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/noir/noir-repo/tooling/nargo/Cargo.toml b/noir/noir-repo/tooling/nargo/Cargo.toml index efd38a182e0..72b412ac5b9 100644 --- a/noir/noir-repo/tooling/nargo/Cargo.toml +++ b/noir/noir-repo/tooling/nargo/Cargo.toml @@ -4,13 +4,11 @@ description = "Noir's package manager" version.workspace = true authors.workspace = true edition.workspace = true +rust-version.workspace = true license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[build-dependencies] -rustc_version = "0.4.0" - [dependencies] acvm.workspace = true fm.workspace = true diff --git a/noir/noir-repo/tooling/nargo/build.rs b/noir/noir-repo/tooling/nargo/build.rs deleted file mode 100644 index ab2b7579132..00000000000 --- a/noir/noir-repo/tooling/nargo/build.rs +++ /dev/null @@ -1,12 +0,0 @@ -use rustc_version::{version, Version}; - -fn check_rustc_version() { - assert!( - version().unwrap() >= Version::parse("1.73.0").unwrap(), - "The minimal supported rustc version is 1.73.0." - ); -} - -fn main() { - check_rustc_version(); -} diff --git a/noir/noir-repo/tooling/nargo_cli/Cargo.toml b/noir/noir-repo/tooling/nargo_cli/Cargo.toml index 1629ae86edb..c20be037e62 100644 --- a/noir/noir-repo/tooling/nargo_cli/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_cli/Cargo.toml @@ -5,6 +5,7 @@ default-run = "nargo" version.workspace = true authors.workspace = true edition.workspace = true +rust-version.workspace = true license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -15,7 +16,6 @@ name = "nargo" path = "src/main.rs" [build-dependencies] -rustc_version = "0.4.0" build-data.workspace = true toml.workspace = true @@ -72,13 +72,9 @@ assert_cmd = "2.0.8" assert_fs = "1.0.10" predicates = "2.1.5" fm.workspace = true -criterion = "0.5.0" +criterion.workspace = true +pprof.workspace = true paste = "1.0.14" -pprof = { version = "0.12", features = [ - "flamegraph", - "frame-pointer", - "criterion", -] } iai = "0.1.1" test-binary = "3.0.2" diff --git a/noir/noir-repo/tooling/nargo_cli/build.rs b/noir/noir-repo/tooling/nargo_cli/build.rs index bf97dfb3e96..0ed2d4c07f7 100644 --- a/noir/noir-repo/tooling/nargo_cli/build.rs +++ b/noir/noir-repo/tooling/nargo_cli/build.rs @@ -1,21 +1,11 @@ -use rustc_version::{version, Version}; use std::fs::File; use std::io::Write; use std::path::{Path, PathBuf}; use std::{env, fs}; -fn check_rustc_version() { - assert!( - version().unwrap() >= Version::parse("1.73.0").unwrap(), - "The minimal supported rustc version is 1.73.0." - ); -} - const GIT_COMMIT: &&str = &"GIT_COMMIT"; fn main() { - check_rustc_version(); - // Only use build_data if the environment variable isn't set. if std::env::var(GIT_COMMIT).is_err() { build_data::set_GIT_COMMIT(); diff --git a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml index 374413ac9f2..05b2fdb7d52 100644 --- a/noir/noir-repo/tooling/nargo_fmt/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_fmt/Cargo.toml @@ -3,6 +3,7 @@ name = "nargo_fmt" version.workspace = true authors.workspace = true edition.workspace = true +rust-version.workspace = true license.workspace = true [dependencies] diff --git a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/array.rs b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/array.rs index db7dc4701b7..011e775a018 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/array.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/array.rs @@ -1,4 +1,4 @@ -use noirc_frontend::{hir::resolution::errors::Span, token::Token, Expression}; +use noirc_frontend::{ast::Expression, hir::resolution::errors::Span, token::Token}; use crate::{ items::Item, diff --git a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs index 6cf69a2309d..e4616c99aaa 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/expr.rs @@ -1,7 +1,7 @@ -use noirc_frontend::{ - macros_api::Span, token::Token, ArrayLiteral, BlockExpression, Expression, ExpressionKind, - Literal, UnaryOp, +use noirc_frontend::ast::{ + ArrayLiteral, BlockExpression, Expression, ExpressionKind, Literal, UnaryOp, }; +use noirc_frontend::{macros_api::Span, token::Token}; use crate::visitor::{ expr::{format_brackets, format_parens, NewlineMode}, diff --git a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/imports.rs b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/imports.rs index 55eb259bcdd..564ef3fa370 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/imports.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/imports.rs @@ -1,4 +1,4 @@ -use noirc_frontend::{PathKind, UseTreeKind}; +use noirc_frontend::ast; use crate::{ items::Item, @@ -60,13 +60,13 @@ pub(crate) struct UseTree { } impl UseTree { - pub(crate) fn from_ast(use_tree: noirc_frontend::UseTree) -> Self { + pub(crate) fn from_ast(use_tree: ast::UseTree) -> Self { let mut result = UseTree { path: vec![] }; match use_tree.prefix.kind { - PathKind::Crate => result.path.push(UseSegment::Crate), - PathKind::Dep => result.path.push(UseSegment::Dep), - PathKind::Plain => {} + ast::PathKind::Crate => result.path.push(UseSegment::Crate), + ast::PathKind::Dep => result.path.push(UseSegment::Dep), + ast::PathKind::Plain => {} }; result.path.extend( @@ -78,13 +78,13 @@ impl UseTree { ); match use_tree.kind { - UseTreeKind::Path(name, alias) => { + ast::UseTreeKind::Path(name, alias) => { result.path.push(UseSegment::Ident( name.to_string(), alias.map(|rename| rename.to_string()), )); } - UseTreeKind::List(list) => { + ast::UseTreeKind::List(list) => { let segment = UseSegment::List(list.into_iter().map(UseTree::from_ast).collect()); result.path.push(segment); } diff --git a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/infix.rs b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/infix.rs index 5d2b387496a..e2555f21187 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/infix.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/infix.rs @@ -1,6 +1,6 @@ use std::iter::zip; -use noirc_frontend::{Expression, ExpressionKind}; +use noirc_frontend::ast::{Expression, ExpressionKind}; use crate::{ rewrite, diff --git a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/parenthesized.rs b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/parenthesized.rs index 3926b52cb73..93e1538b042 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/parenthesized.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/parenthesized.rs @@ -1,4 +1,5 @@ -use noirc_frontend::{hir::resolution::errors::Span, Expression, ExpressionKind}; +use noirc_frontend::ast::{Expression, ExpressionKind}; +use noirc_frontend::hir::resolution::errors::Span; use crate::visitor::{FmtVisitor, Shape}; diff --git a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/typ.rs b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/typ.rs index 980d02ee5dc..278457f82d1 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/rewrite/typ.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/rewrite/typ.rs @@ -1,4 +1,4 @@ -use noirc_frontend::{UnresolvedType, UnresolvedTypeData}; +use noirc_frontend::ast::{UnresolvedType, UnresolvedTypeData}; use crate::{ utils::span_is_empty, diff --git a/noir/noir-repo/tooling/nargo_fmt/src/utils.rs b/noir/noir-repo/tooling/nargo_fmt/src/utils.rs index 94969d45e81..2c5c3085e66 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/utils.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/utils.rs @@ -3,10 +3,10 @@ use std::borrow::Cow; use crate::items::HasItem; use crate::rewrite; use crate::visitor::{FmtVisitor, Shape}; +use noirc_frontend::ast::{Expression, Ident, Param, Visibility}; use noirc_frontend::hir::resolution::errors::Span; use noirc_frontend::lexer::Lexer; use noirc_frontend::token::Token; -use noirc_frontend::{Expression, Ident, Param, Visibility}; pub(crate) fn changed_comment_content(original: &str, new: &str) -> bool { comments(original).ne(comments(new)) diff --git a/noir/noir-repo/tooling/nargo_fmt/src/visitor/expr.rs b/noir/noir-repo/tooling/nargo_fmt/src/visitor/expr.rs index f9836adda18..18b962a7f85 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/visitor/expr.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/visitor/expr.rs @@ -1,7 +1,8 @@ -use noirc_frontend::{ - hir::resolution::errors::Span, lexer::Lexer, token::Token, BlockExpression, - ConstructorExpression, Expression, ExpressionKind, IfExpression, Statement, StatementKind, +use noirc_frontend::ast::Expression; +use noirc_frontend::ast::{ + BlockExpression, ConstructorExpression, ExpressionKind, IfExpression, Statement, StatementKind, }; +use noirc_frontend::{hir::resolution::errors::Span, lexer::Lexer, token::Token}; use super::{ExpressionType, FmtVisitor, Shape}; use crate::{ diff --git a/noir/noir-repo/tooling/nargo_fmt/src/visitor/item.rs b/noir/noir-repo/tooling/nargo_fmt/src/visitor/item.rs index 28aad3c551f..82cfefba632 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/visitor/item.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/visitor/item.rs @@ -1,10 +1,3 @@ -use noirc_frontend::{ - hir::resolution::errors::Span, - parser::{Item, ItemKind}, - token::{Keyword, Token}, - Distinctness, NoirFunction, ParsedModule, Visibility, -}; - use crate::{ rewrite::{self, UseTree}, utils::{ @@ -13,6 +6,13 @@ use crate::{ }, visitor::expr::{format_seq, NewlineMode}, }; +use noirc_frontend::ast::{Distinctness, NoirFunction, Visibility}; +use noirc_frontend::{ + hir::resolution::errors::Span, + parser::{Item, ItemKind}, + token::{Keyword, Token}, + ParsedModule, +}; use super::{ expr::Tactic::{HorizontalVertical, LimitedHorizontalVertical}, diff --git a/noir/noir-repo/tooling/nargo_fmt/src/visitor/stmt.rs b/noir/noir-repo/tooling/nargo_fmt/src/visitor/stmt.rs index ee8cc990e0e..e41827c94a1 100644 --- a/noir/noir-repo/tooling/nargo_fmt/src/visitor/stmt.rs +++ b/noir/noir-repo/tooling/nargo_fmt/src/visitor/stmt.rs @@ -1,6 +1,8 @@ use std::iter::zip; -use noirc_frontend::{ +use noirc_frontend::macros_api::Span; + +use noirc_frontend::ast::{ ConstrainKind, ConstrainStatement, ExpressionKind, ForRange, Statement, StatementKind, }; @@ -14,92 +16,94 @@ impl super::FmtVisitor<'_> { for (Statement { kind, span }, index) in zip(stmts, 1..) { let is_last = index == len; + self.visit_stmt(kind, span, is_last); + self.last_position = span.end(); + } + } - match kind { - StatementKind::Expression(expr) => self.visit_expr( - expr, - if is_last { ExpressionType::SubExpression } else { ExpressionType::Statement }, - ), - StatementKind::Semi(expr) => { - self.visit_expr(expr, ExpressionType::Statement); - self.push_str(";"); - } - StatementKind::Let(let_stmt) => { - let let_str = - self.slice(span.start()..let_stmt.expression.span.start()).trim_end(); - - let expr_str = rewrite::sub_expr(self, self.shape(), let_stmt.expression); - - self.push_rewrite(format!("{let_str} {expr_str};"), span); - } - StatementKind::Constrain(ConstrainStatement(expr, message, kind)) => { - let mut nested_shape = self.shape(); - let shape = nested_shape; - - nested_shape.indent.block_indent(self.config); - - let message = message.map_or(String::new(), |message| { - let message = rewrite::sub_expr(self, nested_shape, message); - format!(", {message}") - }); - - let (callee, args) = match kind { - ConstrainKind::Assert | ConstrainKind::Constrain => { - let assertion = rewrite::sub_expr(self, nested_shape, expr); - let args = format!("{assertion}{message}"); - - ("assert", args) - } - ConstrainKind::AssertEq => { - if let ExpressionKind::Infix(infix) = expr.kind { - let lhs = rewrite::sub_expr(self, nested_shape, infix.lhs); - let rhs = rewrite::sub_expr(self, nested_shape, infix.rhs); + fn visit_stmt(&mut self, kind: StatementKind, span: Span, is_last: bool) { + match kind { + StatementKind::Expression(expr) => self.visit_expr( + expr, + if is_last { ExpressionType::SubExpression } else { ExpressionType::Statement }, + ), + StatementKind::Semi(expr) => { + self.visit_expr(expr, ExpressionType::Statement); + self.push_str(";"); + } + StatementKind::Let(let_stmt) => { + let let_str = self.slice(span.start()..let_stmt.expression.span.start()).trim_end(); - let args = format!("{lhs}, {rhs}{message}"); + let expr_str = rewrite::sub_expr(self, self.shape(), let_stmt.expression); - ("assert_eq", args) - } else { - unreachable!() - } + self.push_rewrite(format!("{let_str} {expr_str};"), span); + } + StatementKind::Constrain(ConstrainStatement(expr, message, kind)) => { + let mut nested_shape = self.shape(); + let shape = nested_shape; + + nested_shape.indent.block_indent(self.config); + + let message = message.map_or(String::new(), |message| { + let message = rewrite::sub_expr(self, nested_shape, message); + format!(", {message}") + }); + + let (callee, args) = match kind { + ConstrainKind::Assert | ConstrainKind::Constrain => { + let assertion = rewrite::sub_expr(self, nested_shape, expr); + let args = format!("{assertion}{message}"); + + ("assert", args) + } + ConstrainKind::AssertEq => { + if let ExpressionKind::Infix(infix) = expr.kind { + let lhs = rewrite::sub_expr(self, nested_shape, infix.lhs); + let rhs = rewrite::sub_expr(self, nested_shape, infix.rhs); + + let args = format!("{lhs}, {rhs}{message}"); + + ("assert_eq", args) + } else { + unreachable!() } - }; - - let args = wrap_exprs( - "(", - ")", - args, - nested_shape, - shape, - NewlineMode::IfContainsNewLineAndWidth, - ); - let constrain = format!("{callee}{args};"); - - self.push_rewrite(constrain, span); - } - StatementKind::For(for_stmt) => { - let identifier = self.slice(for_stmt.identifier.span()); - let range = match for_stmt.range { - ForRange::Range(start, end) => format!( - "{}..{}", - rewrite::sub_expr(self, self.shape(), start), - rewrite::sub_expr(self, self.shape(), end) - ), - ForRange::Array(array) => rewrite::sub_expr(self, self.shape(), array), - }; - let block = rewrite::sub_expr(self, self.shape(), for_stmt.block); - - let result = format!("for {identifier} in {range} {block}"); - self.push_rewrite(result, span); - } - StatementKind::Assign(_) => { - self.push_rewrite(self.slice(span).to_string(), span); - } - StatementKind::Error => unreachable!(), - StatementKind::Break => self.push_rewrite("break;".into(), span), - StatementKind::Continue => self.push_rewrite("continue;".into(), span), + } + }; + + let args = wrap_exprs( + "(", + ")", + args, + nested_shape, + shape, + NewlineMode::IfContainsNewLineAndWidth, + ); + let constrain = format!("{callee}{args};"); + + self.push_rewrite(constrain, span); } - - self.last_position = span.end(); + StatementKind::For(for_stmt) => { + let identifier = self.slice(for_stmt.identifier.span()); + let range = match for_stmt.range { + ForRange::Range(start, end) => format!( + "{}..{}", + rewrite::sub_expr(self, self.shape(), start), + rewrite::sub_expr(self, self.shape(), end) + ), + ForRange::Array(array) => rewrite::sub_expr(self, self.shape(), array), + }; + let block = rewrite::sub_expr(self, self.shape(), for_stmt.block); + + let result = format!("for {identifier} in {range} {block}"); + self.push_rewrite(result, span); + } + StatementKind::Assign(_) => { + self.push_rewrite(self.slice(span).to_string(), span); + } + StatementKind::Error => unreachable!(), + StatementKind::Break => self.push_rewrite("break;".into(), span), + StatementKind::Continue => self.push_rewrite("continue;".into(), span), + StatementKind::Comptime(statement) => self.visit_stmt(*statement, span, is_last), } } } diff --git a/noir/noir-repo/tooling/nargo_toml/Cargo.toml b/noir/noir-repo/tooling/nargo_toml/Cargo.toml index c835ddd936c..574972d99e7 100644 --- a/noir/noir-repo/tooling/nargo_toml/Cargo.toml +++ b/noir/noir-repo/tooling/nargo_toml/Cargo.toml @@ -4,6 +4,7 @@ description = "Utilities for working with Nargo.toml files" version.workspace = true authors.workspace = true edition.workspace = true +rust-version.workspace = true license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json index 438e91ff302..af9e47a8e63 100644 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json +++ b/noir/noir-repo/tooling/noir_js_backend_barretenberg/package.json @@ -42,7 +42,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "portal:../../../../barretenberg/ts", + "@aztec/bb.js": "0.35.1", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/noir/noir-repo/tooling/noirc_abi/Cargo.toml b/noir/noir-repo/tooling/noirc_abi/Cargo.toml index b7fe1ef8084..3258ea04c40 100644 --- a/noir/noir-repo/tooling/noirc_abi/Cargo.toml +++ b/noir/noir-repo/tooling/noirc_abi/Cargo.toml @@ -3,6 +3,7 @@ name = "noirc_abi" version.workspace = true authors.workspace = true edition.workspace = true +rust-version.workspace = true license.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/noir/noir-repo/tooling/noirc_abi/src/lib.rs b/noir/noir-repo/tooling/noirc_abi/src/lib.rs index 6ad13500bdd..8ff0154d32c 100644 --- a/noir/noir-repo/tooling/noirc_abi/src/lib.rs +++ b/noir/noir-repo/tooling/noirc_abi/src/lib.rs @@ -10,7 +10,8 @@ use acvm::{ use errors::AbiError; use input_parser::InputValue; use iter_extended::{try_btree_map, try_vecmap, vecmap}; -use noirc_frontend::{hir::Context, Signedness, Type, TypeBinding, TypeVariableKind, Visibility}; +use noirc_frontend::ast::{Signedness, Visibility}; +use noirc_frontend::{hir::Context, Type, TypeBinding, TypeVariableKind}; use serde::{Deserialize, Serialize}; use std::ops::Range; use std::{collections::BTreeMap, str}; diff --git a/noir/noir-repo/yarn.lock b/noir/noir-repo/yarn.lock index b45678f5d8b..e9915882fac 100644 --- a/noir/noir-repo/yarn.lock +++ b/noir/noir-repo/yarn.lock @@ -221,18 +221,19 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@portal:../../../../barretenberg/ts::locator=%40noir-lang%2Fbackend_barretenberg%40workspace%3Atooling%2Fnoir_js_backend_barretenberg": - version: 0.0.0-use.local - resolution: "@aztec/bb.js@portal:../../../../barretenberg/ts::locator=%40noir-lang%2Fbackend_barretenberg%40workspace%3Atooling%2Fnoir_js_backend_barretenberg" +"@aztec/bb.js@npm:0.35.1": + version: 0.35.1 + resolution: "@aztec/bb.js@npm:0.35.1" dependencies: comlink: ^4.4.1 commander: ^10.0.1 debug: ^4.3.4 tslib: ^2.4.0 bin: - bb.js: ./dest/node/main.js + bb.js: dest/node/main.js + checksum: 8e3551f059523d9494af4721a9219e2c6e63c8ed1df447a2d0daa9f8526a794758ae708bd1d9c9b1fbfb89c56dc867d9f0b87250dbabfcde23ec02dabbb5a32a languageName: node - linkType: soft + linkType: hard "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.11, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5, @babel/code-frame@npm:^7.8.3": version: 7.23.5 @@ -4395,7 +4396,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": "portal:../../../../barretenberg/ts" + "@aztec/bb.js": 0.35.1 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3