From a26419f00f5f082a9ed856346addf6276fbdb4d7 Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Tue, 23 Jul 2024 17:11:58 -0400 Subject: [PATCH] feat: Sync from noir (#7583) Automated pull of development from the [noir](https://github.com/noir-lang/noir) programming language, a dependency of Aztec. BEGIN_COMMIT_OVERRIDE chore: Add a regression test for bit not on untyped integer (https://github.com/noir-lang/noir/pull/5589) feat: Implement `poseidon2_permutation` in comptime interpreter (https://github.com/noir-lang/noir/pull/5590) fix: 'cannot eval non-comptime global' error (https://github.com/noir-lang/noir/pull/5586) fix(frontend)!: Restrict numeric generic types to unsigned ints up to `u32` (https://github.com/noir-lang/noir/pull/5581) feat: Implement `zeroed` in the interpreter (https://github.com/noir-lang/noir/pull/5540) fix: correctly track sources for open LSP documents (https://github.com/noir-lang/noir/pull/5561) fix: error on trait impl generics count mismatch (https://github.com/noir-lang/noir/pull/5582) fix: allow calling a trait method with paths that don't consist of exactly two segments (https://github.com/noir-lang/noir/pull/5577) fix: Run macros within comptime contexts (https://github.com/noir-lang/noir/pull/5576) chore: Remove comptime scanning pass (https://github.com/noir-lang/noir/pull/5569) fix(frontend): Disallow signed numeric generics (https://github.com/noir-lang/noir/pull/5572) chore: disable aztec-packages CI checks (https://github.com/noir-lang/noir/pull/5566) feat: LSP inlay parameter hints (https://github.com/noir-lang/noir/pull/5553) feat: Add `TraitDefinition::as_trait_constraint()` (https://github.com/noir-lang/noir/pull/5541) fix: Fix `uhashmap` test name (https://github.com/noir-lang/noir/pull/5563) fix: let unary traits work at comptime (https://github.com/noir-lang/noir/pull/5507) feat: Add a compile-time hash map type (https://github.com/noir-lang/noir/pull/5543) chore(docs): Docs for turbofish operator (https://github.com/noir-lang/noir/pull/5555) fix(frontend): Error for when impl is stricter than trait (https://github.com/noir-lang/noir/pull/5343) chore: filter warnings from elaborator in Aztec Macros (https://github.com/noir-lang/noir/pull/5556) fix: Don't panic when a macro fails to resolve (https://github.com/noir-lang/noir/pull/5537) fix(ssa): More robust array deduplication check (https://github.com/noir-lang/noir/pull/5547) fix: Fix occurs check (https://github.com/noir-lang/noir/pull/5535) fix: type_of for pointer types (https://github.com/noir-lang/noir/pull/5536) chore: Release Noir(0.32.0) (https://github.com/noir-lang/noir/pull/5268) fix: revert PR #5449 (https://github.com/noir-lang/noir/pull/5548) fix: never panic in LSP inlay hints (https://github.com/noir-lang/noir/pull/5534) feat: LSP document symbol (https://github.com/noir-lang/noir/pull/5532) feat: add comptime support for `modulus_*` compiler builtins (https://github.com/noir-lang/noir/pull/5530) chore: Remove unknown annotation warning (https://github.com/noir-lang/noir/pull/5531) feat: Add TraitConstraint type (https://github.com/noir-lang/noir/pull/5499) fix: Error on empty function bodies (https://github.com/noir-lang/noir/pull/5519) feat: LSP inlay hints for let and global (https://github.com/noir-lang/noir/pull/5510) chore: Remove dbg on find_func_with_name (https://github.com/noir-lang/noir/pull/5526) chore: Remove the remainder of legacy code (https://github.com/noir-lang/noir/pull/5525) chore: Remove `--use-legacy` and resolution code (https://github.com/noir-lang/noir/pull/5248) chore: switch to Noir Keccak implementation with variable size support (https://github.com/noir-lang/noir/pull/5508) chore: standardize experimental feature disclaimer across documentation (https://github.com/noir-lang/noir/pull/5367) END_COMMIT_OVERRIDE --------- Co-authored-by: Maxim Vezenov --- .noir-sync-commit | 2 +- avm-transpiler/Cargo.lock | 285 +++++++++--------- noir/noir-repo/Cargo.lock | 1 + .../aztec_macros/src/utils/hir_utils.rs | 1 + .../compiler/noirc_frontend/Cargo.toml | 1 + .../compiler/noirc_frontend/src/ast/mod.rs | 8 +- .../noirc_frontend/src/elaborator/mod.rs | 24 +- .../noirc_frontend/src/elaborator/traits.rs | 29 +- .../noirc_frontend/src/hir/comptime/errors.rs | 19 +- .../src/hir/comptime/interpreter.rs | 21 +- .../src/hir/comptime/interpreter/builtin.rs | 118 +++++++- .../src/hir/comptime/interpreter/foreign.rs | 48 +++ .../noirc_frontend/src/hir/comptime/value.rs | 5 + .../src/hir/def_collector/dc_mod.rs | 7 +- .../noirc_frontend/src/node_interner.rs | 8 +- .../noirc_frontend/src/parser/errors.rs | 10 +- .../src/parser/parser/function.rs | 18 +- .../noirc_frontend/src/parser/parser/types.rs | 29 +- .../compiler/noirc_frontend/src/tests.rs | 45 ++- noir/noir-repo/tooling/lsp/src/lib.rs | 18 +- .../tooling/lsp/src/notifications/mod.rs | 267 ++++++++++------ .../noir-repo/tooling/lsp/src/requests/mod.rs | 8 +- .../tooling/lsp/src/requests/profile_run.rs | 9 +- .../tooling/lsp/src/requests/references.rs | 11 +- .../tooling/lsp/src/requests/test_run.rs | 12 +- .../tooling/lsp/src/requests/tests.rs | 8 +- 26 files changed, 710 insertions(+), 302 deletions(-) create mode 100644 noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs diff --git a/.noir-sync-commit b/.noir-sync-commit index af17e410445..b2216c735d4 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -951e821a585fe7e0697291cadd4d3c3aa49fd8e4 +453ed590ae3ae6ee8a8d3113419fc51b825b2538 diff --git a/avm-transpiler/Cargo.lock b/avm-transpiler/Cargo.lock index 6e168d79348..cd2c0ca1f2d 100644 --- a/avm-transpiler/Cargo.lock +++ b/avm-transpiler/Cargo.lock @@ -78,9 +78,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "once_cell", @@ -90,9 +90,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -114,47 +114,48 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.11" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2faccea4cc4ab4a667ce676a30e8ec13922a692c99bb8f5b11f1502c72e04220" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys", @@ -279,9 +280,9 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" [[package]] name = "arrayvec" @@ -291,9 +292,9 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "avm-transpiler" @@ -359,9 +360,9 @@ dependencies = [ [[package]] name = "blake3" -version = "1.5.0" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +checksum = "e9ec96fe9a81b5e365f9db71fe00edc4fe4ca2cc7dcb7861f0603012a7caa210" dependencies = [ "arrayref", "arrayvec", @@ -406,12 +407,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "cc" -version = "1.0.83" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" [[package]] name = "cfg-if" @@ -463,9 +461,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "const-oid" @@ -496,9 +494,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -527,9 +525,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", @@ -537,27 +535,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] name = "darling_macro" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] @@ -616,9 +614,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "elliptic-curve" @@ -642,9 +640,9 @@ dependencies = [ [[package]] name = "env_filter" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +checksum = "c6dc8c8ff84895b051f07a0e65f975cf225131742531338752abfb324e4449ff" dependencies = [ "log", "regex", @@ -652,9 +650,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.1" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e7cf40684ae96ade6232ed84582f40ce0a66efcd43a5117aef610534f8e0b8" +checksum = "06676b12debf7bba6903559720abca942d3a66b8acb88815fd2c7c6537e9ade1" dependencies = [ "anstream", "anstyle", @@ -681,9 +679,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -716,9 +714,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -757,7 +755,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.11", ] [[package]] @@ -838,6 +836,12 @@ dependencies = [ "serde", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "iter-extended" version = "0.32.0" @@ -853,9 +857,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" @@ -907,36 +911,36 @@ checksum = "82903360c009b816f5ab72a9b68158c27c301ee2c3f20655b55c5e589e7d3bb7" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "log" -version = "0.4.20" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] [[package]] name = "minreq" -version = "2.11.2" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fdef521c74c2884a4f3570bcdb6d2a77b3c533feb6b27ac2ae72673cc221c64" +checksum = "763d142cdff44aaadd9268bebddb156ef6c65a0e13486bb81673cf2d8739f9b0" dependencies = [ "log", "serde", @@ -976,11 +980,10 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "autocfg", "num-integer", "num-traits", ] @@ -993,19 +996,18 @@ checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -1029,15 +1031,15 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pkcs8" @@ -1063,18 +1065,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -1110,9 +1112,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.3" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -1122,9 +1124,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -1133,9 +1135,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rfc6979" @@ -1159,9 +1161,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "sec1" @@ -1179,15 +1181,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.196" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] @@ -1203,20 +1205,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] name = "serde_json" -version = "1.0.113" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" dependencies = [ "itoa", "ryu", @@ -1225,9 +1227,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.8.1" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" +checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" dependencies = [ "base64 0.22.1", "chrono", @@ -1243,14 +1245,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.8.1" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" +checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] @@ -1302,9 +1304,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -1319,9 +1321,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -1339,22 +1341,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] @@ -1407,7 +1409,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] @@ -1433,15 +1435,15 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "version_check" @@ -1476,7 +1478,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", "wasm-bindgen-shared", ] @@ -1498,7 +1500,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1538,13 +1540,14 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", + "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", @@ -1553,71 +1556,77 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] @@ -1630,5 +1639,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.72", ] diff --git a/noir/noir-repo/Cargo.lock b/noir/noir-repo/Cargo.lock index 17d714fe9bb..f6011b705e5 100644 --- a/noir/noir-repo/Cargo.lock +++ b/noir/noir-repo/Cargo.lock @@ -2906,6 +2906,7 @@ version = "0.32.0" dependencies = [ "acvm", "base64 0.21.2", + "bn254_blackbox_solver", "cfg-if 1.0.0", "chumsky", "fm", 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 0b3c964949c..200ce3099cb 100644 --- a/noir/noir-repo/aztec_macros/src/utils/hir_utils.rs +++ b/noir/noir-repo/aztec_macros/src/utils/hir_utils.rs @@ -221,6 +221,7 @@ pub fn inject_global( let global_id = context.def_interner.push_empty_global( name.clone(), module_id, + *crate_id, file_id, global.attributes.clone(), false, diff --git a/noir/noir-repo/compiler/noirc_frontend/Cargo.toml b/noir/noir-repo/compiler/noirc_frontend/Cargo.toml index 052d2c5f484..f7439a09204 100644 --- a/noir/noir-repo/compiler/noirc_frontend/Cargo.toml +++ b/noir/noir-repo/compiler/noirc_frontend/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true [dependencies] acvm.workspace = true +bn254_blackbox_solver.workspace = true noirc_arena.workspace = true noirc_errors.workspace = true noirc_printable_type.workspace = true diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs index 8cdf377528b..038a13529d7 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/mod.rs @@ -301,15 +301,12 @@ impl UnresolvedTypeExpression { // This large error size is justified because it improves parsing speeds by around 40% in // release mode. See `ParserError` definition for further explanation. #[allow(clippy::result_large_err)] - pub fn from_expr( + pub(crate) fn from_expr( expr: Expression, span: Span, ) -> Result { Self::from_expr_helper(expr).map_err(|err_expr| { - ParserError::with_reason( - ParserErrorReason::InvalidArrayLengthExpression(err_expr), - span, - ) + ParserError::with_reason(ParserErrorReason::InvalidTypeExpression(err_expr), span) }) } @@ -323,7 +320,6 @@ impl UnresolvedTypeExpression { fn from_expr_helper(expr: Expression) -> Result { match expr.kind { - // TODO(https://github.com/noir-lang/noir/issues/5571): The `sign` bool from `Literal::Integer` should be used to construct the constant type expression. ExpressionKind::Literal(Literal::Integer(int, _)) => match int.try_to_u32() { Some(int) => Ok(UnresolvedTypeExpression::Constant(int, expr.span)), None => Err(expr), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs index 3aeac115ad5..e0affad1fbf 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/mod.rs @@ -1418,22 +1418,13 @@ impl<'context> Elaborator<'context> { trait_impl.resolved_generics = self.generics.clone(); // Fetch trait constraints here - let trait_generics = if let Some(trait_id) = trait_impl.trait_id { - let trait_def = self.interner.get_trait(trait_id); - let resolved_generics = trait_def.generics.clone(); - assert_eq!(resolved_generics.len(), trait_impl.trait_generics.len()); - trait_impl - .trait_generics - .iter() - .enumerate() - .map(|(i, generic)| { - self.resolve_type_inner(generic.clone(), &resolved_generics[i].kind) - }) - .collect() - } else { - // We still resolve as to continue type checking - vecmap(&trait_impl.trait_generics, |generic| self.resolve_type(generic.clone())) - }; + let trait_generics = trait_impl + .trait_id + .and_then(|trait_id| self.resolve_trait_impl_generics(trait_impl, trait_id)) + .unwrap_or_else(|| { + // We still resolve as to continue type checking + vecmap(&trait_impl.trait_generics, |generic| self.resolve_type(generic.clone())) + }); trait_impl.resolved_trait_generics = trait_generics; @@ -1617,6 +1608,7 @@ impl<'context> Elaborator<'context> { global, self.file, self.local_module, + self.crate_id, ); generated_items.globals.push(global); diff --git a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs index 8520885bcdc..a00e770218e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/elaborator/traits.rs @@ -8,7 +8,9 @@ use crate::{ FunctionKind, TraitItem, UnresolvedGeneric, UnresolvedGenerics, UnresolvedTraitConstraint, }, hir::{ - def_collector::dc_crate::{CollectedItems, UnresolvedTrait}, + def_collector::dc_crate::{ + CollectedItems, CompilationError, UnresolvedTrait, UnresolvedTraitImpl, + }, type_check::TypeCheckError, }, hir_def::{ @@ -215,6 +217,31 @@ impl<'context> Elaborator<'context> { // Don't check the scope tree for unused variables, they can't be used in a declaration anyway. self.generics.truncate(old_generic_count); } + + pub fn resolve_trait_impl_generics( + &mut self, + trait_impl: &UnresolvedTraitImpl, + trait_id: TraitId, + ) -> Option> { + let trait_def = self.interner.get_trait(trait_id); + let resolved_generics = trait_def.generics.clone(); + if resolved_generics.len() != trait_impl.trait_generics.len() { + self.push_err(CompilationError::TypeError(TypeCheckError::GenericCountMismatch { + item: trait_def.name.to_string(), + expected: resolved_generics.len(), + found: trait_impl.trait_generics.len(), + span: trait_impl.trait_path.span(), + })); + + return None; + } + + let generics = trait_impl.trait_generics.iter().zip(resolved_generics.iter()); + let mapped = generics.map(|(generic, resolved_generic)| { + self.resolve_type_inner(generic.clone(), &resolved_generic.kind) + }); + Some(mapped.collect()) + } } /// Checks that the type of a function in a trait impl matches the type diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs index 0472b0040e5..b52201146dd 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/errors.rs @@ -7,7 +7,7 @@ use crate::{ token::Tokens, Type, }; -use acvm::{acir::AcirField, FieldElement}; +use acvm::{acir::AcirField, BlackBoxResolutionError, FieldElement}; use fm::FileId; use iter_extended::vecmap; use noirc_errors::{CustomDiagnostic, Location}; @@ -53,13 +53,11 @@ pub enum InterpreterError { NoImpl { location: Location }, NoMatchingImplFound { error: NoMatchingImplFoundError, file: FileId }, ImplMethodTypeMismatch { expected: Type, actual: Type, location: Location }, - - Unimplemented { item: String, 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 { location: Location }, ContinueNotInLoop { location: Location }, + BlackBoxError(BlackBoxResolutionError, Location), + + Unimplemented { item: String, location: Location }, // These cases are not errors, they are just used to prevent us from running more code // until the loop can be resumed properly. These cases will never be displayed to users. @@ -118,9 +116,11 @@ impl InterpreterError { | InterpreterError::Unimplemented { location, .. } | InterpreterError::NoImpl { location, .. } | InterpreterError::ImplMethodTypeMismatch { location, .. } + | InterpreterError::DebugEvaluateComptime { location, .. } + | InterpreterError::BlackBoxError(_, location) | InterpreterError::BreakNotInLoop { location, .. } - | InterpreterError::DebugEvaluateComptime { location, .. } => *location, - InterpreterError::ContinueNotInLoop { location, .. } => *location, + | InterpreterError::ContinueNotInLoop { location, .. } => *location, + InterpreterError::FailedToParseMacro { error, file, .. } => { Location::new(error.span(), *file) } @@ -370,6 +370,9 @@ impl<'a> From<&'a InterpreterError> for CustomDiagnostic { ); CustomDiagnostic::simple_error(msg, String::new(), location.span) } + InterpreterError::BlackBoxError(error, location) => { + CustomDiagnostic::simple_error(error.to_string(), String::new(), location.span) + } InterpreterError::NoMatchingImplFound { error, .. } => error.into(), InterpreterError::Break => unreachable!("Uncaught InterpreterError::Break"), InterpreterError::Continue => unreachable!("Uncaught InterpreterError::Continue"), diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 31ee8ef7200..2090310585c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -38,6 +38,7 @@ use super::errors::{IResult, InterpreterError}; use super::value::{unwrap_rc, Value}; mod builtin; +mod foreign; mod unquote; #[allow(unused)] @@ -108,7 +109,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { } if meta.kind != FunctionKind::Normal { - return self.call_builtin(function, arguments, location); + let return_type = meta.return_type().follow_bindings(); + return self.call_builtin(function, arguments, return_type, location); } let parameters = meta.parameters.0.clone(); @@ -134,6 +136,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { &mut self, function: FuncId, arguments: Vec<(Value, Location)>, + return_type: Type, location: Location, ) -> IResult { let attributes = self.elaborator.interner.function_attributes(&function); @@ -142,10 +145,16 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { if let Some(builtin) = func_attrs.builtin() { let builtin = builtin.clone(); - builtin::call_builtin(self.elaborator.interner, &builtin, arguments, location) + builtin::call_builtin( + self.elaborator.interner, + &builtin, + arguments, + return_type, + location, + ) } else if let Some(foreign) = func_attrs.foreign() { - let item = format!("Comptime evaluation for foreign functions like {foreign}"); - Err(InterpreterError::Unimplemented { item, location }) + let foreign = foreign.clone(); + foreign::call_foreign(self.elaborator.interner, &foreign, arguments, location) } else if let Some(oracle) = func_attrs.oracle() { if oracle == "print" { self.print_oracle(arguments) @@ -326,7 +335,6 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { Err(InterpreterError::VariableNotInScope { location }) } else { let name = self.elaborator.interner.definition_name(id).to_string(); - eprintln!("{name} not in scope"); Err(InterpreterError::NonComptimeVarReferenced { name, location }) } } @@ -400,6 +408,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { if let Ok(value) = self.lookup(&ident) { Ok(value) } else { + let crate_of_global = self.elaborator.interner.get_global(*global_id).crate_id; let let_ = self.elaborator.interner.get_global_let_statement(*global_id).ok_or_else( || { @@ -408,7 +417,7 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { }, )?; - if let_.comptime { + if let_.comptime || crate_of_global != self.crate_id { self.evaluate_let(let_.clone())?; } self.lookup(&ident) diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 771f23335fd..02c45165ee3 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -5,8 +5,9 @@ use std::{ use acvm::{AcirField, FieldElement}; use chumsky::Parser; -use iter_extended::vecmap; -use noirc_errors::Location; +use iter_extended::{try_vecmap, vecmap}; +use noirc_errors::{Location, Span}; +use rustc_hash::FxHashMap as HashMap; use crate::{ ast::{IntegerBitSize, TraitBound}, @@ -15,13 +16,14 @@ use crate::{ node_interner::TraitId, parser, token::{SpannedToken, Token, Tokens}, - QuotedType, Type, + QuotedType, Shared, Type, }; pub(super) fn call_builtin( interner: &mut NodeInterner, name: &str, arguments: Vec<(Value, Location)>, + return_type: Type, location: Location, ) -> IResult { match name { @@ -48,6 +50,7 @@ pub(super) fn call_builtin( trait_def_as_trait_constraint(interner, arguments, location) } "quoted_as_trait_constraint" => quoted_as_trait_constraint(interner, arguments, location), + "zeroed" => zeroed(return_type, location), _ => { let item = format!("Comptime evaluation for builtin function {name}"); Err(InterpreterError::Unimplemented { item, location }) @@ -55,7 +58,7 @@ pub(super) fn call_builtin( } } -fn check_argument_count( +pub(super) fn check_argument_count( expected: usize, arguments: &[(Value, Location)], location: Location, @@ -73,6 +76,21 @@ fn failing_constraint(message: impl Into, location: Location) -> IRes Err(InterpreterError::FailingConstraint { message, location }) } +pub(super) fn get_array( + interner: &NodeInterner, + value: Value, + location: Location, +) -> IResult<(im::Vector, Type)> { + match value { + Value::Array(values, typ) => Ok((values, typ)), + value => { + let type_var = Box::new(interner.next_type_variable()); + let expected = Type::Array(type_var.clone(), type_var); + Err(InterpreterError::TypeMismatch { expected, value, location }) + } + } +} + fn get_slice( interner: &NodeInterner, value: Value, @@ -88,7 +106,16 @@ fn get_slice( } } -fn get_u32(value: Value, location: Location) -> IResult { +pub(super) fn get_field(value: Value, location: Location) -> IResult { + match value { + Value::Field(value) => Ok(value), + value => { + Err(InterpreterError::TypeMismatch { expected: Type::FieldElement, value, location }) + } + } +} + +pub(super) fn get_u32(value: Value, location: Location) -> IResult { match value { Value::U32(value) => Ok(value), value => { @@ -408,6 +435,87 @@ fn trait_constraint_eq( Ok(Value::Bool(constraint_a == constraint_b)) } +// fn zeroed() -> T +fn zeroed(return_type: Type, location: Location) -> IResult { + match return_type { + Type::FieldElement => Ok(Value::Field(0u128.into())), + Type::Array(length_type, elem) => { + if let Some(length) = length_type.evaluate_to_u32() { + let element = zeroed(elem.as_ref().clone(), location)?; + let array = std::iter::repeat(element).take(length as usize).collect(); + Ok(Value::Array(array, Type::Array(length_type, elem))) + } else { + // Assume we can resolve the length later + Ok(Value::Zeroed(Type::Array(length_type, elem))) + } + } + Type::Slice(_) => Ok(Value::Slice(im::Vector::new(), return_type)), + Type::Integer(sign, bits) => match (sign, bits) { + (Signedness::Unsigned, IntegerBitSize::One) => Ok(Value::U8(0)), + (Signedness::Unsigned, IntegerBitSize::Eight) => Ok(Value::U8(0)), + (Signedness::Unsigned, IntegerBitSize::Sixteen) => Ok(Value::U16(0)), + (Signedness::Unsigned, IntegerBitSize::ThirtyTwo) => Ok(Value::U32(0)), + (Signedness::Unsigned, IntegerBitSize::SixtyFour) => Ok(Value::U64(0)), + (Signedness::Signed, IntegerBitSize::One) => Ok(Value::I8(0)), + (Signedness::Signed, IntegerBitSize::Eight) => Ok(Value::I8(0)), + (Signedness::Signed, IntegerBitSize::Sixteen) => Ok(Value::I16(0)), + (Signedness::Signed, IntegerBitSize::ThirtyTwo) => Ok(Value::I32(0)), + (Signedness::Signed, IntegerBitSize::SixtyFour) => Ok(Value::I64(0)), + }, + Type::Bool => Ok(Value::Bool(false)), + Type::String(length_type) => { + if let Some(length) = length_type.evaluate_to_u32() { + Ok(Value::String(Rc::new("\0".repeat(length as usize)))) + } else { + // Assume we can resolve the length later + Ok(Value::Zeroed(Type::String(length_type))) + } + } + Type::FmtString(_, _) => { + let item = "format strings in a comptime context".into(); + Err(InterpreterError::Unimplemented { item, location }) + } + Type::Unit => Ok(Value::Unit), + Type::Tuple(fields) => { + Ok(Value::Tuple(try_vecmap(fields, |field| zeroed(field, location))?)) + } + Type::Struct(struct_type, generics) => { + let fields = struct_type.borrow().get_fields(&generics); + let mut values = HashMap::default(); + + for (field_name, field_type) in fields { + let field_value = zeroed(field_type, location)?; + values.insert(Rc::new(field_name), field_value); + } + + let typ = Type::Struct(struct_type, generics); + Ok(Value::Struct(values, typ)) + } + Type::Alias(alias, generics) => zeroed(alias.borrow().get_type(&generics), location), + typ @ Type::Function(..) => { + // Using Value::Zeroed here is probably safer than using FuncId::dummy_id() or similar + Ok(Value::Zeroed(typ)) + } + Type::MutableReference(element) => { + let element = zeroed(*element, location)?; + Ok(Value::Pointer(Shared::new(element), false)) + } + Type::Quoted(QuotedType::TraitConstraint) => Ok(Value::TraitConstraint(TraitBound { + trait_path: Path::from_single(String::new(), Span::default()), + trait_id: None, + trait_generics: Vec::new(), + })), + // Optimistically assume we can resolve this type later or that the value is unused + Type::TypeVariable(_, _) + | Type::Forall(_, _) + | Type::Constant(_) + | Type::Quoted(_) + | Type::Error + | Type::TraitAsType(_, _, _) + | Type::NamedGeneric(_, _, _) => Ok(Value::Zeroed(return_type)), + } +} + fn modulus_be_bits( _interner: &mut NodeInterner, arguments: Vec<(Value, Location)>, diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs new file mode 100644 index 00000000000..fc8c57ab634 --- /dev/null +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/interpreter/foreign.rs @@ -0,0 +1,48 @@ +use acvm::BlackBoxFunctionSolver; +use bn254_blackbox_solver::Bn254BlackBoxSolver; +use iter_extended::try_vecmap; +use noirc_errors::Location; + +use crate::{ + hir::comptime::{errors::IResult, interpreter::builtin::get_field, InterpreterError, Value}, + macros_api::NodeInterner, +}; + +use super::builtin::{check_argument_count, get_array, get_u32}; + +pub(super) fn call_foreign( + interner: &mut NodeInterner, + name: &str, + arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + match name { + "poseidon2_permutation" => poseidon2_permutation(interner, arguments, location), + _ => { + let item = format!("Comptime evaluation for builtin function {name}"); + Err(InterpreterError::Unimplemented { item, location }) + } + } +} + +// poseidon2_permutation(_input: [Field; N], _state_length: u32) -> [Field; N] +fn poseidon2_permutation( + interner: &mut NodeInterner, + mut arguments: Vec<(Value, Location)>, + location: Location, +) -> IResult { + check_argument_count(2, &arguments, location)?; + + let state_length = get_u32(arguments.pop().unwrap().0, location)?; + let (input, typ) = get_array(interner, arguments.pop().unwrap().0, location)?; + + let input = try_vecmap(input, |integer| get_field(integer, location))?; + + // Currently locked to only bn254! + let fields = Bn254BlackBoxSolver + .poseidon2_permutation(&input, state_length) + .map_err(|error| InterpreterError::BlackBoxError(error, location))?; + + let array = fields.into_iter().map(Value::Field).collect(); + Ok(Value::Array(array, typ)) +} diff --git a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs index a5f35e144cd..f29b67bfc4e 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/hir/comptime/value.rs @@ -51,6 +51,7 @@ pub enum Value { TraitDefinition(TraitId), FunctionDefinition(FuncId), ModuleDefinition(ModuleId), + Zeroed(Type), } impl Value { @@ -94,6 +95,7 @@ impl Value { Value::TraitDefinition(_) => Type::Quoted(QuotedType::TraitDefinition), Value::FunctionDefinition(_) => Type::Quoted(QuotedType::FunctionDefinition), Value::ModuleDefinition(_) => Type::Quoted(QuotedType::Module), + Value::Zeroed(typ) => return Cow::Borrowed(typ), }) } @@ -215,6 +217,7 @@ impl Value { | Value::TraitConstraint(_) | Value::TraitDefinition(_) | Value::FunctionDefinition(_) + | Value::Zeroed(_) | Value::ModuleDefinition(_) => { return Err(InterpreterError::CannotInlineMacro { value: self, location }) } @@ -329,6 +332,7 @@ impl Value { | Value::TraitConstraint(_) | Value::TraitDefinition(_) | Value::FunctionDefinition(_) + | Value::Zeroed(_) | Value::ModuleDefinition(_) => { return Err(InterpreterError::CannotInlineMacro { value: self, location }) } @@ -438,6 +442,7 @@ impl Display for Value { Value::TraitDefinition(_) => write!(f, "(trait definition)"), Value::FunctionDefinition(_) => write!(f, "(function definition)"), Value::ModuleDefinition(_) => write!(f, "(module)"), + Value::Zeroed(typ) => write!(f, "(zeroed {typ})"), } } } 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 762c08b9205..e5893dc43d5 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 @@ -84,7 +84,7 @@ pub fn collect_defs( }); } - errors.extend(collector.collect_globals(context, ast.globals)); + errors.extend(collector.collect_globals(context, ast.globals, crate_id)); errors.extend(collector.collect_traits(context, ast.traits, crate_id)); @@ -106,6 +106,7 @@ impl<'a> ModCollector<'a> { &mut self, context: &mut Context, globals: Vec, + crate_id: CrateId, ) -> Vec<(CompilationError, fm::FileId)> { let mut errors = vec![]; for global in globals { @@ -115,6 +116,7 @@ impl<'a> ModCollector<'a> { global, self.file_id, self.module_id, + crate_id, ); if let Some(error) = error { @@ -492,6 +494,7 @@ impl<'a> ModCollector<'a> { let global_id = context.def_interner.push_empty_global( name.clone(), trait_id.0.local_id, + krate, self.file_id, vec![], false, @@ -862,12 +865,14 @@ pub(crate) fn collect_global( global: LetStatement, file_id: FileId, module_id: LocalModuleId, + crate_id: CrateId, ) -> (UnresolvedGlobal, Option<(CompilationError, FileId)>) { let name = global.pattern.name_ident().clone(); let global_id = interner.push_empty_global( name.clone(), module_id, + crate_id, file_id, global.attributes.clone(), matches!(global.pattern, Pattern::Mutable { .. }), 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 61a108b04fe..87ff45f8f1a 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/node_interner.rs @@ -543,6 +543,7 @@ pub struct GlobalInfo { pub definition_id: DefinitionId, pub ident: Ident, pub local_id: LocalModuleId, + pub crate_id: CrateId, pub location: Location, pub let_statement: StmtId, pub value: Option, @@ -756,6 +757,7 @@ impl NodeInterner { &mut self, ident: Ident, local_id: LocalModuleId, + crate_id: CrateId, let_statement: StmtId, file: FileId, attributes: Vec, @@ -773,6 +775,7 @@ impl NodeInterner { definition_id, ident, local_id, + crate_id, let_statement, location, value: None, @@ -786,10 +789,12 @@ impl NodeInterner { } /// Intern an empty global. Used for collecting globals before they're defined + #[allow(clippy::too_many_arguments)] pub fn push_empty_global( &mut self, name: Ident, local_id: LocalModuleId, + crate_id: CrateId, file: FileId, attributes: Vec, mutable: bool, @@ -797,7 +802,8 @@ impl NodeInterner { ) -> GlobalId { let statement = self.push_stmt(HirStatement::Error); let span = name.span(); - let id = self.push_global(name, local_id, statement, file, attributes, mutable, comptime); + let id = self + .push_global(name, local_id, crate_id, statement, file, attributes, mutable, comptime); self.push_stmt_location(statement, span, file); id } 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 9df9a8e15dd..c566489eb40 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/errors.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/errors.rs @@ -20,8 +20,8 @@ pub enum ParserErrorReason { MissingSeparatingSemi, #[error("constrain keyword is deprecated")] ConstrainDeprecated, - #[error("Expression is invalid in an array-length type: '{0}'. Only unsigned integer constants, globals, generics, +, -, *, /, and % may be used in this context.")] - InvalidArrayLengthExpression(Expression), + #[error("Invalid type expression: '{0}'. Only unsigned integer constants up to `u32`, globals, generics, +, -, *, /, and % may be used in this context.")] + InvalidTypeExpression(Expression), #[error("Early 'return' is unsupported")] EarlyReturn, #[error("Patterns aren't allowed in a trait's function declarations")] @@ -44,10 +44,8 @@ pub enum ParserErrorReason { InvalidBitSize(u32), #[error("{0}")] Lexer(LexerErrorKind), - // TODO(https://github.com/noir-lang/noir/issues/5571): This error can be removed once support is expanded for type-level integers. - // This error reason was added to prevent the panic outline in this issue: https://github.com/noir-lang/noir/issues/5552. - #[error("Only unsigned integers allowed for numeric generics")] - SignedNumericGeneric, + #[error("The only supported numeric generic types are `u1`, `u8`, `u16`, and `u32`")] + ForbiddenNumericGenericType, } /// Represents a parsing error, or a parsing error in the making. 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 fd1c7d5237f..2fd337e1cb1 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 @@ -4,8 +4,8 @@ use super::{ parameter_name_recovery, parameter_recovery, parenthesized, parse_type, pattern, self_parameter, where_clause, NoirParser, }; -use crate::parser::spanned; use crate::token::{Keyword, Token}; +use crate::{ast::IntegerBitSize, parser::spanned}; use crate::{ ast::{ FunctionDefinition, FunctionReturnType, ItemVisibility, NoirFunction, Param, Visibility, @@ -91,10 +91,12 @@ pub(super) fn numeric_generic() -> impl NoirParser { .map(|(ident, typ)| UnresolvedGeneric::Numeric { ident, typ }) .validate(|generic, span, emit| { if let UnresolvedGeneric::Numeric { typ, .. } = &generic { - if let UnresolvedTypeData::Integer(signedness, _) = typ.typ { - if matches!(signedness, Signedness::Signed) { + if let UnresolvedTypeData::Integer(signedness, bit_size) = typ.typ { + if matches!(signedness, Signedness::Signed) + || matches!(bit_size, IntegerBitSize::SixtyFour) + { emit(ParserError::with_reason( - ParserErrorReason::SignedNumericGeneric, + ParserErrorReason::ForbiddenNumericGenericType, span, )); } @@ -228,7 +230,7 @@ mod test { // fn func_name(x: impl Eq) {} with error Expected an end of input but found end of input // "fn func_name(x: impl Eq) {}", "fn func_name(x: impl Eq, y : T) where T: SomeTrait + Eq {}", - "fn func_name(x: [Field; N]) {}", + "fn func_name(x: [Field; N]) {}", ], ); @@ -249,8 +251,12 @@ mod test { "fn func_name(y: T) {}", "fn func_name(y: T) {}", "fn func_name(y: T) {}", - "fn func_name(y: T) {}", + // Test failure of missing `let` + "fn func_name(y: T) {}", + // Test that signed numeric generics are banned "fn func_name() {}", + // Test that `u64` is banned + "fn func_name(x: [Field; N]) {}", ], ); } diff --git a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs index 3bd59608b12..cecc1cbcd4c 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/parser/parser/types.rs @@ -3,7 +3,9 @@ use super::{ expression_with_precedence, keyword, nothing, parenthesized, path, NoirParser, ParserError, ParserErrorReason, Precedence, }; -use crate::ast::{Recoverable, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression}; +use crate::ast::{ + Expression, Recoverable, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, +}; use crate::QuotedType; use crate::parser::labels::ParsingRuleLabel; @@ -201,8 +203,7 @@ pub(super) fn generic_type_args<'a>( // separator afterward. Failing early here ensures we try the `type_expression` // parser afterward. .then_ignore(one_of([Token::Comma, Token::Greater]).rewind()) - .or(type_expression() - .map_with_span(|expr, span| UnresolvedTypeData::Expression(expr).with_span(span))) + .or(type_expression_validated()) .separated_by(just(Token::Comma)) .allow_trailing() .at_least(1) @@ -234,7 +235,26 @@ pub(super) fn slice_type( }) } -pub(super) fn type_expression() -> impl NoirParser { +fn type_expression() -> impl NoirParser { + type_expression_inner().try_map(UnresolvedTypeExpression::from_expr) +} + +/// This parser is the same as `type_expression()`, however, it continues parsing and +/// emits a parser error in the case of an invalid type expression rather than halting the parser. +fn type_expression_validated() -> impl NoirParser { + type_expression_inner().validate(|expr, span, emit| { + let type_expr = UnresolvedTypeExpression::from_expr(expr, span); + match type_expr { + Ok(type_expression) => UnresolvedTypeData::Expression(type_expression).with_span(span), + Err(parser_error) => { + emit(parser_error); + UnresolvedType::error(span) + } + } + }) +} + +fn type_expression_inner() -> impl NoirParser { recursive(|expr| { expression_with_precedence( Precedence::lowest_type_precedence(), @@ -246,7 +266,6 @@ pub(super) fn type_expression() -> impl NoirParser { ) }) .labelled(ParsingRuleLabel::TypeExpression) - .try_map(UnresolvedTypeExpression::from_expr) } pub(super) fn tuple_type(type_parser: T) -> impl NoirParser diff --git a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs index b4f17489ff7..cbc15da20ff 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/tests.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/tests.rs @@ -1634,7 +1634,7 @@ fn numeric_generic_in_function_signature() { #[test] fn numeric_generic_as_struct_field_type() { let src = r#" - struct Foo { + struct Foo { a: Field, b: N, } @@ -1695,7 +1695,7 @@ fn numeric_generic_as_param_type() { #[test] fn numeric_generic_used_in_nested_type_fail() { let src = r#" - struct Foo { + struct Foo { a: Field, b: Bar, } @@ -1745,7 +1745,7 @@ fn numeric_generic_used_in_nested_type_pass() { #[test] fn numeric_generic_used_in_trait() { - // We want to make sure that `N` in `impl Deserialize` does + // We want to make sure that `N` in `impl Deserialize` does // not trigger `expected type, found numeric generic parameter N` as the trait // does in fact expect a numeric generic. let src = r#" @@ -1756,7 +1756,7 @@ fn numeric_generic_used_in_trait() { d: T, } - impl Deserialize for MyType { + impl Deserialize for MyType { fn deserialize(fields: [Field; N], other: T) -> Self { MyType { a: fields[0], b: fields[1], c: fields[2], d: other } } @@ -2224,7 +2224,6 @@ fn impl_stricter_than_trait_different_trait_generics() { .. }) = &errors[0].0 { - dbg!(constraint_name.as_str()); assert!(matches!(constraint_typ.to_string().as_str(), "A")); assert!(matches!(constraint_name.as_str(), "T2")); assert!(matches!(constraint_generics[0].to_string().as_str(), "B")); @@ -2459,3 +2458,39 @@ fn no_super() { assert_eq!(span.start(), 4); assert_eq!(span.end(), 9); } + +#[test] +fn trait_impl_generics_count_mismatch() { + let src = r#" + trait Foo {} + + impl Foo<()> for Field {} + + fn main() {}"#; + let errors = get_program_errors(src); + assert_eq!(errors.len(), 1); + + let CompilationError::TypeError(TypeCheckError::GenericCountMismatch { + item, + expected, + found, + .. + }) = &errors[0].0 + else { + panic!("Expected a generic count mismatch error, got {:?}", errors[0].0); + }; + + assert_eq!(item, "Foo"); + assert_eq!(*expected, 0); + assert_eq!(*found, 1); +} + +#[test] +fn bit_not_on_untyped_integer() { + let src = r#" + fn main() { + let _: u32 = 3 & !1; + } + "#; + assert_no_errors(src); +} diff --git a/noir/noir-repo/tooling/lsp/src/lib.rs b/noir/noir-repo/tooling/lsp/src/lib.rs index ea787f05e98..c7b70339e1d 100644 --- a/noir/noir-repo/tooling/lsp/src/lib.rs +++ b/noir/noir-repo/tooling/lsp/src/lib.rs @@ -30,7 +30,7 @@ use lsp_types::{ use nargo::{ package::{Package, PackageType}, parse_all, - workspace::Workspace, + workspace::{self, Workspace}, }; use nargo_toml::{find_file_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{file_manager_with_stdlib, prepare_crate, NOIR_ARTIFACT_VERSION_STRING}; @@ -398,6 +398,22 @@ fn parse_diff(file_manager: &FileManager, state: &mut LspState) -> ParsedFiles { } } +pub fn insert_all_files_for_workspace_into_file_manager( + state: &LspState, + workspace: &workspace::Workspace, + file_manager: &mut FileManager, +) { + // First add files we cached: these have the source code of files that are modified + // but not saved to disk yet, and we want to make sure all LSP features work well + // according to these unsaved buffers, not what's saved on disk. + for (path, source) in &state.input_files { + let path = path.strip_prefix("file://").unwrap(); + file_manager.add_file_with_source_canonical_path(Path::new(path), source.clone()); + } + + nargo::insert_all_files_for_workspace_into_file_manager(workspace, file_manager); +} + #[test] fn prepare_package_from_source_string() { let source = r#" diff --git a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs index b6c28d37b91..24409e85db8 100644 --- a/noir/noir-repo/tooling/lsp/src/notifications/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/notifications/mod.rs @@ -1,11 +1,10 @@ use std::ops::ControlFlow; +use crate::insert_all_files_for_workspace_into_file_manager; use async_lsp::{ErrorCode, LanguageClient, ResponseError}; -use nargo::insert_all_files_for_workspace_into_file_manager; use noirc_driver::{check_crate, file_manager_with_stdlib}; use noirc_errors::{DiagnosticKind, FileDiagnostic}; -use crate::requests::collect_lenses_for_package; use crate::types::{ notification, Diagnostic, DiagnosticSeverity, DidChangeConfigurationParams, DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams, @@ -13,8 +12,8 @@ use crate::types::{ }; use crate::{ - byte_span_to_range, get_package_tests_in_crate, parse_diff, prepare_source, - resolve_workspace_for_source_path, workspace_package_for_file, LspState, + byte_span_to_range, get_package_tests_in_crate, parse_diff, resolve_workspace_for_source_path, + LspState, }; pub(super) fn on_initialized( @@ -38,8 +37,15 @@ pub(super) fn on_did_open_text_document( state.input_files.insert(params.text_document.uri.to_string(), params.text_document.text); let document_uri = params.text_document.uri; - - match process_workspace_for_noir_document(document_uri, state) { + let only_process_document_uri_package = false; + let output_diagnostics = true; + + match process_workspace_for_noir_document( + state, + document_uri, + only_process_document_uri_package, + output_diagnostics, + ) { Ok(_) => { state.open_documents_count += 1; ControlFlow::Continue(()) @@ -55,41 +61,19 @@ pub(super) fn on_did_change_text_document( let text = params.content_changes.into_iter().next().unwrap().text; state.input_files.insert(params.text_document.uri.to_string(), text.clone()); - let file_path = params.text_document.uri.to_file_path().unwrap(); - - let (mut context, crate_id) = prepare_source(text, state); - let _ = check_crate(&mut context, crate_id, false, false, None); - - let workspace = match resolve_workspace_for_source_path( - params.text_document.uri.to_file_path().unwrap().as_path(), - &state.root_path, + let document_uri = params.text_document.uri; + let only_process_document_uri_package = true; + let output_diagnotics = false; + + match process_workspace_for_noir_document( + state, + document_uri, + only_process_document_uri_package, + output_diagnotics, ) { - Ok(workspace) => workspace, - Err(lsp_error) => { - return ControlFlow::Break(Err(ResponseError::new( - ErrorCode::REQUEST_FAILED, - lsp_error.to_string(), - ) - .into())) - } - }; - - let package = match workspace_package_for_file(&workspace, &file_path) { - Some(package) => package, - None => { - return ControlFlow::Break(Err(ResponseError::new( - ErrorCode::REQUEST_FAILED, - "Selected workspace has no members", - ) - .into())) - } - }; - - let lenses = collect_lenses_for_package(&context, crate_id, &workspace, package, None); - - state.cached_lenses.insert(params.text_document.uri.to_string(), lenses); - - ControlFlow::Continue(()) + Ok(_) => ControlFlow::Continue(()), + Err(err) => ControlFlow::Break(Err(err)), + } } pub(super) fn on_did_close_text_document( @@ -105,7 +89,19 @@ pub(super) fn on_did_close_text_document( state.cached_definitions.clear(); } - ControlFlow::Continue(()) + let document_uri = params.text_document.uri; + let only_process_document_uri_package = true; + let output_diagnotics = false; + + match process_workspace_for_noir_document( + state, + document_uri, + only_process_document_uri_package, + output_diagnotics, + ) { + Ok(_) => ControlFlow::Continue(()), + Err(err) => ControlFlow::Break(Err(err)), + } } pub(super) fn on_did_save_text_document( @@ -113,8 +109,15 @@ pub(super) fn on_did_save_text_document( params: DidSaveTextDocumentParams, ) -> ControlFlow> { let document_uri = params.text_document.uri; - - match process_workspace_for_noir_document(document_uri, state) { + let only_process_document_uri_package = false; + let output_diagnotics = true; + + match process_workspace_for_noir_document( + state, + document_uri, + only_process_document_uri_package, + output_diagnotics, + ) { Ok(_) => ControlFlow::Continue(()), Err(err) => ControlFlow::Break(Err(err)), } @@ -124,8 +127,10 @@ pub(super) fn on_did_save_text_document( // it's only contained in a package), then type-checks the workspace's packages, // caching code lenses and type definitions, and notifying about compilation errors. pub(crate) fn process_workspace_for_noir_document( - document_uri: lsp_types::Url, state: &mut LspState, + document_uri: lsp_types::Url, + only_process_document_uri_package: bool, + output_diagnostics: bool, ) -> Result<(), async_lsp::Error> { let file_path = document_uri.to_file_path().map_err(|_| { ResponseError::new(ErrorCode::REQUEST_FAILED, "URI is not a valid file path") @@ -137,13 +142,23 @@ pub(crate) fn process_workspace_for_noir_document( })?; let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); - insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); + insert_all_files_for_workspace_into_file_manager( + state, + &workspace, + &mut workspace_file_manager, + ); let parsed_files = parse_diff(&workspace_file_manager, state); let diagnostics: Vec<_> = workspace .into_iter() .flat_map(|package| -> Vec { + let package_root_dir: String = package.root_dir.as_os_str().to_string_lossy().into(); + + if only_process_document_uri_package && !file_path.starts_with(&package.root_dir) { + return vec![]; + } + let (mut context, crate_id) = crate::prepare_package(&workspace_file_manager, &parsed_files, package); @@ -152,8 +167,6 @@ pub(crate) fn process_workspace_for_noir_document( Err(errors_and_warnings) => errors_and_warnings, }; - let package_root_dir: String = package.root_dir.as_os_str().to_string_lossy().into(); - // We don't add test headings for a package if it contains no `#[test]` functions if let Some(tests) = get_package_tests_in_crate(&context, &crate_id, &package.name) { let _ = state.client.notify::(NargoPackageTests { @@ -176,46 +189,53 @@ pub(crate) fn process_workspace_for_noir_document( let fm = &context.file_manager; let files = fm.as_file_map(); - file_diagnostics - .into_iter() - .filter_map(|FileDiagnostic { file_id, diagnostic, call_stack: _ }| { - // Ignore diagnostics for any file that wasn't the file we saved - // TODO: In the future, we could create "related" diagnostics for these files - if fm.path(file_id).expect("file must exist to have emitted diagnostic") - != file_path - { - return None; - } - - // TODO: Should this be the first item in secondaries? Should we bail when we find a range? - let range = diagnostic - .secondaries - .into_iter() - .filter_map(|sec| byte_span_to_range(files, file_id, sec.span.into())) - .last() - .unwrap_or_default(); - - let severity = match diagnostic.kind { - DiagnosticKind::Error => DiagnosticSeverity::ERROR, - DiagnosticKind::Warning => DiagnosticSeverity::WARNING, - DiagnosticKind::Info => DiagnosticSeverity::INFORMATION, - DiagnosticKind::Bug => DiagnosticSeverity::WARNING, - }; - Some(Diagnostic { - range, - severity: Some(severity), - message: diagnostic.message, - ..Default::default() + if output_diagnostics { + file_diagnostics + .into_iter() + .filter_map(|FileDiagnostic { file_id, diagnostic, call_stack: _ }| { + // Ignore diagnostics for any file that wasn't the file we saved + // TODO: In the future, we could create "related" diagnostics for these files + if fm.path(file_id).expect("file must exist to have emitted diagnostic") + != file_path + { + return None; + } + + // TODO: Should this be the first item in secondaries? Should we bail when we find a range? + let range = diagnostic + .secondaries + .into_iter() + .filter_map(|sec| byte_span_to_range(files, file_id, sec.span.into())) + .last() + .unwrap_or_default(); + + let severity = match diagnostic.kind { + DiagnosticKind::Error => DiagnosticSeverity::ERROR, + DiagnosticKind::Warning => DiagnosticSeverity::WARNING, + DiagnosticKind::Info => DiagnosticSeverity::INFORMATION, + DiagnosticKind::Bug => DiagnosticSeverity::WARNING, + }; + Some(Diagnostic { + range, + severity: Some(severity), + message: diagnostic.message, + ..Default::default() + }) }) - }) - .collect() + .collect() + } else { + vec![] + } }) .collect(); - let _ = state.client.publish_diagnostics(PublishDiagnosticsParams { - uri: document_uri, - version: None, - diagnostics, - }); + + if output_diagnostics { + let _ = state.client.publish_diagnostics(PublishDiagnosticsParams { + uri: document_uri, + version: None, + diagnostics, + }); + } Ok(()) } @@ -226,3 +246,82 @@ pub(super) fn on_exit( ) -> ControlFlow> { ControlFlow::Continue(()) } + +#[cfg(test)] +mod notification_tests { + use crate::test_utils; + + use super::*; + use lsp_types::{ + InlayHintLabel, InlayHintParams, Position, TextDocumentContentChangeEvent, + TextDocumentIdentifier, TextDocumentItem, VersionedTextDocumentIdentifier, + WorkDoneProgressParams, + }; + use tokio::test; + + #[test] + async fn test_caches_open_files() { + let (mut state, noir_text_document) = test_utils::init_lsp_server("inlay_hints").await; + + // Open the document, fake the text to be empty + on_did_open_text_document( + &mut state, + DidOpenTextDocumentParams { + text_document: TextDocumentItem { + uri: noir_text_document.clone(), + language_id: "noir".to_string(), + version: 0, + text: "".to_string(), + }, + }, + ); + + // Fake the text to change to "global a = 1;" + on_did_change_text_document( + &mut state, + DidChangeTextDocumentParams { + text_document: VersionedTextDocumentIdentifier { + uri: noir_text_document.clone(), + version: 1, + }, + content_changes: vec![TextDocumentContentChangeEvent { + range: None, + range_length: None, + // Should get an inlay hint for ": bool" after "a" + text: "global a = true;".to_string(), + }], + }, + ); + + // Get inlay hints. These should now be relative to the changed text, + // not the saved file's text. + let inlay_hints = crate::requests::on_inlay_hint_request( + &mut state, + InlayHintParams { + work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, + text_document: TextDocumentIdentifier { uri: noir_text_document }, + range: lsp_types::Range { + start: lsp_types::Position { line: 0, character: 0 }, + end: lsp_types::Position { line: 1, character: 0 }, + }, + }, + ) + .await + .expect("Could not execute on_inlay_hint_request") + .unwrap(); + + assert_eq!(inlay_hints.len(), 1); + + let inlay_hint = &inlay_hints[0]; + assert_eq!(inlay_hint.position, Position { line: 0, character: 8 }); + + if let InlayHintLabel::LabelParts(labels) = &inlay_hint.label { + assert_eq!(labels.len(), 2); + assert_eq!(labels[0].value, ": "); + assert_eq!(labels[0].location, None); + assert_eq!(labels[1].value, "bool"); + } else { + panic!("Expected InlayHintLabel::LabelParts, got {:?}", inlay_hint.label); + } + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/mod.rs b/noir/noir-repo/tooling/lsp/src/requests/mod.rs index 08c4bc32b65..4d261c1b50a 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/mod.rs @@ -1,5 +1,6 @@ use std::{collections::HashMap, future::Future}; +use crate::insert_all_files_for_workspace_into_file_manager; use crate::{ parse_diff, resolve_workspace_for_source_path, types::{CodeLensOptions, InitializeParams}, @@ -11,7 +12,6 @@ use lsp_types::{ TextDocumentSyncCapability, TextDocumentSyncKind, TypeDefinitionProviderCapability, Url, WorkDoneProgressOptions, }; -use nargo::insert_all_files_for_workspace_into_file_manager; use nargo_fmt::Config; use noirc_driver::file_manager_with_stdlib; use noirc_frontend::{graph::Dependency, macros_api::NodeInterner}; @@ -367,7 +367,11 @@ where let package_root_path: String = package.root_dir.as_os_str().to_string_lossy().into(); let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); - insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); + insert_all_files_for_workspace_into_file_manager( + state, + &workspace, + &mut workspace_file_manager, + ); let parsed_files = parse_diff(&workspace_file_manager, state); let (mut context, crate_id) = diff --git a/noir/noir-repo/tooling/lsp/src/requests/profile_run.rs b/noir/noir-repo/tooling/lsp/src/requests/profile_run.rs index 57bc3299455..d3b7743557a 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/profile_run.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/profile_run.rs @@ -3,9 +3,10 @@ use std::{ future::{self, Future}, }; +use crate::insert_all_files_for_workspace_into_file_manager; use acvm::acir::circuit::ExpressionWidth; use async_lsp::{ErrorCode, ResponseError}; -use nargo::{insert_all_files_for_workspace_into_file_manager, ops::report_errors}; +use nargo::ops::report_errors; use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_artifacts::debug::DebugArtifact; use noirc_driver::{ @@ -53,7 +54,11 @@ fn on_profile_run_request_inner( })?; let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); - insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); + insert_all_files_for_workspace_into_file_manager( + state, + &workspace, + &mut workspace_file_manager, + ); let parsed_files = parse_diff(&workspace_file_manager, state); // Since we filtered on crate name, this should be the only item in the iterator diff --git a/noir/noir-repo/tooling/lsp/src/requests/references.rs b/noir/noir-repo/tooling/lsp/src/requests/references.rs index badea8921b2..375e0b69aed 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/references.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/references.rs @@ -108,7 +108,16 @@ mod references_tests { let two_lib = Url::from_file_path(workspace_dir.join("two/src/lib.nr")).unwrap(); // We call this to open the document, so that the entire workspace is analyzed - notifications::process_workspace_for_noir_document(one_lib.clone(), &mut state).unwrap(); + let only_process_document_uri_package = false; + let output_diagnostics = true; + + notifications::process_workspace_for_noir_document( + &mut state, + one_lib.clone(), + only_process_document_uri_package, + output_diagnostics, + ) + .unwrap(); let params = ReferenceParams { text_document_position: TextDocumentPositionParams { diff --git a/noir/noir-repo/tooling/lsp/src/requests/test_run.rs b/noir/noir-repo/tooling/lsp/src/requests/test_run.rs index a4d507161c2..bf4d9763faf 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/test_run.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/test_run.rs @@ -1,10 +1,8 @@ use std::future::{self, Future}; +use crate::insert_all_files_for_workspace_into_file_manager; use async_lsp::{ErrorCode, ResponseError}; -use nargo::{ - insert_all_files_for_workspace_into_file_manager, - ops::{run_test, TestStatus}, -}; +use nargo::ops::{run_test, TestStatus}; use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{ check_crate, file_manager_with_stdlib, CompileOptions, NOIR_ARTIFACT_VERSION_STRING, @@ -51,7 +49,11 @@ fn on_test_run_request_inner( })?; let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); - insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); + insert_all_files_for_workspace_into_file_manager( + state, + &workspace, + &mut workspace_file_manager, + ); let parsed_files = parse_diff(&workspace_file_manager, state); // Since we filtered on crate name, this should be the only item in the iterator diff --git a/noir/noir-repo/tooling/lsp/src/requests/tests.rs b/noir/noir-repo/tooling/lsp/src/requests/tests.rs index f0d2388504c..20b96029696 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/tests.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/tests.rs @@ -1,8 +1,8 @@ use std::future::{self, Future}; +use crate::insert_all_files_for_workspace_into_file_manager; use async_lsp::{ErrorCode, LanguageClient, ResponseError}; use lsp_types::{LogMessageParams, MessageType}; -use nargo::insert_all_files_for_workspace_into_file_manager; use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{check_crate, file_manager_with_stdlib, NOIR_ARTIFACT_VERSION_STRING}; @@ -51,7 +51,11 @@ fn on_tests_request_inner( })?; let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); - insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); + insert_all_files_for_workspace_into_file_manager( + state, + &workspace, + &mut workspace_file_manager, + ); let parsed_files = parse_diff(&workspace_file_manager, state); let package_tests: Vec<_> = workspace