diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8c99a6e3603..37136df68d8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -63,7 +63,7 @@ jobs: repo: noir-lang/noir ref: master token: ${{ secrets.GITHUB_TOKEN }} - inputs: '{ "noir-ref": "${{ needs.release-please.outputs.tag-name }}", "publish": true }' + inputs: '{ "tag": "${{ needs.release-please.outputs.tag-name }}", "publish": true }' publish-wasm: name: Publish noir_wasm package diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 66cb260aebf..7d429accdbf 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -130,7 +130,7 @@ jobs: uses: actions/download-artifact@v3 with: name: noir_wasm - path: ./crates/wasm/dist + path: ./crates/wasm/result - name: Download nargo binary uses: actions/download-artifact@v3 diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 76d5538a47e..30b6d45ad63 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.9.0" + ".": "0.10.1" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 830c841ddb6..a2f9a80b24c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,124 @@ # Changelog +## [0.10.1](https://github.com/noir-lang/noir/compare/v0.10.0...v0.10.1) (2023-08-15) + + +### Features + +* Add full call stacks to runtime errors ([#2310](https://github.com/noir-lang/noir/issues/2310)) ([9004181](https://github.com/noir-lang/noir/commit/900418192216dc2657d6ffe48f85ac82411fb054)) +* Improved error message for unexpected return type ([#2302](https://github.com/noir-lang/noir/issues/2302)) ([d7e1e65](https://github.com/noir-lang/noir/commit/d7e1e658fe09443ae37f34e3fc6a8cb7765cf1d9)) +* **ssa:** Perform dead instruction elimination on intrinsic functions ([#2276](https://github.com/noir-lang/noir/issues/2276)) ([3fe3f8c](https://github.com/noir-lang/noir/commit/3fe3f8ca11d646a054f36e6939211a22f79f10f1)) +* **ssa:** Switch mem2reg pass to be per function rather than per block ([#2243](https://github.com/noir-lang/noir/issues/2243)) ([0d548b9](https://github.com/noir-lang/noir/commit/0d548b9b27710de231759c34e1a198c9991d33ef)) +* **stdlib:** Implement `str` `as_bytes` and `into_bytes` function ([#2298](https://github.com/noir-lang/noir/issues/2298)) ([92549d4](https://github.com/noir-lang/noir/commit/92549d432470244ff7e8581fbe02c744c88942d9)) + +## [0.10.0](https://github.com/noir-lang/noir/compare/v0.9.0...v0.10.0) (2023-08-15) + + +### ⚠ BREAKING CHANGES + +* **nargo:** Remove `-p` short flag from the `--program-dir` flag ([#2300](https://github.com/noir-lang/noir/issues/2300)) +* **nargo:** Replace `--contracts` flag with `contract` package type ([#2204](https://github.com/noir-lang/noir/issues/2204)) +* **nargo:** remove `flat_witness` feature flag ([#2208](https://github.com/noir-lang/noir/issues/2208)) +* **nargo:** Require package `type` be specified in Nargo.toml ([#2134](https://github.com/noir-lang/noir/issues/2134)) +* Allow specifying new package name with `--name` flag ([#2144](https://github.com/noir-lang/noir/issues/2144)) +* **nargo:** Remove unused flags on LSP command ([#2170](https://github.com/noir-lang/noir/issues/2170)) +* Support workspaces and package selection on every nargo command ([#1992](https://github.com/noir-lang/noir/issues/1992)) +* **nargo:** Require package names in Nargo.toml files ([#2056](https://github.com/noir-lang/noir/issues/2056)) +* Update to ACVM 0.21.0 ([#2051](https://github.com/noir-lang/noir/issues/2051)) +* Drop support for the legacy SSA ([#2049](https://github.com/noir-lang/noir/issues/2049)) +* **nargo:** Rename nargo gates to nargo info ([#2038](https://github.com/noir-lang/noir/issues/2038)) +* **nargo:** Default to new SSA code for compilation + +### Features + +* **acir_gen:** RecursiveAggregation opcode and updates to black box func call generation ([#2097](https://github.com/noir-lang/noir/issues/2097)) ([5cb8166](https://github.com/noir-lang/noir/commit/5cb816664e03992a766ba9dcb2650e9596fbb291)) +* Add `assert_constant` ([#2242](https://github.com/noir-lang/noir/issues/2242)) ([a72daa4](https://github.com/noir-lang/noir/commit/a72daa4764310078ab0c70720f16c85b2c0375bb)) +* Add `deprecated` attribute ([#2041](https://github.com/noir-lang/noir/issues/2041)) ([9e2cf6f](https://github.com/noir-lang/noir/commit/9e2cf6f25f775d927b67c12aba1698c5635242e3)) +* Add `Option<T>` to noir stdlib ([#1781](https://github.com/noir-lang/noir/issues/1781)) ([920a900](https://github.com/noir-lang/noir/commit/920a900818b31285c9bf2f5dd5b84c2799610a7c)) +* Add basic benchmarking ([#2213](https://github.com/noir-lang/noir/issues/2213)) ([c8fe617](https://github.com/noir-lang/noir/commit/c8fe6175fa69abdfa29d7a9e1c5c653af5f15f1d)) +* Add slice append ([#2241](https://github.com/noir-lang/noir/issues/2241)) ([90c5d18](https://github.com/noir-lang/noir/commit/90c5d182b578b6d512e4b5dc2c07810e6815f31e)) +* Add support for bitshifts by distances known at runtime ([#2072](https://github.com/noir-lang/noir/issues/2072)) ([b0fbc53](https://github.com/noir-lang/noir/commit/b0fbc536dc432ba8d3ab6c12462758b11c2c21c4)) +* Add support for slices of structs and nested slices in brillig ([#2084](https://github.com/noir-lang/noir/issues/2084)) ([620517f](https://github.com/noir-lang/noir/commit/620517f6527a7b5173dccc16e200ce349f489998)) +* allow returning nested arrays from brillig ([#2047](https://github.com/noir-lang/noir/issues/2047)) ([4378bb8](https://github.com/noir-lang/noir/commit/4378bb85bf2900e7ab13856cadc764fd4a80bff5)) +* Allow specifying new package name with `--name` flag ([#2144](https://github.com/noir-lang/noir/issues/2144)) ([e932599](https://github.com/noir-lang/noir/commit/e932599b1187fbf426b73c364626d1b17348a55e)) +* Drop support for the legacy SSA ([#2049](https://github.com/noir-lang/noir/issues/2049)) ([3f33e44](https://github.com/noir-lang/noir/commit/3f33e447fbd6f1b94ff9935e21905c68c1dc9c83)) +* Execute brillig opcodes with constant inputs at compile-time ([#2190](https://github.com/noir-lang/noir/issues/2190)) ([79af8e6](https://github.com/noir-lang/noir/commit/79af8e6fd359723716395913b23057beddcbdb83)) +* Format strings for prints ([#1952](https://github.com/noir-lang/noir/issues/1952)) ([3c82721](https://github.com/noir-lang/noir/commit/3c827217900d19a710ee8a49d782ed3d43a6336c)) +* Implement traits - parser support [#2094](https://github.com/noir-lang/noir/issues/2094) ([#2230](https://github.com/noir-lang/noir/issues/2230)) ([589f173](https://github.com/noir-lang/noir/commit/589f173a85dabb68ada248e5c44fc0e13c6eb0f3)) +* Implement type aliases ([#2112](https://github.com/noir-lang/noir/issues/2112)) ([ce94cb4](https://github.com/noir-lang/noir/commit/ce94cb4f9f9fccf504de9d0b12b8760fc8fab75c)) +* Include struct names in ABIs ([#2266](https://github.com/noir-lang/noir/issues/2266)) ([9824ca5](https://github.com/noir-lang/noir/commit/9824ca567c6c151c0cc5469be402ffba2cbaa9cc)) +* Issue warning for signed integers ([#2185](https://github.com/noir-lang/noir/issues/2185)) ([1be1bcc](https://github.com/noir-lang/noir/commit/1be1bcc5b40374c76f0b8e4d717e9292e15457f5)) +* Make arrays and slices polymorphic over each other ([#2070](https://github.com/noir-lang/noir/issues/2070)) ([ef91286](https://github.com/noir-lang/noir/commit/ef91286b920fb3e17c7368839a93ccad2441edc8)) +* **nargo:** Add `--exact` flag to `nargo test` ([#2272](https://github.com/noir-lang/noir/issues/2272)) ([1ad9199](https://github.com/noir-lang/noir/commit/1ad9199bcfbc5dd52166038a25ddfc7b03d90981)) +* **nargo:** Add `--workspace` flag to run commands in every package ([#2313](https://github.com/noir-lang/noir/issues/2313)) ([d6deb0c](https://github.com/noir-lang/noir/commit/d6deb0c96bf8a12e21881bf10bbd139bc6531796)) +* **nargo:** Add support for contracts in `nargo check` ([#2267](https://github.com/noir-lang/noir/issues/2267)) ([3d1b252](https://github.com/noir-lang/noir/commit/3d1b2522c8d9a0acebff102f9f877c44178c5db5)) +* **nargo:** Default to new SSA code for compilation ([ce37718](https://github.com/noir-lang/noir/commit/ce377186a1d6afa025bd88d7436f61319c30cc33)) +* **nargo:** Replace `--contracts` flag with `contract` package type ([#2204](https://github.com/noir-lang/noir/issues/2204)) ([968e12c](https://github.com/noir-lang/noir/commit/968e12c896f3fc910f36063b5afa542756ea95db)) +* **nargo:** Require package `type` be specified in Nargo.toml ([#2134](https://github.com/noir-lang/noir/issues/2134)) ([1c991d0](https://github.com/noir-lang/noir/commit/1c991d0f0eac9270eb218b9ad672e36e8af74bc9)) +* **nargo:** Support custom entry points specified in TOML ([#2158](https://github.com/noir-lang/noir/issues/2158)) ([effb02a](https://github.com/noir-lang/noir/commit/effb02afc78f379d023719a0d869f42e7109b05f)) +* Only create new witnesses for distinctiveness when duplicates exist ([#2191](https://github.com/noir-lang/noir/issues/2191)) ([14cbdbc](https://github.com/noir-lang/noir/commit/14cbdbc1055ce7efe5d31bb02707c9a601ee7745)) +* open functions are unconstrained ([be44c7b](https://github.com/noir-lang/noir/commit/be44c7be172b93ebaf74719b870fc9cc3bc24105)) +* Optimize `x < 0` for unsigned `x` to false ([#2206](https://github.com/noir-lang/noir/issues/2206)) ([25bc969](https://github.com/noir-lang/noir/commit/25bc9698efee601f5d8d4531a1bece8e5dc293ab)) +* Optimize away constant calls to black box functions ([#1981](https://github.com/noir-lang/noir/issues/1981)) ([47b372c](https://github.com/noir-lang/noir/commit/47b372c1762ed1184bf2ed9b90d7dc3e2c161880)) +* Optimize equality checks between a boolean and constant ([#2201](https://github.com/noir-lang/noir/issues/2201)) ([478c026](https://github.com/noir-lang/noir/commit/478c0266cc267b942f7ff10d32fffdeb6affa140)) +* Optionally output a debug artifact on compile ([#2260](https://github.com/noir-lang/noir/issues/2260)) ([edded24](https://github.com/noir-lang/noir/commit/edded24d2256a074e8e390285e123e39f926551d)) +* Perform input validation on user's package names ([#2293](https://github.com/noir-lang/noir/issues/2293)) ([87174ac](https://github.com/noir-lang/noir/commit/87174ac4927c4e237a2d0dbd6290da309e9f70c0)) +* Perform sorting of constant arrays at compile time ([#2195](https://github.com/noir-lang/noir/issues/2195)) ([c46d7a0](https://github.com/noir-lang/noir/commit/c46d7a01ca49bb47548df6f3b2aa25d35aa43360)) +* Remove `comptime` and warn upon usage ([#2178](https://github.com/noir-lang/noir/issues/2178)) ([98d0de3](https://github.com/noir-lang/noir/commit/98d0de3814eb228f38c2985be99095e1db564065)) +* Remove an unnecessary witness in `mul_with_witness` ([#2078](https://github.com/noir-lang/noir/issues/2078)) ([9f3198e](https://github.com/noir-lang/noir/commit/9f3198efc77c308028f761175da4fe3659f70579)) +* replace boolean `AND`s with multiplication ([#1954](https://github.com/noir-lang/noir/issues/1954)) ([435ab35](https://github.com/noir-lang/noir/commit/435ab3520d06b6b4f898d41a5ad403c5ddbd7771)) +* **ssa:** Add additional BinaryOp simplifications ([#2124](https://github.com/noir-lang/noir/issues/2124)) ([50b2816](https://github.com/noir-lang/noir/commit/50b2816099a021e4b8cb44a9017fb849abf014e6)) +* Support `contract` package type in `nargo info` command ([#2249](https://github.com/noir-lang/noir/issues/2249)) ([d309cc0](https://github.com/noir-lang/noir/commit/d309cc0086df4c2a5697269ef9618cf026d323ff)) +* Support workspaces and package selection on every nargo command ([#1992](https://github.com/noir-lang/noir/issues/1992)) ([940b189](https://github.com/noir-lang/noir/commit/940b189d4fd47dad8cc9f2650162da9e99c5024c)) +* Update to ACVM 0.21.0 ([#2051](https://github.com/noir-lang/noir/issues/2051)) ([ad118eb](https://github.com/noir-lang/noir/commit/ad118eb8165ef83402e25b3001dfe27cf3a358b1)) + + +### Bug Fixes + +* Add foreign impl error ([#2216](https://github.com/noir-lang/noir/issues/2216)) ([a53f5ed](https://github.com/noir-lang/noir/commit/a53f5ed86ad9a372ecad8a0367f7af3a843aae56)) +* Avoid non-determinism in defunctionalization ([#2069](https://github.com/noir-lang/noir/issues/2069)) ([898a9fa](https://github.com/noir-lang/noir/commit/898a9fa3328b24334e5fac1a8ae8d43570652599)) +* avoid non-determinism in defunctionalize ([898a9fa](https://github.com/noir-lang/noir/commit/898a9fa3328b24334e5fac1a8ae8d43570652599)) +* avoid potential panic in `two_complement` ([#2081](https://github.com/noir-lang/noir/issues/2081)) ([63c4da0](https://github.com/noir-lang/noir/commit/63c4da0586e2575d6d14a3e537ccb64863a13f78)) +* Fix 3 parser test cases in parsing ([#2284](https://github.com/noir-lang/noir/issues/2284)) ([094aef1](https://github.com/noir-lang/noir/commit/094aef191a3eafeccba714823e43d8e73ede8f50)) +* fix an ICE happening when we call a closure result from if/else ([#2146](https://github.com/noir-lang/noir/issues/2146)) ([928b3ad](https://github.com/noir-lang/noir/commit/928b3ad5d93943960cc6f480b28bce25f29b3271)) +* Fix an ICE when reassigning a mutable lambda variable to one with a different environment type ([#2172](https://github.com/noir-lang/noir/issues/2172)) ([a56db3e](https://github.com/noir-lang/noir/commit/a56db3ec9b20de587735e2f002be5c355c6b6b83)) +* Fix assignment when both `mut` and `&mut` are used ([#2264](https://github.com/noir-lang/noir/issues/2264)) ([b07a7ff](https://github.com/noir-lang/noir/commit/b07a7ff90445afa1f173934367ffaecd0878777c)) +* Fix methods not mutating fields ([#2087](https://github.com/noir-lang/noir/issues/2087)) ([6acc242](https://github.com/noir-lang/noir/commit/6acc242bae48aee7e1de013ceadb6587dc900296)) +* flattening pass no longer overwrites previously mapped condition values ([#2117](https://github.com/noir-lang/noir/issues/2117)) ([f7742ab](https://github.com/noir-lang/noir/commit/f7742ab026092f129bd4ec4f122bcd3249100529)) +* **globals:** Accurately filter literals for resolving globals ([#2126](https://github.com/noir-lang/noir/issues/2126)) ([1c21d0c](https://github.com/noir-lang/noir/commit/1c21d0caf1e3b3a92266b4b8238f3e6e6c394d05)) +* Implement `.len()` in Acir-Gen ([#2077](https://github.com/noir-lang/noir/issues/2077)) ([ab61e3a](https://github.com/noir-lang/noir/commit/ab61e3ab70aa0f7a037e0ad4a430975f50266097)) +* Implement slices of structs ([#2150](https://github.com/noir-lang/noir/issues/2150)) ([6abcb79](https://github.com/noir-lang/noir/commit/6abcb792e510454896d032cea5017bd43ef8cfc3)) +* Initialize numeric generics' type to a polymorphic integer when used in an expression ([#2179](https://github.com/noir-lang/noir/issues/2179)) ([c74b228](https://github.com/noir-lang/noir/commit/c74b22850ef0a530d0a3327c2bb3a8a05bd43bbb)) +* **lsp:** Ensure lsp does not crawl past the root specified ([#2322](https://github.com/noir-lang/noir/issues/2322)) ([d69e372](https://github.com/noir-lang/noir/commit/d69e3728a22a31a7d170bf383ac9e65cc4cf61cc)) +* **lsp:** Improve dependency resolution in context of `Nargo.toml` ([#2226](https://github.com/noir-lang/noir/issues/2226)) ([8846bf2](https://github.com/noir-lang/noir/commit/8846bf23364b6fdcb4e79171dffedddad5df91b6)) +* **lsp:** Pass `--program-dir` to test command from codelens ([#2292](https://github.com/noir-lang/noir/issues/2292)) ([92e1802](https://github.com/noir-lang/noir/commit/92e1802979e5713ec4287d8932e4675c95439861)) +* Mutating a variable no longer mutates its copy ([#2057](https://github.com/noir-lang/noir/issues/2057)) ([e85e485](https://github.com/noir-lang/noir/commit/e85e4850546552b7240466031e770c2667280444)) +* **nargo:** Allow `--program-dir` flag anywhere in a command ([#2290](https://github.com/noir-lang/noir/issues/2290)) ([7834fce](https://github.com/noir-lang/noir/commit/7834fcee0bda8f72d97a65964605fd82742ea75f)) +* **nargo:** Indicate which TOML file is missing package name ([#2177](https://github.com/noir-lang/noir/issues/2177)) ([9529157](https://github.com/noir-lang/noir/commit/9529157bd759d1ce1f632b732d76a58417ddfb51)) +* **nargo:** Make dependencies section optional in TOML ([#2161](https://github.com/noir-lang/noir/issues/2161)) ([099f4d4](https://github.com/noir-lang/noir/commit/099f4d421e86c471343693d29e77beb1fb912a33)) +* **nargo:** Remove `-p` short flag from the `--program-dir` flag ([#2300](https://github.com/noir-lang/noir/issues/2300)) ([cc2af74](https://github.com/noir-lang/noir/commit/cc2af74e586bbbba0c45aa0b7c9f9a9e6480f851)) +* Open contract functions are unconstrained ([#2052](https://github.com/noir-lang/noir/issues/2052)) ([be44c7b](https://github.com/noir-lang/noir/commit/be44c7be172b93ebaf74719b870fc9cc3bc24105)) +* optimize contracts built by `nargo info` ([b30b3f4](https://github.com/noir-lang/noir/commit/b30b3f438e8ed6953f2fec9c610619ac4fb17553)) +* Optimize contracts built by `nargo info` ([#2259](https://github.com/noir-lang/noir/issues/2259)) ([b30b3f4](https://github.com/noir-lang/noir/commit/b30b3f438e8ed6953f2fec9c610619ac4fb17553)) +* Overflowing assignment will result in an error ([#2321](https://github.com/noir-lang/noir/issues/2321)) ([bc645fc](https://github.com/noir-lang/noir/commit/bc645fcebb42858984ee0e7df560e40b56438512)) +* Prevent panic when passing relative paths to `--program-dir` ([#2324](https://github.com/noir-lang/noir/issues/2324)) ([9eb45da](https://github.com/noir-lang/noir/commit/9eb45dafc7bef8e1714235e95d9e703c2b8c3c3b)) +* properly capture lvalues in closure environments ([#2120](https://github.com/noir-lang/noir/issues/2120)) ([#2257](https://github.com/noir-lang/noir/issues/2257)) ([ed5273c](https://github.com/noir-lang/noir/commit/ed5273c827c5556f1b92e5ed8b628a0be77870be)) +* remove duplicated `name` option in `nargo new` ([#2183](https://github.com/noir-lang/noir/issues/2183)) ([68f5887](https://github.com/noir-lang/noir/commit/68f5887f9083e8194a9252d09ee0af363ffffa03)) +* Remove last vestige of array of structs to struct of arrays conversion ([#2217](https://github.com/noir-lang/noir/issues/2217)) ([34be264](https://github.com/noir-lang/noir/commit/34be264c0c112e9d0139654eabe4840f35535c1e)) +* Rename `Option::value` to `Option::_value` ([#2127](https://github.com/noir-lang/noir/issues/2127)) ([8a1ace7](https://github.com/noir-lang/noir/commit/8a1ace792c4550ab1ce8c6044794abdb39d02872)) +* Require package names to be non-empty ([#2293](https://github.com/noir-lang/noir/issues/2293)) ([87174ac](https://github.com/noir-lang/noir/commit/87174ac4927c4e237a2d0dbd6290da309e9f70c0)) +* Set location before cast instructions in SSA ([#2202](https://github.com/noir-lang/noir/issues/2202)) ([a72cc96](https://github.com/noir-lang/noir/commit/a72cc96e7535f3b85db005f2b09014488933b4df)) +* simplification of overflowing integer operations ([#2153](https://github.com/noir-lang/noir/issues/2153)) ([4a5d2de](https://github.com/noir-lang/noir/commit/4a5d2de23af112b9cb794a2e86caf313f860f8d3)) +* **stdlib:** correct `tecurve::contains` formula ([#1821](https://github.com/noir-lang/noir/issues/1821)) ([6a10ecf](https://github.com/noir-lang/noir/commit/6a10ecf829a5c228b1e1e8a3e9ded886e53cad48)) + + +### Miscellaneous Chores + +* **nargo:** remove `flat_witness` feature flag ([#2208](https://github.com/noir-lang/noir/issues/2208)) ([32d52d3](https://github.com/noir-lang/noir/commit/32d52d36052b954b777e918d2cd67d056dd04232)) +* **nargo:** Remove unused flags on LSP command ([#2170](https://github.com/noir-lang/noir/issues/2170)) ([ccba78e](https://github.com/noir-lang/noir/commit/ccba78e330463ea9eee00f745e0b489379059bd9)) +* **nargo:** Rename nargo gates to nargo info ([#2038](https://github.com/noir-lang/noir/issues/2038)) ([5907e96](https://github.com/noir-lang/noir/commit/5907e96b8dded6eb3a68d5b9e167b055f65bf783)) +* **nargo:** Require package names in Nargo.toml files ([#2056](https://github.com/noir-lang/noir/issues/2056)) ([bb28223](https://github.com/noir-lang/noir/commit/bb282232aec7b0b9dae08a062b586e4564036123)) + ## [0.9.0](https://github.com/noir-lang/noir/compare/v0.8.0...v0.9.0) (2023-07-25) diff --git a/Cargo.lock b/Cargo.lock index 7535d132a96..5e99a3d3550 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,6 +124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", + "getrandom", "once_cell", "version_check", ] @@ -152,6 +153,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "anstream" version = "0.3.2" @@ -203,7 +210,7 @@ dependencies = [ [[package]] name = "arena" -version = "0.9.0" +version = "0.10.1" dependencies = [ "generational-arena", ] @@ -325,6 +332,12 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "assert_cmd" version = "2.0.12" @@ -593,6 +606,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + [[package]] name = "bytes" version = "1.4.0" @@ -605,6 +624,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38fcc2979eff34a4b84e1cf9a1e3da42a7d44b3b690a40cdcb23e3d556cfb2e5" +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.0.79" @@ -650,6 +675,33 @@ dependencies = [ "stacker", ] +[[package]] +name = "ciborium" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" + +[[package]] +name = "ciborium-ll" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "clang-sys" version = "1.6.1" @@ -822,6 +874,15 @@ dependencies = [ "windows-sys 0.33.0", ] +[[package]] +name = "cpp_demangle" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee34052ee3d93d6d8f3e6f81d85c47921f6653a19a7b70e939e3e602d893a674" +dependencies = [ + "cfg-if", +] + [[package]] name = "cpufeatures" version = "0.2.9" @@ -899,6 +960,42 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + [[package]] name = "crossbeam-channel" version = "0.5.8" @@ -964,6 +1061,27 @@ dependencies = [ "typenum", ] +[[package]] +name = "csv" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + [[package]] name = "darling" version = "0.20.3" @@ -998,6 +1116,15 @@ dependencies = [ "syn 2.0.26", ] +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] + [[package]] name = "der" version = "0.6.1" @@ -1045,6 +1172,16 @@ dependencies = [ "dirs-sys", ] +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + [[package]] name = "dirs-sys" version = "0.3.7" @@ -1056,6 +1193,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -1100,6 +1248,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "encoding_rs" version = "0.8.32" @@ -1212,6 +1366,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + [[package]] name = "flate2" version = "1.0.26" @@ -1233,7 +1399,7 @@ dependencies = [ [[package]] name = "fm" -version = "0.9.0" +version = "0.10.1" dependencies = [ "cfg-if", "codespan-reporting", @@ -1470,6 +1636,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + [[package]] name = "hashbrown" version = "0.11.2" @@ -1602,6 +1774,12 @@ dependencies = [ "tokio-rustls", ] +[[package]] +name = "iai" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71a816c97c42258aa5834d07590b718b4c9a598944cd39a52dc25b351185d678" + [[package]] name = "iana-time-zone" version = "0.1.57" @@ -1699,6 +1877,24 @@ dependencies = [ "hashbrown 0.14.0", ] +[[package]] +name = "inferno" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fb7c1b80a1dfa604bb4a649a5c5aeef3d913f7c520cb42b40e534e8a61bcdfc" +dependencies = [ + "ahash 0.8.3", + "indexmap 1.9.3", + "is-terminal", + "itoa", + "log", + "num-format", + "once_cell", + "quick-xml", + "rgb", + "str_stack", +] + [[package]] name = "instant" version = "0.1.12" @@ -1738,7 +1934,7 @@ dependencies = [ [[package]] name = "iter-extended" -version = "0.9.0" +version = "0.10.1" [[package]] name = "itertools" @@ -1840,6 +2036,16 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.19" @@ -1975,7 +2181,7 @@ checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" [[package]] name = "nargo" -version = "0.9.0" +version = "0.10.1" dependencies = [ "acvm", "base64", @@ -1994,7 +2200,7 @@ dependencies = [ [[package]] name = "nargo_cli" -version = "0.9.0" +version = "0.10.1" dependencies = [ "acvm", "acvm-backend-barretenberg", @@ -2005,9 +2211,11 @@ dependencies = [ "clap", "color-eyre", "const_format", + "criterion", "dirs", "fm", "hex", + "iai", "iter-extended", "nargo", "nargo_toml", @@ -2016,7 +2224,10 @@ dependencies = [ "noirc_driver", "noirc_errors", "noirc_frontend", + "paste", + "pprof", "predicates 2.1.5", + "prettytable-rs", "rustc_version", "serde", "serde_json", @@ -2031,7 +2242,7 @@ dependencies = [ [[package]] name = "nargo_toml" -version = "0.9.0" +version = "0.10.1" dependencies = [ "dirs", "fm", @@ -2043,9 +2254,21 @@ dependencies = [ "url", ] +[[package]] +name = "nix" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "static_assertions", +] + [[package]] name = "noir_lsp" -version = "0.9.0" +version = "0.10.1" dependencies = [ "acvm", "async-lsp", @@ -2066,7 +2289,7 @@ dependencies = [ [[package]] name = "noir_wasm" -version = "0.9.0" +version = "0.10.1" dependencies = [ "acvm", "build-data", @@ -2084,7 +2307,7 @@ dependencies = [ [[package]] name = "noirc_abi" -version = "0.9.0" +version = "0.10.1" dependencies = [ "acvm", "iter-extended", @@ -2098,7 +2321,7 @@ dependencies = [ [[package]] name = "noirc_driver" -version = "0.9.0" +version = "0.10.1" dependencies = [ "acvm", "base64", @@ -2113,7 +2336,7 @@ dependencies = [ [[package]] name = "noirc_errors" -version = "0.9.0" +version = "0.10.1" dependencies = [ "chumsky", "codespan", @@ -2124,7 +2347,7 @@ dependencies = [ [[package]] name = "noirc_evaluator" -version = "0.9.0" +version = "0.10.1" dependencies = [ "acvm", "im", @@ -2138,7 +2361,7 @@ dependencies = [ [[package]] name = "noirc_frontend" -version = "0.9.0" +version = "0.10.1" dependencies = [ "acvm", "arena", @@ -2185,6 +2408,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec", + "itoa", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -2241,6 +2474,12 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + [[package]] name = "owo-colors" version = "3.5.0" @@ -2258,6 +2497,29 @@ dependencies = [ "sha2", ] +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.3.5", + "smallvec", + "windows-targets", +] + [[package]] name = "paste" version = "1.0.14" @@ -2304,6 +2566,56 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "pprof" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978385d59daf9269189d052ca8a84c1acfd0715c0599a5d5188d4acc078ca46a" +dependencies = [ + "backtrace", + "cfg-if", + "criterion", + "findshlibs", + "inferno", + "libc", + "log", + "nix", + "once_cell", + "parking_lot", + "smallvec", + "symbolic-demangle", + "tempfile", + "thiserror", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2352,6 +2664,20 @@ dependencies = [ "termtree", ] +[[package]] +name = "prettytable-rs" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" +dependencies = [ + "csv", + "encode_unicode", + "is-terminal", + "lazy_static", + "term", + "unicode-width", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -2414,6 +2740,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "quick-xml" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.31" @@ -2675,6 +3010,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rgb" +version = "0.8.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" version = "0.16.20" @@ -3179,6 +3523,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "str_stack" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" + [[package]] name = "strsim" version = "0.10.0" @@ -3210,6 +3566,29 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "symbolic-common" +version = "12.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167a4ffd7c35c143fd1030aa3c2caf76ba42220bd5a6b5f4781896434723b8c3" +dependencies = [ + "debugid", + "memmap2", + "stable_deref_trait", + "uuid", +] + +[[package]] +name = "symbolic-demangle" +version = "12.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e378c50e80686c1c5c205674e1f86a2858bec3d2a7dfdd690331a8a19330f293" +dependencies = [ + "cpp_demangle", + "rustc-demangle", + "symbolic-common", +] + [[package]] name = "syn" version = "1.0.109" @@ -3268,6 +3647,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + [[package]] name = "termcolor" version = "1.2.0" @@ -3324,6 +3714,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tinyvec" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index b7dc03f87ac..4ed4c31c535 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ default-members = ["crates/nargo_cli"] [workspace.package] # x-release-please-start-version -version = "0.9.0" +version = "0.10.1" # x-release-please-end authors = ["The Noir Team "] edition = "2021" diff --git a/crates/fm/src/file_map.rs b/crates/fm/src/file_map.rs index 8bbfcf99dd7..22ac6d4e179 100644 --- a/crates/fm/src/file_map.rs +++ b/crates/fm/src/file_map.rs @@ -34,7 +34,9 @@ impl From<&PathBuf> for PathString { pub struct FileMap(SimpleFiles); // XXX: Note that we derive Default here due to ModuleOrigin requiring us to set a FileId -#[derive(Default, Debug, Clone, PartialEq, Eq, Copy, Hash, Serialize, Deserialize)] +#[derive( + Default, Debug, Clone, PartialEq, Eq, Copy, Hash, Serialize, Deserialize, PartialOrd, Ord, +)] pub struct FileId(usize); impl FileId { diff --git a/crates/fm/src/lib.rs b/crates/fm/src/lib.rs index 4c2ce39dd40..ef976a80dbd 100644 --- a/crates/fm/src/lib.rs +++ b/crates/fm/src/lib.rs @@ -70,7 +70,7 @@ impl FileManager { assert!(old_value.is_none(), "ice: the same path was inserted into the file manager twice"); } - pub fn fetch_file(&mut self, file_id: FileId) -> File { + pub fn fetch_file(&self, file_id: FileId) -> File { // Unwrap as we ensure that all file_id's map to a corresponding file in the file map self.file_map.get_file(file_id).unwrap() } diff --git a/crates/lsp/src/lib.rs b/crates/lsp/src/lib.rs index b0b0871ad3f..1fe0565da40 100644 --- a/crates/lsp/src/lib.rs +++ b/crates/lsp/src/lib.rs @@ -1,6 +1,7 @@ use std::{ future::{self, Future}, ops::{self, ControlFlow}, + path::PathBuf, pin::Pin, task::{self, Poll}, }; @@ -19,9 +20,10 @@ use lsp_types::{ PublishDiagnosticsParams, Range, ServerCapabilities, TextDocumentSyncOptions, }; use nargo::prepare_package; -use nargo_toml::{find_package_manifest, resolve_workspace_from_toml}; +use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::check_crate; use noirc_errors::{DiagnosticKind, FileDiagnostic}; +use noirc_frontend::hir::FunctionNameMatch; use serde_json::Value as JsonValue; use tower::Service; @@ -30,12 +32,13 @@ const TEST_CODELENS_TITLE: &str = "▶\u{fe0e} Run Test"; // State for the LSP gets implemented on this struct and is internal to the implementation pub struct LspState { + root_path: Option, client: ClientSocket, } impl LspState { fn new(client: &ClientSocket) -> Self { - Self { client: client.clone() } + Self { client: client.clone(), root_path: None } } } @@ -101,9 +104,11 @@ impl LspService for NargoLspService { // and params passed in. fn on_initialize( - _state: &mut LspState, - _params: InitializeParams, + state: &mut LspState, + params: InitializeParams, ) -> impl Future> { + state.root_path = params.root_uri.and_then(|root_uri| root_uri.to_file_path().ok()); + async { let text_document_sync = TextDocumentSyncOptions { save: Some(true.into()), ..Default::default() }; @@ -143,7 +148,17 @@ fn on_code_lens_request( } }; - let toml_path = match find_package_manifest(&file_path) { + let root_path = match &state.root_path { + Some(root) => root, + None => { + return future::ready(Err(ResponseError::new( + ErrorCode::REQUEST_FAILED, + "Could not find project root", + ))) + } + }; + + let toml_path = match find_package_manifest(root_path, &file_path) { Ok(toml_path) => toml_path, Err(err) => { // If we cannot find a manifest, we log a warning but return no code lenses @@ -155,7 +170,7 @@ fn on_code_lens_request( return future::ready(Ok(None)); } }; - let workspace = match resolve_workspace_from_toml(&toml_path, None) { + let workspace = match resolve_workspace_from_toml(&toml_path, PackageSelection::All) { Ok(workspace) => workspace, Err(err) => { // If we found a manifest, but the workspace is invalid, we raise an error about it @@ -176,7 +191,8 @@ fn on_code_lens_request( let fm = &context.file_manager; let files = fm.as_simple_files(); - let tests = context.get_all_test_functions_in_crate_matching(&crate_id, ""); + let tests = context + .get_all_test_functions_in_crate_matching(&crate_id, FunctionNameMatch::Anything); for (func_name, func_id) in tests { let location = context.function_meta(&func_id).name.location; @@ -196,7 +212,14 @@ fn on_code_lens_request( let command = Command { title: TEST_CODELENS_TITLE.into(), command: TEST_COMMAND.into(), - arguments: Some(vec![func_name.into()]), + arguments: Some(vec![ + "--program-dir".into(), + format!("{}", workspace.root_dir.display()).into(), + "--package".into(), + format!("{}", package.name).into(), + "--exact".into(), + func_name.into(), + ]), }; let lens = CodeLens { range, command: command.into(), data: None }; @@ -260,7 +283,18 @@ fn on_did_save_text_document( } }; - let toml_path = match find_package_manifest(&file_path) { + let root_path = match &state.root_path { + Some(root) => root, + None => { + return ControlFlow::Break(Err(ResponseError::new( + ErrorCode::REQUEST_FAILED, + "Could not find project root", + ) + .into())); + } + }; + + let toml_path = match find_package_manifest(root_path, &file_path) { Ok(toml_path) => toml_path, Err(err) => { // If we cannot find a manifest, we log a warning but return no diagnostics @@ -272,7 +306,7 @@ fn on_did_save_text_document( return ControlFlow::Continue(()); } }; - let workspace = match resolve_workspace_from_toml(&toml_path, None) { + let workspace = match resolve_workspace_from_toml(&toml_path, PackageSelection::All) { Ok(workspace) => workspace, Err(err) => { // If we found a manifest, but the workspace is invalid, we raise an error about it @@ -298,7 +332,7 @@ fn on_did_save_text_document( let fm = &context.file_manager; let files = fm.as_simple_files(); - for FileDiagnostic { file_id, diagnostic } in file_diagnostics { + for FileDiagnostic { file_id, diagnostic, call_stack: _ } in file_diagnostics { // Ignore diagnostics for any file that wasn't the file we saved // TODO: In the future, we could create "related" diagnostics for these files // TODO: This currently just appends the `.nr` file extension that we store as a constant, diff --git a/crates/nargo/src/artifacts/debug.rs b/crates/nargo/src/artifacts/debug.rs new file mode 100644 index 00000000000..b6eefd17189 --- /dev/null +++ b/crates/nargo/src/artifacts/debug.rs @@ -0,0 +1,55 @@ +use serde::{Deserialize, Serialize}; +use std::{ + collections::{BTreeMap, BTreeSet}, + path::PathBuf, +}; + +use fm::FileId; +use noirc_errors::debug_info::DebugInfo; +use noirc_frontend::hir::Context; + +/// For a given file, we store the source code and the path to the file +/// so consumers of the debug artifact can reconstruct the original source code structure. +#[derive(Debug, Serialize, Deserialize)] +pub struct DebugFile { + pub source: String, + pub path: PathBuf, +} + +/// A Debug Artifact stores, for a given program, the debug info for every function +/// along with a map of file Id to the source code so locations in debug info can be mapped to source code they point to. +#[derive(Debug, Serialize, Deserialize)] +pub struct DebugArtifact { + pub debug_symbols: Vec, + pub file_map: BTreeMap, +} + +impl DebugArtifact { + pub fn new(debug_symbols: Vec, compilation_context: &Context) -> Self { + let mut file_map = BTreeMap::new(); + + let files_with_debug_symbols: BTreeSet = debug_symbols + .iter() + .flat_map(|function_symbols| { + function_symbols + .locations + .values() + .filter_map(|call_stack| call_stack.last().map(|location| location.file)) + }) + .collect(); + + for file_id in files_with_debug_symbols { + let file_source = compilation_context.file_manager.fetch_file(file_id).source(); + + file_map.insert( + file_id, + DebugFile { + source: file_source.to_string(), + path: compilation_context.file_manager.path(file_id).to_path_buf(), + }, + ); + } + + Self { debug_symbols, file_map } + } +} diff --git a/crates/nargo/src/artifacts/mod.rs b/crates/nargo/src/artifacts/mod.rs index 1ba12c49af7..33311e0856e 100644 --- a/crates/nargo/src/artifacts/mod.rs +++ b/crates/nargo/src/artifacts/mod.rs @@ -8,6 +8,7 @@ use base64::Engine; use serde::{Deserializer, Serializer}; pub mod contract; +pub mod debug; pub mod program; // TODO: move these down into ACVM. diff --git a/crates/nargo/src/ops/preprocess.rs b/crates/nargo/src/ops/preprocess.rs index d07da256ede..0ee4e2590f9 100644 --- a/crates/nargo/src/ops/preprocess.rs +++ b/crates/nargo/src/ops/preprocess.rs @@ -42,7 +42,7 @@ pub fn preprocess_contract_function( include_keys: bool, common_reference_string: &[u8], func: ContractFunction, -) -> Result { +) -> Result<(PreprocessedContractFunction, DebugInfo), B::Error> { // TODO: currently `func`'s bytecode is already optimized for the backend. // In future we'll need to apply those optimizations here. let optimized_bytecode = func.bytecode; @@ -54,14 +54,17 @@ pub fn preprocess_contract_function( (None, None) }; - Ok(PreprocessedContractFunction { - name: func.name, - function_type: func.function_type, - is_internal: func.is_internal, - abi: func.abi, + Ok(( + PreprocessedContractFunction { + name: func.name, + function_type: func.function_type, + is_internal: func.is_internal, + abi: func.abi, - bytecode: optimized_bytecode, - proving_key, - verification_key, - }) + bytecode: optimized_bytecode, + proving_key, + verification_key, + }, + func.debug, + )) } diff --git a/crates/nargo_cli/Cargo.toml b/crates/nargo_cli/Cargo.toml index fd052e9dc95..13205c00022 100644 --- a/crates/nargo_cli/Cargo.toml +++ b/crates/nargo_cli/Cargo.toml @@ -32,13 +32,14 @@ acvm.workspace = true toml.workspace = true serde.workspace = true serde_json.workspace = true +prettytable-rs = "0.10" thiserror.workspace = true tower.workspace = true async-lsp = { version = "0.0.5", default-features = false, features = [ "client-monitor", "stdio", "tracing", - "tokio" + "tokio", ] } const_format = "0.2.30" hex = "0.4.2" @@ -56,10 +57,25 @@ assert_cmd = "2.0.8" assert_fs = "1.0.10" predicates = "2.1.5" fm.workspace = true +criterion = "0.5.0" +paste = "1.0.14" +pprof = { version = "0.12", features = [ + "flamegraph", + "frame-pointer", + "criterion", +] } +iai = "0.1.1" + +[[bench]] +name = "criterion" +harness = false + +[[bench]] +name = "iai" +harness = false [features] default = ["plonk_bn254"] # The plonk backend can only use bn254, so we do not specify the field plonk_bn254 = ["acvm-backend-barretenberg/native"] plonk_bn254_wasm = ["acvm-backend-barretenberg/wasm"] - diff --git a/crates/nargo_cli/benches/criterion.rs b/crates/nargo_cli/benches/criterion.rs new file mode 100644 index 00000000000..a7b094fd7aa --- /dev/null +++ b/crates/nargo_cli/benches/criterion.rs @@ -0,0 +1,33 @@ +//! Select representative tests to bench with criterion +use assert_cmd::prelude::{CommandCargoExt, OutputAssertExt}; +use criterion::{criterion_group, criterion_main, Criterion}; +use paste::paste; +use pprof::criterion::{Output, PProfProfiler}; +use std::process::Command; +include!("./utils.rs"); + +macro_rules! criterion_command { + ($command_name:tt, $command_string:expr) => { + paste! { + fn [](c: &mut Criterion) { + let test_program_dirs = get_selected_tests(); + for test_program_dir in test_program_dirs { + let mut cmd = Command::cargo_bin("nargo").unwrap(); + cmd.arg("--program-dir").arg(&test_program_dir); + cmd.arg($command_string); + + c.bench_function(&format!("{}_{}", test_program_dir.file_name().unwrap().to_str().unwrap(), $command_string), |b| { + b.iter(|| cmd.assert()) + }); + } + } + } + }; +} +criterion_command!(execution, "execute"); +criterion_group! { + name = benches; + config = Criterion::default().sample_size(20).with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); + targets = criterion_selected_tests_execution +} +criterion_main!(benches); diff --git a/crates/nargo_cli/benches/iai.rs b/crates/nargo_cli/benches/iai.rs new file mode 100644 index 00000000000..bcd60111ccf --- /dev/null +++ b/crates/nargo_cli/benches/iai.rs @@ -0,0 +1,25 @@ +//! Select representative tests to bench with iai +use assert_cmd::prelude::{CommandCargoExt, OutputAssertExt}; +use iai::black_box; +use paste::paste; +use std::process::Command; +include!("./utils.rs"); + +macro_rules! iai_command { + ($command_name:tt, $command_string:expr) => { + paste! { + fn []() { + let test_program_dirs = get_selected_tests(); + for test_program_dir in test_program_dirs { + let mut cmd = Command::cargo_bin("nargo").unwrap(); + cmd.arg("--program-dir").arg(&test_program_dir); + cmd.arg($command_string); + + black_box(cmd.assert()); + } + } + } + }; +} +iai_command!(execution, "execute"); +iai::main!(iai_selected_tests_execution); diff --git a/crates/nargo_cli/benches/utils.rs b/crates/nargo_cli/benches/utils.rs new file mode 100644 index 00000000000..52a6b718c44 --- /dev/null +++ b/crates/nargo_cli/benches/utils.rs @@ -0,0 +1,14 @@ +use std::path::PathBuf; + +#[allow(unused)] +fn get_selected_tests() -> Vec { + let manifest_dir = match std::env::var("CARGO_MANIFEST_DIR") { + Ok(dir) => PathBuf::from(dir), + Err(_) => std::env::current_dir().unwrap().join("crates").join("nargo_cli"), + }; + let test_dir = manifest_dir.join("tests").join("execution_success"); + + let selected_tests = + vec!["8_integration", "sha256_blocks", "struct", "eddsa", "regression", "regression_2099"]; + selected_tests.into_iter().map(|t| test_dir.join(t)).collect() +} diff --git a/crates/nargo_cli/build.rs b/crates/nargo_cli/build.rs index 6d8dae136e2..2a3b80b3ab6 100644 --- a/crates/nargo_cli/build.rs +++ b/crates/nargo_cli/build.rs @@ -40,7 +40,7 @@ fn main() { let test_dir = manifest_dir.join("tests"); generate_execution_success_tests(&mut test_file, &test_dir); - generate_compile_success_tests(&mut test_file, &test_dir); + generate_compile_success_empty_tests(&mut test_file, &test_dir); generate_compile_failure_tests(&mut test_file, &test_dir); } @@ -81,8 +81,8 @@ fn execution_success_{test_name}() {{ } } -fn generate_compile_success_tests(test_file: &mut File, test_data_dir: &Path) { - let test_sub_dir = "compile_success"; +fn generate_compile_success_empty_tests(test_file: &mut File, test_data_dir: &Path) { + let test_sub_dir = "compile_success_empty"; let test_data_dir = test_data_dir.join(test_sub_dir); let test_case_dirs = @@ -102,15 +102,19 @@ fn generate_compile_success_tests(test_file: &mut File, test_data_dir: &Path) { test_file, r#" #[test] -fn compile_success_{test_name}() {{ +fn compile_success_empty_{test_name}() {{ let test_program_dir = PathBuf::from("{test_dir}"); let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.arg("--program-dir").arg(test_program_dir); cmd.arg("info"); - // `compile_success` tests should be able to compile down to an empty circuit. - cmd.assert().stdout(predicate::str::contains("Total ACIR opcodes generated for language PLONKCSat {{ width: 3 }}: 0")); + // `compile_success_empty` tests should be able to compile down to an empty circuit. + cmd.assert().stdout(predicate::str::contains("| Package") + .and(predicate::str::contains("| Language")) + .and(predicate::str::contains("| ACIR Opcodes | Backend Circuit Size |")) + .and(predicate::str::contains("| PLONKCSat {{ width: 3 }} |")) + .and(predicate::str::contains("| 0 | 1 |"))); }} "#, test_dir = test_dir.display(), diff --git a/crates/nargo_cli/src/cli/check_cmd.rs b/crates/nargo_cli/src/cli/check_cmd.rs index 432e0b9599c..cd61832dabc 100644 --- a/crates/nargo_cli/src/cli/check_cmd.rs +++ b/crates/nargo_cli/src/cli/check_cmd.rs @@ -3,7 +3,7 @@ use acvm::Backend; use clap::Args; use iter_extended::btree_map; use nargo::{package::Package, prepare_package}; -use nargo_toml::{find_package_manifest, resolve_workspace_from_toml}; +use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_abi::{AbiParameter, AbiType, MAIN_RETURN_NAME}; use noirc_driver::{check_crate, compute_function_signature, CompileOptions}; use noirc_frontend::{ @@ -18,9 +18,13 @@ use super::NargoConfig; #[derive(Debug, Clone, Args)] pub(crate) struct CheckCommand { /// The name of the package to check - #[clap(long)] + #[clap(long, conflicts_with = "workspace")] package: Option, + /// Check all packages in the workspace + #[clap(long, conflicts_with = "package")] + workspace: bool, + #[clap(flatten)] compile_options: CompileOptions, } @@ -30,8 +34,11 @@ pub(crate) fn run( args: CheckCommand, config: NargoConfig, ) -> Result<(), CliError> { - let toml_path = find_package_manifest(&config.program_dir)?; - let workspace = resolve_workspace_from_toml(&toml_path, args.package)?; + let toml_path = get_package_manifest(&config.program_dir)?; + let default_selection = + if args.workspace { PackageSelection::All } else { PackageSelection::DefaultOrAll }; + let selection = args.package.map_or(default_selection, PackageSelection::Selected); + let workspace = resolve_workspace_from_toml(&toml_path, selection)?; for package in &workspace { check_package(package, &args.compile_options)?; @@ -88,7 +95,7 @@ fn create_input_toml_template( .collect(); toml::Value::Array(default_value_vec) } - AbiType::Struct { fields } => { + AbiType::Struct { fields, .. } => { let default_value_map = toml::map::Map::from_iter( fields.into_iter().map(|(name, typ)| (name, default_value(typ))), ); @@ -128,6 +135,7 @@ mod tests { typed_param( "d", AbiType::Struct { + name: String::from("MyStruct"), fields: vec![ (String::from("d1"), AbiType::Field), ( diff --git a/crates/nargo_cli/src/cli/codegen_verifier_cmd.rs b/crates/nargo_cli/src/cli/codegen_verifier_cmd.rs index bc7e1c80c0f..7662eaa8401 100644 --- a/crates/nargo_cli/src/cli/codegen_verifier_cmd.rs +++ b/crates/nargo_cli/src/cli/codegen_verifier_cmd.rs @@ -20,7 +20,7 @@ use nargo::{ ops::{codegen_verifier, preprocess_program}, package::Package, }; -use nargo_toml::{find_package_manifest, resolve_workspace_from_toml}; +use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::CompileOptions; use noirc_frontend::graph::CrateName; @@ -28,9 +28,13 @@ use noirc_frontend::graph::CrateName; #[derive(Debug, Clone, Args)] pub(crate) struct CodegenVerifierCommand { /// The name of the package to codegen - #[clap(long)] + #[clap(long, conflicts_with = "workspace")] package: Option, + /// Codegen all packages in the workspace + #[clap(long, conflicts_with = "package")] + workspace: bool, + #[clap(flatten)] compile_options: CompileOptions, } @@ -40,8 +44,11 @@ pub(crate) fn run( args: CodegenVerifierCommand, config: NargoConfig, ) -> Result<(), CliError> { - let toml_path = find_package_manifest(&config.program_dir)?; - let workspace = resolve_workspace_from_toml(&toml_path, args.package)?; + let toml_path = get_package_manifest(&config.program_dir)?; + let default_selection = + if args.workspace { PackageSelection::All } else { PackageSelection::DefaultOrAll }; + let selection = args.package.map_or(default_selection, PackageSelection::Selected); + let workspace = resolve_workspace_from_toml(&toml_path, selection)?; for package in &workspace { let circuit_build_path = workspace.package_build_path(package); diff --git a/crates/nargo_cli/src/cli/compile_cmd.rs b/crates/nargo_cli/src/cli/compile_cmd.rs index fad9783e654..8c09c248093 100644 --- a/crates/nargo_cli/src/cli/compile_cmd.rs +++ b/crates/nargo_cli/src/cli/compile_cmd.rs @@ -2,14 +2,16 @@ use acvm::acir::circuit::OpcodeLabel; use acvm::{acir::circuit::Circuit, Backend}; use iter_extended::try_vecmap; use iter_extended::vecmap; +use nargo::artifacts::debug::DebugArtifact; use nargo::package::Package; use nargo::prepare_package; use nargo::{artifacts::contract::PreprocessedContract, NargoError}; -use nargo_toml::{find_package_manifest, resolve_workspace_from_toml}; +use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{ compile_contracts, compile_main, CompileOptions, CompiledContract, CompiledProgram, - ContractFunction, ErrorsAndWarnings, Warnings, + ErrorsAndWarnings, Warnings, }; +use noirc_errors::debug_info::DebugInfo; use noirc_frontend::graph::CrateName; use noirc_frontend::hir::Context; @@ -19,6 +21,7 @@ use nargo::ops::{preprocess_contract_function, preprocess_program}; use crate::errors::{CliError, CompileError}; +use super::fs::program::save_debug_artifact_to_file; use super::fs::{ common_reference_string::{ read_cached_common_reference_string, update_common_reference_string, @@ -38,10 +41,18 @@ pub(crate) struct CompileCommand { #[arg(long)] include_keys: bool, + /// Output debug files + #[arg(long, hide = true)] + output_debug: bool, + /// The name of the package to compile - #[clap(long)] + #[clap(long, conflicts_with = "workspace")] package: Option, + /// Compile all packages in the workspace + #[clap(long, conflicts_with = "package")] + workspace: bool, + #[clap(flatten)] compile_options: CompileOptions, } @@ -51,8 +62,11 @@ pub(crate) fn run( args: CompileCommand, config: NargoConfig, ) -> Result<(), CliError> { - let toml_path = find_package_manifest(&config.program_dir)?; - let workspace = resolve_workspace_from_toml(&toml_path, args.package)?; + let toml_path = get_package_manifest(&config.program_dir)?; + let default_selection = + if args.workspace { PackageSelection::All } else { PackageSelection::DefaultOrAll }; + let selection = args.package.map_or(default_selection, PackageSelection::Selected); + let workspace = resolve_workspace_from_toml(&toml_path, selection)?; let circuit_dir = workspace.target_directory_path(); let mut common_reference_string = read_cached_common_reference_string(); @@ -70,49 +84,72 @@ pub(crate) fn run( // As can be seen here, It seems like a leaky abstraction where ContractFunctions (essentially CompiledPrograms) // are compiled via nargo-core and then the PreprocessedContract is constructed here. // This is due to EACH function needing it's own CRS, PKey, and VKey from the backend. - let preprocessed_contracts: Result, CliError> = - try_vecmap(optimized_contracts, |contract| { - let preprocessed_contract_functions = try_vecmap(contract.functions, |func| { - common_reference_string = update_common_reference_string( - backend, - &common_reference_string, - &func.bytecode, - ) - .map_err(CliError::CommonReferenceStringError)?; - - preprocess_contract_function( - backend, - args.include_keys, - &common_reference_string, - func, - ) - .map_err(CliError::ProofSystemCompilerError) - })?; - - Ok(PreprocessedContract { + let preprocessed_contracts: Result< + Vec<(PreprocessedContract, Vec)>, + CliError, + > = try_vecmap(optimized_contracts, |contract| { + let preprocess_result = try_vecmap(contract.functions, |func| { + common_reference_string = update_common_reference_string( + backend, + &common_reference_string, + &func.bytecode, + ) + .map_err(CliError::CommonReferenceStringError)?; + + preprocess_contract_function( + backend, + args.include_keys, + &common_reference_string, + func, + ) + .map_err(CliError::ProofSystemCompilerError) + })?; + + let (preprocessed_contract_functions, debug_infos): (Vec<_>, Vec<_>) = + preprocess_result.into_iter().unzip(); + + Ok(( + PreprocessedContract { name: contract.name, backend: String::from(BACKEND_IDENTIFIER), functions: preprocessed_contract_functions, - }) - }); - for contract in preprocessed_contracts? { + }, + debug_infos, + )) + }); + for (contract, debug_infos) in preprocessed_contracts? { save_contract_to_file( &contract, &format!("{}-{}", package.name, contract.name), &circuit_dir, ); + + if args.output_debug { + let debug_artifact = DebugArtifact::new(debug_infos, &context); + save_debug_artifact_to_file( + &debug_artifact, + &format!("{}-{}", package.name, contract.name), + &circuit_dir, + ); + } } } else { - let (_, program) = compile_package(backend, package, &args.compile_options)?; + let (context, program) = compile_package(backend, package, &args.compile_options)?; common_reference_string = update_common_reference_string(backend, &common_reference_string, &program.circuit) .map_err(CliError::CommonReferenceStringError)?; - let (preprocessed_program, _) = + let (preprocessed_program, debug_info) = preprocess_program(backend, args.include_keys, &common_reference_string, program) .map_err(CliError::ProofSystemCompilerError)?; save_program_to_file(&preprocessed_program, &package.name, &circuit_dir); + + if args.output_debug { + let debug_artifact = DebugArtifact::new(vec![debug_info], &context); + let circuit_name: String = (&package.name).into(); + save_debug_artifact_to_file(&debug_artifact, &circuit_name, &circuit_dir); + } } } @@ -167,9 +204,18 @@ pub(super) fn optimize_contract( backend: &B, contract: CompiledContract, ) -> Result> { - let functions = try_vecmap(contract.functions, |func| { - let optimized_bytecode = optimize_circuit(backend, func.bytecode)?.0; - Ok::<_, CliError>(ContractFunction { bytecode: optimized_bytecode, ..func }) + let functions = try_vecmap(contract.functions, |mut func| { + let (optimized_bytecode, opcode_labels) = optimize_circuit(backend, func.bytecode)?; + let opcode_ids = vecmap(opcode_labels, |label| match label { + OpcodeLabel::Unresolved => { + unreachable!("Compiled circuit opcodes must resolve to some index") + } + OpcodeLabel::Resolved(index) => index as usize, + }); + + func.bytecode = optimized_bytecode; + func.debug.update_acir(opcode_ids); + Ok::<_, CliError>(func) })?; Ok(CompiledContract { functions, ..contract }) diff --git a/crates/nargo_cli/src/cli/execute_cmd.rs b/crates/nargo_cli/src/cli/execute_cmd.rs index 1f0095fed5a..169ddb3d937 100644 --- a/crates/nargo_cli/src/cli/execute_cmd.rs +++ b/crates/nargo_cli/src/cli/execute_cmd.rs @@ -5,7 +5,7 @@ use clap::Args; use nargo::constants::PROVER_INPUT_FILE; use nargo::package::Package; use nargo::NargoError; -use nargo_toml::{find_package_manifest, resolve_workspace_from_toml}; +use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_abi::input_parser::{Format, InputValue}; use noirc_abi::{Abi, InputMap}; use noirc_driver::{CompileOptions, CompiledProgram}; @@ -29,9 +29,13 @@ pub(crate) struct ExecuteCommand { prover_name: String, /// The name of the package to execute - #[clap(long)] + #[clap(long, conflicts_with = "workspace")] package: Option, + /// Execute all packages in the workspace + #[clap(long, conflicts_with = "package")] + workspace: bool, + #[clap(flatten)] compile_options: CompileOptions, } @@ -41,8 +45,11 @@ pub(crate) fn run( args: ExecuteCommand, config: NargoConfig, ) -> Result<(), CliError> { - let toml_path = find_package_manifest(&config.program_dir)?; - let workspace = resolve_workspace_from_toml(&toml_path, args.package)?; + let toml_path = get_package_manifest(&config.program_dir)?; + let default_selection = + if args.workspace { PackageSelection::All } else { PackageSelection::DefaultOrAll }; + let selection = args.package.map_or(default_selection, PackageSelection::Selected); + let workspace = resolve_workspace_from_toml(&toml_path, selection)?; let witness_dir = &workspace.target_directory_path(); for package in &workspace { @@ -101,23 +108,23 @@ fn extract_unsatisfied_constraint_from_nargo_error(nargo_err: &NargoError) -> Op _ => None, } } + fn report_unsatisfied_constraint_error( opcode_idx: Option, debug: &DebugInfo, context: &Context, ) { if let Some(opcode_index) = opcode_idx { - if let Some(loc) = debug.opcode_location(opcode_index) { - noirc_errors::reporter::report( - &context.file_manager, - &CustomDiagnostic::simple_error( - "Unsatisfied constraint".to_string(), - "Constraint failed".to_string(), - loc.span, - ), - Some(loc.file), - false, - ); + if let Some(locations) = debug.opcode_location(opcode_index) { + // The location of the error itself will be the location at the top + // of the call stack (the last item in the Vec). + if let Some(location) = locations.last() { + let message = "Failed constraint".into(); + CustomDiagnostic::simple_error(message, String::new(), location.span) + .in_file(location.file) + .with_call_stack(locations) + .report(&context.file_manager, false); + } } } } diff --git a/crates/nargo_cli/src/cli/fs/program.rs b/crates/nargo_cli/src/cli/fs/program.rs index 311923a6686..3f7107de667 100644 --- a/crates/nargo_cli/src/cli/fs/program.rs +++ b/crates/nargo_cli/src/cli/fs/program.rs @@ -1,6 +1,8 @@ use std::path::{Path, PathBuf}; -use nargo::artifacts::{contract::PreprocessedContract, program::PreprocessedProgram}; +use nargo::artifacts::{ + contract::PreprocessedContract, debug::DebugArtifact, program::PreprocessedProgram, +}; use noirc_frontend::graph::CrateName; use crate::errors::FilesystemError; @@ -15,6 +17,7 @@ pub(crate) fn save_program_to_file>( let circuit_name: String = crate_name.into(); save_build_artifact_to_file(compiled_program, &circuit_name, circuit_dir) } + pub(crate) fn save_contract_to_file>( compiled_contract: &PreprocessedContract, circuit_name: &str, @@ -22,13 +25,23 @@ pub(crate) fn save_contract_to_file>( ) -> PathBuf { save_build_artifact_to_file(compiled_contract, circuit_name, circuit_dir) } + +pub(crate) fn save_debug_artifact_to_file>( + debug_artifact: &DebugArtifact, + circuit_name: &str, + circuit_dir: P, +) -> PathBuf { + let artifact_name = format!("debug_{}", circuit_name); + save_build_artifact_to_file(debug_artifact, &artifact_name, circuit_dir) +} + fn save_build_artifact_to_file, T: ?Sized + serde::Serialize>( build_artifact: &T, - circuit_name: &str, + artifact_name: &str, circuit_dir: P, ) -> PathBuf { create_named_dir(circuit_dir.as_ref(), "target"); - let circuit_path = circuit_dir.as_ref().join(circuit_name).with_extension("json"); + let circuit_path = circuit_dir.as_ref().join(artifact_name).with_extension("json"); write_to_file(&serde_json::to_vec(build_artifact).unwrap(), &circuit_path); diff --git a/crates/nargo_cli/src/cli/info_cmd.rs b/crates/nargo_cli/src/cli/info_cmd.rs index d972aeb0511..a761c376973 100644 --- a/crates/nargo_cli/src/cli/info_cmd.rs +++ b/crates/nargo_cli/src/cli/info_cmd.rs @@ -2,9 +2,10 @@ use acvm::Backend; use clap::Args; use iter_extended::try_vecmap; use nargo::{package::Package, prepare_package}; -use nargo_toml::{find_package_manifest, resolve_workspace_from_toml}; +use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{compile_contracts, CompileOptions}; use noirc_frontend::graph::CrateName; +use prettytable::{row, Table}; use crate::{cli::compile_cmd::compile_package, errors::CliError}; @@ -21,9 +22,13 @@ use super::{ #[derive(Debug, Clone, Args)] pub(crate) struct InfoCommand { /// The name of the package to detail - #[clap(long)] + #[clap(long, conflicts_with = "workspace")] package: Option, + /// Detail all packages in the workspace + #[clap(long, conflicts_with = "package")] + workspace: bool, + #[clap(flatten)] compile_options: CompileOptions, } @@ -33,17 +38,50 @@ pub(crate) fn run( args: InfoCommand, config: NargoConfig, ) -> Result<(), CliError> { - let toml_path = find_package_manifest(&config.program_dir)?; - let workspace = resolve_workspace_from_toml(&toml_path, args.package)?; + let toml_path = get_package_manifest(&config.program_dir)?; + let default_selection = + if args.workspace { PackageSelection::All } else { PackageSelection::DefaultOrAll }; + let selection = args.package.map_or(default_selection, PackageSelection::Selected); + let workspace = resolve_workspace_from_toml(&toml_path, selection)?; + + let mut package_table = Table::new(); + package_table.add_row( + row![Fm->"Package", Fm->"Language", Fm->"ACIR Opcodes", Fm->"Backend Circuit Size"], + ); + let mut contract_table = Table::new(); + contract_table.add_row(row![ + Fm->"Contract", + Fm->"Function", + Fm->"Language", + Fm->"ACIR Opcodes", + Fm->"Backend Circuit Size" + ]); for package in &workspace { if package.is_contract() { - count_opcodes_and_gates_in_contracts(backend, package, &args.compile_options)?; + count_opcodes_and_gates_in_contracts( + backend, + package, + &args.compile_options, + &mut contract_table, + )?; } else { - count_opcodes_and_gates_in_package(backend, package, &args.compile_options)?; + count_opcodes_and_gates_in_package( + backend, + package, + &args.compile_options, + &mut package_table, + )?; } } + if package_table.len() > 1 { + package_table.printstd(); + } + if contract_table.len() > 1 { + contract_table.printstd(); + } + Ok(()) } @@ -51,22 +89,21 @@ fn count_opcodes_and_gates_in_package( backend: &B, package: &Package, compile_options: &CompileOptions, + table: &mut Table, ) -> Result<(), CliError> { let (_, compiled_program) = compile_package(backend, package, compile_options)?; let num_opcodes = compiled_program.circuit.opcodes.len(); - - println!( - "[{}] Total ACIR opcodes generated for language {:?}: {}", - package.name, - backend.np_language(), - num_opcodes - ); - let exact_circuit_size = backend .get_exact_circuit_size(&compiled_program.circuit) .map_err(CliError::ProofSystemCompilerError)?; - println!("[{}] Backend circuit size: {exact_circuit_size}", package.name); + + table.add_row(row![ + Fm->format!("{}", package.name), + format!("{:?}", backend.np_language()), + Fc->format!("{}", num_opcodes), + Fc->format!("{}", exact_circuit_size), + ]); Ok(()) } @@ -75,6 +112,7 @@ fn count_opcodes_and_gates_in_contracts( backend: &B, package: &Package, compile_options: &CompileOptions, + table: &mut Table, ) -> Result<(), CliError> { let (mut context, crate_id) = prepare_package(package); let result = compile_contracts(&mut context, crate_id, compile_options); @@ -92,8 +130,13 @@ fn count_opcodes_and_gates_in_contracts( .map_err(CliError::ProofSystemCompilerError)?; for info in function_info { - println!("[{}]({}) Total ACIR opcodes generated: {}", contract.name, info.0, info.1,); - println!("[{}]({}) Backend circuit size: {}", contract.name, info.0, info.2); + table.add_row(row![ + Fm->format!("{}", contract.name), + Fc->format!("{}", info.0), + format!("{:?}", backend.np_language()), + Fc->format!("{}", info.1), + Fc->format!("{}", info.2), + ]); } } diff --git a/crates/nargo_cli/src/cli/init_cmd.rs b/crates/nargo_cli/src/cli/init_cmd.rs index d6ad172ce26..e6020e3cfd9 100644 --- a/crates/nargo_cli/src/cli/init_cmd.rs +++ b/crates/nargo_cli/src/cli/init_cmd.rs @@ -6,6 +6,7 @@ use acvm::Backend; use clap::Args; use nargo::constants::{PKG_FILE, SRC_DIR}; use nargo::package::PackageType; +use noirc_frontend::graph::CrateName; use std::path::PathBuf; /// Create a Noir project in the current directory. @@ -13,7 +14,7 @@ use std::path::PathBuf; pub(crate) struct InitCommand { /// Name of the package [default: current directory name] #[clap(long)] - name: Option, + name: Option, /// Use a library template #[arg(long, conflicts_with = "bin", conflicts_with = "contract")] @@ -67,9 +68,13 @@ pub(crate) fn run( args: InitCommand, config: NargoConfig, ) -> Result<(), CliError> { - let package_name = args - .name - .unwrap_or_else(|| config.program_dir.file_name().unwrap().to_str().unwrap().to_owned()); + let package_name = match args.name { + Some(name) => name, + None => { + let name = config.program_dir.file_name().unwrap().to_str().unwrap(); + name.parse().map_err(|_| CliError::InvalidPackageName(name.into()))? + } + }; let package_type = if args.lib { PackageType::Library @@ -78,14 +83,14 @@ pub(crate) fn run( } else { PackageType::Binary }; - initialize_project(config.program_dir, &package_name, package_type); + initialize_project(config.program_dir, package_name, package_type); Ok(()) } /// Initializes a new Noir project in `package_dir`. pub(crate) fn initialize_project( package_dir: PathBuf, - package_name: &str, + package_name: CrateName, package_type: PackageType, ) { let src_dir = package_dir.join(SRC_DIR); diff --git a/crates/nargo_cli/src/cli/mod.rs b/crates/nargo_cli/src/cli/mod.rs index 2774d4cb6a4..2603db3ce19 100644 --- a/crates/nargo_cli/src/cli/mod.rs +++ b/crates/nargo_cli/src/cli/mod.rs @@ -39,7 +39,8 @@ struct NargoCli { #[non_exhaustive] #[derive(Args, Clone, Debug)] pub(crate) struct NargoConfig { - #[arg(short, long, hide=true, default_value_os_t = std::env::current_dir().unwrap())] + // REMINDER: Also change this flag in the LSP test lens if renamed + #[arg(long, hide = true, global = true, default_value = "./")] program_dir: PathBuf, } @@ -63,6 +64,11 @@ enum NargoCommand { pub(crate) fn start_cli() -> eyre::Result<()> { let NargoCli { command, mut config } = NargoCli::parse(); + // If the provided `program_dir` is relative, make it absolute by joining it to the current directory. + if !config.program_dir.is_absolute() { + config.program_dir = std::env::current_dir().unwrap().join(config.program_dir); + } + // Search through parent directories to find package root if necessary. if !matches!(command, NargoCommand::New(_) | NargoCommand::Init(_) | NargoCommand::Lsp(_)) { config.program_dir = find_package_root(&config.program_dir)?; diff --git a/crates/nargo_cli/src/cli/new_cmd.rs b/crates/nargo_cli/src/cli/new_cmd.rs index dbdeddef712..d6a9d00257b 100644 --- a/crates/nargo_cli/src/cli/new_cmd.rs +++ b/crates/nargo_cli/src/cli/new_cmd.rs @@ -4,6 +4,7 @@ use super::{init_cmd::initialize_project, NargoConfig}; use acvm::Backend; use clap::Args; use nargo::package::PackageType; +use noirc_frontend::graph::CrateName; use std::path::PathBuf; /// Create a Noir project in a new directory. @@ -14,7 +15,7 @@ pub(crate) struct NewCommand { /// Name of the package [default: package directory name] #[clap(long)] - name: Option, + name: Option, /// Use a library template #[arg(long, conflicts_with = "bin", conflicts_with = "contract")] @@ -41,8 +42,13 @@ pub(crate) fn run( return Err(CliError::DestinationAlreadyExists(package_dir)); } - let package_name = - args.name.unwrap_or_else(|| args.path.file_name().unwrap().to_str().unwrap().to_owned()); + let package_name = match args.name { + Some(name) => name, + None => { + let name = args.path.file_name().unwrap().to_str().unwrap(); + name.parse().map_err(|_| CliError::InvalidPackageName(name.into()))? + } + }; let package_type = if args.lib { PackageType::Library } else if args.contract { @@ -50,6 +56,6 @@ pub(crate) fn run( } else { PackageType::Binary }; - initialize_project(package_dir, &package_name, package_type); + initialize_project(package_dir, package_name, package_type); Ok(()) } diff --git a/crates/nargo_cli/src/cli/prove_cmd.rs b/crates/nargo_cli/src/cli/prove_cmd.rs index 018e150f89f..e4766828a5b 100644 --- a/crates/nargo_cli/src/cli/prove_cmd.rs +++ b/crates/nargo_cli/src/cli/prove_cmd.rs @@ -6,7 +6,7 @@ use nargo::artifacts::program::PreprocessedProgram; use nargo::constants::{PROVER_INPUT_FILE, VERIFIER_INPUT_FILE}; use nargo::ops::{preprocess_program, prove_execution, verify_proof}; use nargo::package::Package; -use nargo_toml::{find_package_manifest, resolve_workspace_from_toml}; +use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_abi::input_parser::Format; use noirc_driver::CompileOptions; use noirc_frontend::graph::CrateName; @@ -40,9 +40,13 @@ pub(crate) struct ProveCommand { verify: bool, /// The name of the package to prove - #[clap(long)] + #[clap(long, conflicts_with = "workspace")] package: Option, + /// Prove all packages in the workspace + #[clap(long, conflicts_with = "package")] + workspace: bool, + #[clap(flatten)] compile_options: CompileOptions, } @@ -52,8 +56,11 @@ pub(crate) fn run( args: ProveCommand, config: NargoConfig, ) -> Result<(), CliError> { - let toml_path = find_package_manifest(&config.program_dir)?; - let workspace = resolve_workspace_from_toml(&toml_path, args.package)?; + let toml_path = get_package_manifest(&config.program_dir)?; + let default_selection = + if args.workspace { PackageSelection::All } else { PackageSelection::DefaultOrAll }; + let selection = args.package.map_or(default_selection, PackageSelection::Selected); + let workspace = resolve_workspace_from_toml(&toml_path, selection)?; let proof_dir = workspace.proofs_directory_path(); for package in &workspace { diff --git a/crates/nargo_cli/src/cli/test_cmd.rs b/crates/nargo_cli/src/cli/test_cmd.rs index ecbee5b4668..94c8ff86dcd 100644 --- a/crates/nargo_cli/src/cli/test_cmd.rs +++ b/crates/nargo_cli/src/cli/test_cmd.rs @@ -3,9 +3,13 @@ use std::io::Write; use acvm::{acir::native_types::WitnessMap, Backend}; use clap::Args; use nargo::{ops::execute_circuit, package::Package, prepare_package}; -use nargo_toml::{find_package_manifest, resolve_workspace_from_toml}; +use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{compile_no_check, CompileOptions}; -use noirc_frontend::{graph::CrateName, hir::Context, node_interner::FuncId}; +use noirc_frontend::{ + graph::CrateName, + hir::{Context, FunctionNameMatch}, + node_interner::FuncId, +}; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; use crate::{cli::check_cmd::check_crate_and_report_errors, errors::CliError}; @@ -22,10 +26,18 @@ pub(crate) struct TestCommand { #[arg(long)] show_output: bool, - /// The name of the package to test + /// Only run tests that match exactly #[clap(long)] + exact: bool, + + /// The name of the package to test + #[clap(long, conflicts_with = "workspace")] package: Option, + /// Test all packages in the workspace + #[clap(long, conflicts_with = "package")] + workspace: bool, + #[clap(flatten)] compile_options: CompileOptions, } @@ -35,13 +47,25 @@ pub(crate) fn run( args: TestCommand, config: NargoConfig, ) -> Result<(), CliError> { - let test_name: String = args.test_name.unwrap_or_else(|| "".to_owned()); - - let toml_path = find_package_manifest(&config.program_dir)?; - let workspace = resolve_workspace_from_toml(&toml_path, args.package)?; + let toml_path = get_package_manifest(&config.program_dir)?; + let default_selection = + if args.workspace { PackageSelection::All } else { PackageSelection::DefaultOrAll }; + let selection = args.package.map_or(default_selection, PackageSelection::Selected); + let workspace = resolve_workspace_from_toml(&toml_path, selection)?; + + let pattern = match &args.test_name { + Some(name) => { + if args.exact { + FunctionNameMatch::Exact(name) + } else { + FunctionNameMatch::Contains(name) + } + } + None => FunctionNameMatch::Anything, + }; for package in &workspace { - run_tests(backend, package, &test_name, args.show_output, &args.compile_options)?; + run_tests(backend, package, pattern, args.show_output, &args.compile_options)?; } Ok(()) @@ -50,7 +74,7 @@ pub(crate) fn run( fn run_tests( backend: &B, package: &Package, - test_name: &str, + test_name: FunctionNameMatch, show_output: bool, compile_options: &CompileOptions, ) -> Result<(), CliError> { diff --git a/crates/nargo_cli/src/cli/verify_cmd.rs b/crates/nargo_cli/src/cli/verify_cmd.rs index 5af28b4e25b..c2f21a2123b 100644 --- a/crates/nargo_cli/src/cli/verify_cmd.rs +++ b/crates/nargo_cli/src/cli/verify_cmd.rs @@ -18,7 +18,7 @@ use clap::Args; use nargo::constants::{PROOF_EXT, VERIFIER_INPUT_FILE}; use nargo::ops::{preprocess_program, verify_proof}; use nargo::{artifacts::program::PreprocessedProgram, package::Package}; -use nargo_toml::{find_package_manifest, resolve_workspace_from_toml}; +use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_abi::input_parser::Format; use noirc_driver::CompileOptions; use noirc_frontend::graph::CrateName; @@ -32,9 +32,13 @@ pub(crate) struct VerifyCommand { verifier_name: String, /// The name of the package verify - #[clap(long)] + #[clap(long, conflicts_with = "workspace")] package: Option, + /// Verify all packages in the workspace + #[clap(long, conflicts_with = "package")] + workspace: bool, + #[clap(flatten)] compile_options: CompileOptions, } @@ -44,8 +48,11 @@ pub(crate) fn run( args: VerifyCommand, config: NargoConfig, ) -> Result<(), CliError> { - let toml_path = find_package_manifest(&config.program_dir)?; - let workspace = resolve_workspace_from_toml(&toml_path, args.package)?; + let toml_path = get_package_manifest(&config.program_dir)?; + let default_selection = + if args.workspace { PackageSelection::All } else { PackageSelection::DefaultOrAll }; + let selection = args.package.map_or(default_selection, PackageSelection::Selected); + let workspace = resolve_workspace_from_toml(&toml_path, selection)?; let proofs_dir = workspace.proofs_directory_path(); for package in &workspace { diff --git a/crates/nargo_cli/src/errors.rs b/crates/nargo_cli/src/errors.rs index d801a56d3f5..dade4262431 100644 --- a/crates/nargo_cli/src/errors.rs +++ b/crates/nargo_cli/src/errors.rs @@ -41,6 +41,9 @@ pub(crate) enum CliError { #[error("Failed to verify proof {}", .0.display())] InvalidProof(PathBuf), + #[error("Invalid package name {0}. Did you mean to use `--name`?")] + InvalidPackageName(String), + /// ABI encoding/decoding error #[error(transparent)] AbiError(#[from] AbiError), diff --git a/crates/nargo_cli/tests/README.md b/crates/nargo_cli/tests/README.md new file mode 100644 index 00000000000..5ead2e4c4ed --- /dev/null +++ b/crates/nargo_cli/tests/README.md @@ -0,0 +1,38 @@ +# Integration test directory structure + +Integration tests for the Noir compiler are broken down into the following directories: + +- `compile_failure`: programs which are not valid or unsatisfiable Noir code and so the compiler should reject. +- `compile_success_empty`: programs which are valid satisfiable Noir code but have no opcodes. +- `execution_success`: programs which are valid Noir satisfiable code and have opcodes. + +The current testing flow can be thought of as shown: +```mermaid +flowchart TD + + subgraph compile_failure + A1[Attempt to compile] --> A2[Assert compilation fails] + end + + subgraph compile_success_empty + B1[Attempt to compile] --> B2[Assert compilation succeeds] + B2 --> B3[Assert empty circuit] + end + + subgraph execution_success + C1[Attempt to compile] --> C2[Assert compilation succeeds] + C2 --> C3[Write circuit to file] + C3 --> C4[Assert execution succeeds] + C4 --> C5[Write witness to file] + + C6[Publish witness + circuit as artifact] + C3 --> C6 + C5 --> C6 + end +``` + +## `execution_success` vs `compile_success_empty` + +Note that `execution_success` and `compile_success_empty` are distinct as `compile_success_empty` is expected to compile down to an empty circuit. This may not be possible for some argument-less circuits in the situation where instructions have side-effects or certain compiler optimizations are missing, but once moved to `compile_success_empty` a program compiling down to a non-empty circuit is a compiler regression. + + diff --git a/crates/nargo_cli/tests/compile_failure/assert_constant_fail/Nargo.toml b/crates/nargo_cli/tests/compile_failure/assert_constant_fail/Nargo.toml new file mode 100644 index 00000000000..a8c9b4ad344 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/assert_constant_fail/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "assert_constant_fail" +type = "bin" +authors = [""] +compiler_version = "0.9.0" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/compile_failure/assert_constant_fail/src/main.nr b/crates/nargo_cli/tests/compile_failure/assert_constant_fail/src/main.nr new file mode 100644 index 00000000000..cf682607083 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/assert_constant_fail/src/main.nr @@ -0,0 +1,10 @@ +use dep::std::assert_constant; + +fn main(x: Field) { + foo(5, x); +} + +fn foo(constant: Field, non_constant: Field) { + assert_constant(constant); + assert_constant(non_constant); +} diff --git a/crates/nargo_cli/tests/compile_failure/invalid_dependency_name/Nargo.toml b/crates/nargo_cli/tests/compile_failure/invalid_dependency_name/Nargo.toml new file mode 100644 index 00000000000..69b56a2e7d0 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/invalid_dependency_name/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "invalid_dependency_name" +type = "bin" +authors = [""] +compiler_version = "0.9.0" + +[dependencies] +bad_name = { path = "../../test_libraries/bad_name" } diff --git a/crates/nargo_cli/tests/compile_failure/invalid_dependency_name/src/main.nr b/crates/nargo_cli/tests/compile_failure/invalid_dependency_name/src/main.nr new file mode 100644 index 00000000000..faf1ba0045a --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/invalid_dependency_name/src/main.nr @@ -0,0 +1 @@ +fn main(x: Field) { } diff --git a/crates/nargo_cli/tests/compile_failure/overflowing_assignment/Nargo.toml b/crates/nargo_cli/tests/compile_failure/overflowing_assignment/Nargo.toml new file mode 100644 index 00000000000..25bdea70129 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/overflowing_assignment/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "overflowing_assignment" +type = "bin" +authors = [""] +compiler_version = "0.9.0" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/compile_failure/overflowing_assignment/src/main.nr b/crates/nargo_cli/tests/compile_failure/overflowing_assignment/src/main.nr new file mode 100644 index 00000000000..78535a853a6 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/overflowing_assignment/src/main.nr @@ -0,0 +1,5 @@ +fn main() { + let x:u8 = -1; + let y:u8 = 300; + assert(x!=y); +} diff --git a/crates/nargo_cli/tests/compile_failure/package_name_empty/Nargo.toml b/crates/nargo_cli/tests/compile_failure/package_name_empty/Nargo.toml new file mode 100644 index 00000000000..7c4dbd0c994 --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/package_name_empty/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "" +type = "bin" +authors = [""] +compiler_version = "0.9.0" + +[dependencies] diff --git a/crates/nargo_cli/tests/compile_failure/package_name_empty/src/main.nr b/crates/nargo_cli/tests/compile_failure/package_name_empty/src/main.nr new file mode 100644 index 00000000000..faf1ba0045a --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/package_name_empty/src/main.nr @@ -0,0 +1 @@ +fn main(x: Field) { } diff --git a/crates/nargo_cli/tests/compile_failure/package_name_hyphen/Nargo.toml b/crates/nargo_cli/tests/compile_failure/package_name_hyphen/Nargo.toml new file mode 100644 index 00000000000..f8d0a85db4a --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/package_name_hyphen/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "hyphenated-name" +type = "bin" +authors = [""] +compiler_version = "0.9.0" + +[dependencies] diff --git a/crates/nargo_cli/tests/compile_failure/package_name_hyphen/src/main.nr b/crates/nargo_cli/tests/compile_failure/package_name_hyphen/src/main.nr new file mode 100644 index 00000000000..faf1ba0045a --- /dev/null +++ b/crates/nargo_cli/tests/compile_failure/package_name_hyphen/src/main.nr @@ -0,0 +1 @@ +fn main(x: Field) { } diff --git a/crates/nargo_cli/tests/execution_success/brillig_cast/Nargo.toml b/crates/nargo_cli/tests/compile_success_empty/brillig_cast/Nargo.toml similarity index 100% rename from crates/nargo_cli/tests/execution_success/brillig_cast/Nargo.toml rename to crates/nargo_cli/tests/compile_success_empty/brillig_cast/Nargo.toml diff --git a/crates/nargo_cli/tests/execution_success/brillig_cast/src/main.nr b/crates/nargo_cli/tests/compile_success_empty/brillig_cast/src/main.nr similarity index 100% rename from crates/nargo_cli/tests/execution_success/brillig_cast/src/main.nr rename to crates/nargo_cli/tests/compile_success_empty/brillig_cast/src/main.nr diff --git a/crates/nargo_cli/tests/execution_success/brillig_to_bits/Nargo.toml b/crates/nargo_cli/tests/compile_success_empty/brillig_to_bits/Nargo.toml similarity index 100% rename from crates/nargo_cli/tests/execution_success/brillig_to_bits/Nargo.toml rename to crates/nargo_cli/tests/compile_success_empty/brillig_to_bits/Nargo.toml diff --git a/crates/nargo_cli/tests/execution_success/brillig_to_bits/src/main.nr b/crates/nargo_cli/tests/compile_success_empty/brillig_to_bits/src/main.nr similarity index 100% rename from crates/nargo_cli/tests/execution_success/brillig_to_bits/src/main.nr rename to crates/nargo_cli/tests/compile_success_empty/brillig_to_bits/src/main.nr diff --git a/crates/nargo_cli/tests/compile_success/comptime_sort/Nargo.toml b/crates/nargo_cli/tests/compile_success_empty/comptime_sort/Nargo.toml similarity index 100% rename from crates/nargo_cli/tests/compile_success/comptime_sort/Nargo.toml rename to crates/nargo_cli/tests/compile_success_empty/comptime_sort/Nargo.toml diff --git a/crates/nargo_cli/tests/compile_success/comptime_sort/src/main.nr b/crates/nargo_cli/tests/compile_success_empty/comptime_sort/src/main.nr similarity index 100% rename from crates/nargo_cli/tests/compile_success/comptime_sort/src/main.nr rename to crates/nargo_cli/tests/compile_success_empty/comptime_sort/src/main.nr diff --git a/crates/nargo_cli/tests/compile_success/ec_baby_jubjub/Nargo.toml b/crates/nargo_cli/tests/compile_success_empty/ec_baby_jubjub/Nargo.toml similarity index 100% rename from crates/nargo_cli/tests/compile_success/ec_baby_jubjub/Nargo.toml rename to crates/nargo_cli/tests/compile_success_empty/ec_baby_jubjub/Nargo.toml diff --git a/crates/nargo_cli/tests/compile_success/ec_baby_jubjub/src/main.nr b/crates/nargo_cli/tests/compile_success_empty/ec_baby_jubjub/src/main.nr similarity index 100% rename from crates/nargo_cli/tests/compile_success/ec_baby_jubjub/src/main.nr rename to crates/nargo_cli/tests/compile_success_empty/ec_baby_jubjub/src/main.nr diff --git a/crates/nargo_cli/tests/compile_success/higher_order_fn_selector/Nargo.toml b/crates/nargo_cli/tests/compile_success_empty/higher_order_fn_selector/Nargo.toml similarity index 100% rename from crates/nargo_cli/tests/compile_success/higher_order_fn_selector/Nargo.toml rename to crates/nargo_cli/tests/compile_success_empty/higher_order_fn_selector/Nargo.toml diff --git a/crates/nargo_cli/tests/compile_success/higher_order_fn_selector/src/main.nr b/crates/nargo_cli/tests/compile_success_empty/higher_order_fn_selector/src/main.nr similarity index 100% rename from crates/nargo_cli/tests/compile_success/higher_order_fn_selector/src/main.nr rename to crates/nargo_cli/tests/compile_success_empty/higher_order_fn_selector/src/main.nr diff --git a/crates/nargo_cli/tests/compile_success/inner_outer_cl/Nargo.toml b/crates/nargo_cli/tests/compile_success_empty/inner_outer_cl/Nargo.toml similarity index 100% rename from crates/nargo_cli/tests/compile_success/inner_outer_cl/Nargo.toml rename to crates/nargo_cli/tests/compile_success_empty/inner_outer_cl/Nargo.toml diff --git a/crates/nargo_cli/tests/compile_success/inner_outer_cl/src/main.nr b/crates/nargo_cli/tests/compile_success_empty/inner_outer_cl/src/main.nr similarity index 100% rename from crates/nargo_cli/tests/compile_success/inner_outer_cl/src/main.nr rename to crates/nargo_cli/tests/compile_success_empty/inner_outer_cl/src/main.nr diff --git a/crates/nargo_cli/tests/compile_success_empty/intrinsic_die/Nargo.toml b/crates/nargo_cli/tests/compile_success_empty/intrinsic_die/Nargo.toml new file mode 100644 index 00000000000..60e0310eab2 --- /dev/null +++ b/crates/nargo_cli/tests/compile_success_empty/intrinsic_die/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "intrinsic_die" +type = "bin" +authors = [""] +compiler_version = "0.6.0" + +[dependencies] diff --git a/crates/nargo_cli/tests/compile_success_empty/intrinsic_die/src/main.nr b/crates/nargo_cli/tests/compile_success_empty/intrinsic_die/src/main.nr new file mode 100644 index 00000000000..5c59d2c8028 --- /dev/null +++ b/crates/nargo_cli/tests/compile_success_empty/intrinsic_die/src/main.nr @@ -0,0 +1,9 @@ +use dep::std; + +// This test checks that we perform dead-instruction-elimination on intrinsic functions. + +fn main(x: Field) { + let bytes = x.to_be_bytes(32); + + let hash = std::hash::pedersen([x]); +} diff --git a/crates/nargo_cli/tests/compile_success/let_stmt/Nargo.toml b/crates/nargo_cli/tests/compile_success_empty/let_stmt/Nargo.toml similarity index 100% rename from crates/nargo_cli/tests/compile_success/let_stmt/Nargo.toml rename to crates/nargo_cli/tests/compile_success_empty/let_stmt/Nargo.toml diff --git a/crates/nargo_cli/tests/compile_success/let_stmt/Prover.toml b/crates/nargo_cli/tests/compile_success_empty/let_stmt/Prover.toml similarity index 100% rename from crates/nargo_cli/tests/compile_success/let_stmt/Prover.toml rename to crates/nargo_cli/tests/compile_success_empty/let_stmt/Prover.toml diff --git a/crates/nargo_cli/tests/compile_success/let_stmt/src/main.nr b/crates/nargo_cli/tests/compile_success_empty/let_stmt/src/main.nr similarity index 100% rename from crates/nargo_cli/tests/compile_success/let_stmt/src/main.nr rename to crates/nargo_cli/tests/compile_success_empty/let_stmt/src/main.nr diff --git a/crates/nargo_cli/tests/compile_success/numeric_generics/Nargo.toml b/crates/nargo_cli/tests/compile_success_empty/numeric_generics/Nargo.toml similarity index 100% rename from crates/nargo_cli/tests/compile_success/numeric_generics/Nargo.toml rename to crates/nargo_cli/tests/compile_success_empty/numeric_generics/Nargo.toml diff --git a/crates/nargo_cli/tests/compile_success/numeric_generics/Prover.toml b/crates/nargo_cli/tests/compile_success_empty/numeric_generics/Prover.toml similarity index 100% rename from crates/nargo_cli/tests/compile_success/numeric_generics/Prover.toml rename to crates/nargo_cli/tests/compile_success_empty/numeric_generics/Prover.toml diff --git a/crates/nargo_cli/tests/compile_success/numeric_generics/src/main.nr b/crates/nargo_cli/tests/compile_success_empty/numeric_generics/src/main.nr similarity index 100% rename from crates/nargo_cli/tests/compile_success/numeric_generics/src/main.nr rename to crates/nargo_cli/tests/compile_success_empty/numeric_generics/src/main.nr diff --git a/crates/nargo_cli/tests/compile_success/option/Nargo.toml b/crates/nargo_cli/tests/compile_success_empty/option/Nargo.toml similarity index 100% rename from crates/nargo_cli/tests/compile_success/option/Nargo.toml rename to crates/nargo_cli/tests/compile_success_empty/option/Nargo.toml diff --git a/crates/nargo_cli/tests/compile_success/option/src/main.nr b/crates/nargo_cli/tests/compile_success_empty/option/src/main.nr similarity index 100% rename from crates/nargo_cli/tests/compile_success/option/src/main.nr rename to crates/nargo_cli/tests/compile_success_empty/option/src/main.nr diff --git a/crates/nargo_cli/tests/execution_success/regression_2099/Nargo.toml b/crates/nargo_cli/tests/compile_success_empty/regression_2099/Nargo.toml similarity index 100% rename from crates/nargo_cli/tests/execution_success/regression_2099/Nargo.toml rename to crates/nargo_cli/tests/compile_success_empty/regression_2099/Nargo.toml diff --git a/crates/nargo_cli/tests/execution_success/regression_2099/src/main.nr b/crates/nargo_cli/tests/compile_success_empty/regression_2099/src/main.nr similarity index 100% rename from crates/nargo_cli/tests/execution_success/regression_2099/src/main.nr rename to crates/nargo_cli/tests/compile_success_empty/regression_2099/src/main.nr diff --git a/crates/nargo_cli/tests/compile_success/to_bits/Nargo.toml b/crates/nargo_cli/tests/compile_success_empty/to_bits/Nargo.toml similarity index 100% rename from crates/nargo_cli/tests/compile_success/to_bits/Nargo.toml rename to crates/nargo_cli/tests/compile_success_empty/to_bits/Nargo.toml diff --git a/crates/nargo_cli/tests/compile_success/to_bits/src/main.nr b/crates/nargo_cli/tests/compile_success_empty/to_bits/src/main.nr similarity index 100% rename from crates/nargo_cli/tests/compile_success/to_bits/src/main.nr rename to crates/nargo_cli/tests/compile_success_empty/to_bits/src/main.nr diff --git a/crates/nargo_cli/tests/execution_success/unconstrained_empty/Nargo.toml b/crates/nargo_cli/tests/compile_success_empty/unconstrained_empty/Nargo.toml similarity index 100% rename from crates/nargo_cli/tests/execution_success/unconstrained_empty/Nargo.toml rename to crates/nargo_cli/tests/compile_success_empty/unconstrained_empty/Nargo.toml diff --git a/crates/nargo_cli/tests/execution_success/unconstrained_empty/src/main.nr b/crates/nargo_cli/tests/compile_success_empty/unconstrained_empty/src/main.nr similarity index 100% rename from crates/nargo_cli/tests/execution_success/unconstrained_empty/src/main.nr rename to crates/nargo_cli/tests/compile_success_empty/unconstrained_empty/src/main.nr diff --git a/crates/nargo_cli/tests/compile_success/unit/Nargo.toml b/crates/nargo_cli/tests/compile_success_empty/unit/Nargo.toml similarity index 100% rename from crates/nargo_cli/tests/compile_success/unit/Nargo.toml rename to crates/nargo_cli/tests/compile_success_empty/unit/Nargo.toml diff --git a/crates/nargo_cli/tests/compile_success/unit/src/main.nr b/crates/nargo_cli/tests/compile_success_empty/unit/src/main.nr similarity index 100% rename from crates/nargo_cli/tests/compile_success/unit/src/main.nr rename to crates/nargo_cli/tests/compile_success_empty/unit/src/main.nr diff --git a/crates/nargo_cli/tests/compile_success/unused_variables/Nargo.toml b/crates/nargo_cli/tests/compile_success_empty/unused_variables/Nargo.toml similarity index 100% rename from crates/nargo_cli/tests/compile_success/unused_variables/Nargo.toml rename to crates/nargo_cli/tests/compile_success_empty/unused_variables/Nargo.toml diff --git a/crates/nargo_cli/tests/compile_success/unused_variables/src/main.nr b/crates/nargo_cli/tests/compile_success_empty/unused_variables/src/main.nr similarity index 100% rename from crates/nargo_cli/tests/compile_success/unused_variables/src/main.nr rename to crates/nargo_cli/tests/compile_success_empty/unused_variables/src/main.nr diff --git a/crates/nargo_cli/tests/execution_success/array_dynamic/src/main.nr b/crates/nargo_cli/tests/execution_success/array_dynamic/src/main.nr index b27c96b30c3..69e5fdc93db 100644 --- a/crates/nargo_cli/tests/execution_success/array_dynamic/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/array_dynamic/src/main.nr @@ -21,7 +21,7 @@ fn dyn_array(mut x: [u32; 5], y: Field, z: Field) { assert(x[y] == 111); assert(x[z] == 101); x[z] = 0; - assert(x[y] == 111); + assert(x[y] == 111); assert(x[1] == 0); if y as u32 < 10 { x[y] = x[y] - 2; diff --git a/crates/nargo_cli/tests/execution_success/brillig_calls_conditionals/target/brillig_calls_conditionals.json b/crates/nargo_cli/tests/execution_success/brillig_calls_conditionals/target/brillig_calls_conditionals.json index 323ec175852..59f1cb780ea 100644 --- a/crates/nargo_cli/tests/execution_success/brillig_calls_conditionals/target/brillig_calls_conditionals.json +++ b/crates/nargo_cli/tests/execution_success/brillig_calls_conditionals/target/brillig_calls_conditionals.json @@ -1 +1 @@ -{"backend":"acvm-backend-barretenberg","abi":{"parameters":[{"name":"x","type":{"kind":"array","length":3,"type":{"kind":"integer","sign":"unsigned","width":32}},"visibility":"private"}],"param_witnesses":{"x":[1,2,3]},"return_type":null,"return_witnesses":[]},"bytecode":"H4sIAAAAAAAA/+2b226jSBCG2waMG3xKZi9W2r2YB9gLfJpgaS/8KonWudh9zp3nmmGGVn53fKrQjUujv6TIYHDV1wXBNMn3yRjzh/kZg+8/w/b1M6wPvfWkXc/azyXe55P2Z2Deh3tv375W3WI5CJerQt4Ulv9uN9h2fBgjWN4H4hiF71OVAasFZjeeWbs9DVt31dRKjDl5Dvh9w743LFlYlsqatx4kUCcPW+fHOemfF27d1SqBIYvIUp6oHXvMzXg+m7dzK4eeu32+tq/N8f4NuGxYrlXDNT7DZYHL7fMfcP0OXGVYrnXDVZzhKoHL7fMncP3r7Wfgsw3rJCzr8tLv6+QES6KIJVXEkiliGSliyRWxjBWxWEUshSKWUhHL4M4s1ry/n2quMxXcs07b9/F7z31+at7vh2OaRRgT1tnDuqtVAMNUAUupiKVQxGIVsYwVseSKWEaKWDJFLKkilkQRy9BjsbAd7+vnEfhcHZfXrbtaOGeeRGQpT9TGeVez/D8wucA5WIzjZ7z+uJidYEkUsaSKWDJFLCNFLLkilrEiFquIpVDEUipimShiif0dLmGJPY+7xnJubrqBuan7fsW5qf/di/vhmBYRxoR19rDuauHcdK6AZaqIZaKIpVTEUihisYpYxopYckUsI0UsmSKWVBFLoohl6LFY2I7zwIcIfK6Oy+vWXS2cs88ispQnauM8vVn+a/DG5ALn7DGOn/H642Jh3rMkilhSRSyZIpaRIpZcEctYEYtVxFIoYikVsUwUsUwVscwUscS+t5GwxJ73X2M59yyjhmcZ7r4Dn2X49yS4H47pMcKYsM4e1l0tfJbxoIBlrohlpohlqohlooilVMRSKGKxiljGilhyRSwjRSyZIpZUEUuiiGXosVjYjs8NPkXgc3VcXrfuauEznkVElvJEbXyuU5zoiYV9H+G9oZcnb5mHYZl36L8MT9RP4tav71z/SVLfv78OduJgzsADXKaQa1192WwOT6vDcr18rla7l3pbbbYvX+plvdzW239W9Xp9qDf10+5l91Ttlpv1Yfm63a1f22RZgFyH1x/x3JeAl4XLdSTgjWA59Y6dMccXvdBjMl4dv48zE/lkjXGQYjQqN+FO/ljjzsMfowpSqu5pXxeBoYlzERjDMi3cDwct3DYvLVxauBC0cHtioYVLC1fCQguXFq6EZXBnFlq48Vlo4dLClbDQwqWFK2GhhUsL95b+GK8/Lmjh3s5CC5cWroSFFi4tXAkLLVxauH2y0MKlhSthoYVLC1fCQguXFu4lPlq4l/tjvP64oIV7OwstXFq4EhZauLRwJSy0cGnhXmOhhRufhRYuLVwJCy1cWrgSFlq4tHAlLLRwaeFW3YIWrvnFLdwx5OoqzdkAuVoLd9eXgGfD5ToS8ApYpoXbMac1x7O9UHlLE+7kjzXuMvwxqiCl6p72dRFITJyLwASWaeF+OGjhtnlp4dLChaCF2xMLLVxauBIWWri0cCUsgzuz0MKNz0ILlxauhIUWLi1cCQstXFq4t/THeP1xQQv3dhZauLRwJSy0cGnhSlho4dLC7ZOFFi4tXAkLLVxauBIWWri0cC/x0cK93B/j9ccFLdzbWWjh0sKVsNDCpYUrYaGFSwv3Ggst3PgstHBp4UpYaOHSwpWw0MKlhSthoYVLC7fqFrRwzS9u4U4gV1dpbhogV2vh1n0JeNNwuY4EPPxnMFq4HXNOPehQeecm3Mkfa9zz8MeogpSqe3ruIhC2H6tnrIF/EaI5++GgOdvmpTlLcxaC5mxPLDRnac5KWGjO0pyVsAzuzEJzNj4LzVmasxIWmrM0ZyUsNGdpzt7SH+P1xwXN2dtZaM7SnJWw0JylOSthoTlLc7ZPFpqzNGclLDRnac5KWGjO0py9xEdz9nJ/jNcfFzRnb2ehOUtzVsJCc5bmrISF5izN2WssNGfjs9CcpTkrYaE5S3NWwkJzluashIXmLM3ZqlvQnDUKzNm+LNJFuFxHFukDLNMi7Zhz0TY0dN5HyKXRIm3G/Rj+GFWQUnVPE2Bsfnl8K7GJbwpW52lU7QAA","proving_key":null,"verification_key":null} \ No newline at end of file +{"backend":"acvm-backend-barretenberg","abi":{"parameters":[{"name":"x","type":{"kind":"array","length":3,"type":{"kind":"integer","sign":"unsigned","width":32}},"visibility":"private"}],"param_witnesses":{"x":[1,2,3]},"return_type":null,"return_witnesses":[]},"bytecode":"H4sIAAAAAAAA/+2by27jNhSGaetKXWwn00WBdjEP0IXky0QGuvCrTFBn0T5n+1ytpiLym5Edc0QqB4OfQGDRks/5eCTTopDvk1LqF/V/W/z3txxeP0N/afWjoZ8Mn4usz0fD30K9bea90/DaTGvtwl+sBnlj2P592KGH8WFLYfvkiSP1X6cmAVYNzGY8q2F/7Dfvts8VKTV6Ddh1w7r3LIlflkar1xpEkCfzm+fbNWlfF6ZvcpXAkARkKUdyhx5zP57P6vXayqDm5ph/htf+fP8EXNov17bnyq9waeAyx/wFXD8DV+mXa9dzFVe4SuAyx/wKXH9axyn4bM9a+WVtb31fqxGWSBBLLIglEcSSCmLJBLHkgli0IJZCEEspiGXxwSxavb2f6ueZBu5Z6+F9/N0zn6/V2+NwTKsAY8I8J+ibXAUw1AJYSkEshSAWLYglF8SSCWJJBbEkglhiQSyRIJalxaJhP97XrwPwmTwmrumbXLhmrgKylCO5cd3Vb/8NTKbhGizE+VNWfUxbjbBEglhiQSyJIJZUEEsmiCUXxKIFsRSCWEpBLJUgltC/4S4soddx77FcW5vuYW1qfl9xbWr/9uJxOKZNgDFhnhP0TS5cm64FsNSCWCpBLKUglkIQixbEkgtiyQSxpIJYEkEssSCWSBDL0mLRsB/XgQ8B+EweE9f0TS5cs68CspQjuXGd3m//tnhlMg3X7CHOn7LqY9pGvWWJBLHEglgSQSypIJZMEEsuiEULYikEsZSCWCpBLLUglpUgltD3Ni4sodf977Fce5bRwbMMc9+BzzLsexI8Dsf0GGBMmOcEfZMLn2U8CGBZC2JZCWKpBbFUglhKQSyFIBYtiCUXxJIJYkkFsSSCWGJBLJEglqXFomE/Pjf4FIDP5DFxTd/kwmc8m4As5UhufK5TjNREw7GP8N7SipMNzEu/zEf0X5Yj+aOw+bsPzv/kkt++v/Z24WBMzwNsY4i1a77s9+en7bndtV+b7fG5OzT7w/OXru3aQ3f4Y9vtdudu3z0dn49PzbHd787ty+G4exmCJR5inV++ta9zCXiJv1gXAl4K27F17pS6nPR8j0lZeew6rlTgizXESQpRqEz5u/hDjTvzf44aCCm6pnNNAksVZhLIYZsW7nc3WrhDXFq4tHCh0cKdiYUWLi1cFxZauLRwXVgWH8xCCzc8Cy1cWrguLLRwaeG6sNDCpYV7T32UVR/TaOHez0ILlxauCwstXFq4Liy0cGnhzslCC5cWrgsLLVxauC4stHBp4d7io4V7uz7Kqo9ptHDvZ6GFSwvXhYUWLi1cFxZauLRw32OhhRuehRYuLVwXFlq4tHBdWGjh0sJ1YaGFSwu3mdZo4aof3MLNIdZUaU57iDVYuMe5BDztL9aFgFfANi3ciTG1ulzt+YpbKn8Xf6hxl/7PUQMhRdd0rkkgUmEmgQq2aeF+d6OFO8SlhUsLFxot3JlYaOHSwnVhoYVLC9eFZfHBLLRww7PQwqWF68JCC5cWrgsLLVxauPfUR1n1MY0W7v0stHBp4bqw0MKlhevCQguXFu6cLLRwaeG6sNDCpYXrwkILlxbuLT5auLfro6z6mEYL934WWri0cF1YaOHSwnVhoYVLC/c9Flq44Vlo4dLCdWGhhUsL14WFFi4tXBcWWri0cJtpjRau+sEt3ApiTZXmag+xBgu3m0vAq/3FuhDw8J/BaOFOjFlb0L7irpW/iz/UuNf+z1EDIcXX9Brn1Ngbj/Wca7LyyYy8D7DNyWpizM1QUN9xH5Xsyaof96P/cxR0svJZ0wgY+y+PrY337V8ow1m/u7cAAA==","proving_key":null,"verification_key":null} \ No newline at end of file diff --git a/crates/nargo_cli/tests/execution_success/brillig_cast/target/brillig_cast.json b/crates/nargo_cli/tests/execution_success/brillig_cast/target/brillig_cast.json deleted file mode 100644 index 7c2348cceb4..00000000000 --- a/crates/nargo_cli/tests/execution_success/brillig_cast/target/brillig_cast.json +++ /dev/null @@ -1 +0,0 @@ -{"backend":"acvm-backend-barretenberg","abi":{"parameters":[],"param_witnesses":{},"return_type":null,"return_witnesses":[]},"bytecode":"H4sIAAAAAAAA/+2WzW7CMBCEHYeQXwgh5FRVolVvvZCCeu67tOr7P0LrxiNGbuiB7gYhsRJysFY7u1+seCJjTGKGiM3pQM6c9t78uvtnuJqRcE3X78zXWn7/UtKQHqC/gTsvbuDOjD/B7a8cnNOIRTVeD3PSkOrbcbDE2FLfM3oHiYIu6ide19J/4/tATunXhSLbVGFGPr8z6jsltpms7iGi+jiX6CMjtshZT8C2kJ3xh21ujsF9F7RXiur2765mpTBLEcyCvivaWyjoon4UaBS0v/TPic/Jg95iyrn3q+ZZWonWHDjU5hgZ9b0i/o3wLBHVT7wu+miILXKeJmDbitYc2OL7ErJtldm2Adv1CFvkPE/AthOtObDdmHG2nTLbLmC7GWGLnBdiezFD+UhAhHWv1lC6D7mlF+YiJ11LzKS1HS8YPbduSTemFTmhARQ2ax8KRuyXAbTUd0a8M+KMVdgw9WyYwBu6BfFGThvwFjY9n1qGBucEvCuaA7zxXBLvWraXfWictqRbE2/k3AW8ZT/au97NDSPR0NzCF37PFz4uB+i2NDdyHmhu1cvBnIgvzUgn0D0UAAA=","proving_key":null,"verification_key":null} \ No newline at end of file diff --git a/crates/nargo_cli/tests/execution_success/brillig_cast/target/witness.tr b/crates/nargo_cli/tests/execution_success/brillig_cast/target/witness.tr deleted file mode 100644 index 4e90289d5e1..00000000000 Binary files a/crates/nargo_cli/tests/execution_success/brillig_cast/target/witness.tr and /dev/null differ diff --git a/crates/nargo_cli/tests/execution_success/brillig_field_binary_operations/target/brillig_field_binary_operations.json b/crates/nargo_cli/tests/execution_success/brillig_field_binary_operations/target/brillig_field_binary_operations.json index e08b7dc3b1d..811f409701c 100644 --- a/crates/nargo_cli/tests/execution_success/brillig_field_binary_operations/target/brillig_field_binary_operations.json +++ b/crates/nargo_cli/tests/execution_success/brillig_field_binary_operations/target/brillig_field_binary_operations.json @@ -1 +1 @@ -{"backend":"acvm-backend-barretenberg","abi":{"parameters":[],"param_witnesses":{},"return_type":null,"return_witnesses":[]},"bytecode":"H4sIAAAAAAAA/+2Y3W7CMAyFDf2BMujY3R4jadI2vdurrFp5/0cYFalwUXaznlRVG0soFoGDj230CT6I6JMeEd0fe/o7vuwppoX2qF3smCbP3+yZWZ/8PnP4TvG1iZR9JkozuWscmI8hBj+5PWPHXcTel7y8jvdkuDvQuKdII5IcBQ7aSlRad3XRSSW/RdG0phS6bCsjjSxN+VMYpTqjTd20TS0aqVUnb2WjblZ8B9Ta4zxXkauhBF8SCaxZ8Hojlscvs+P3HhZ/tC+uPubkeVl9DCnyoBsTbvl9+Y7xMxJMctE9XRt1E5YH6j49b466CVArxXku5qIusOYRdQ8sD9SdqJnS+BuG0j3Ssqnb+z7iZ+SVusiero26nEKBus/nN0fdDKh1wnk2c1EXWLNw/YHEFydQ95+aJ9tQtO6Zlk3d3vcZPyOv1EX2dG3UvbA8UPcRfMaboe4FqJXjPM/2WxdY84i67ywP1J2omduGonWvtGzq9r6v+Bl5pS66p674BXI9tT78HQAA","proving_key":null,"verification_key":null} \ No newline at end of file +{"backend":"acvm-backend-barretenberg","abi":{"parameters":[],"param_witnesses":{},"return_type":null,"return_witnesses":[]},"bytecode":"H4sIAAAAAAAA/+2UQQ6DIBBFp0K1WttVDwICCrtepaZ4/yM0piwmVrthSDBxNkxYfN78If8GAA/4rVM4n+FUotfaD52XSr5E50ZrhDZjb6WVxpp3Z5XyVtvBjW4QTmrl5WScmsS3TkhLRBZDfP+YRVxJSmbMW6Cer9yxcJYJZoLFO0sf7yt3pI+nWFKRQJcB3edPNTej35FAktl7usUZq81hf2FFyYx5z6g/wipSkwdDqXVLyDusePggxDtKGlbUnm5xxmpXsL+womTGvBfUH2EVqVkFQ6l1a8g7rOa5a/odJQ0rak+3OGO1G9hfWFEyY94r6o+witRsgqHUui3kHVbz3C39jpKGFbWna/UBXDJMO9gSAAA=","proving_key":null,"verification_key":null} \ No newline at end of file diff --git a/crates/nargo_cli/tests/execution_success/brillig_field_binary_operations/target/witness.tr b/crates/nargo_cli/tests/execution_success/brillig_field_binary_operations/target/witness.tr index 908ac53760e..3e291a6c8c5 100644 Binary files a/crates/nargo_cli/tests/execution_success/brillig_field_binary_operations/target/witness.tr and b/crates/nargo_cli/tests/execution_success/brillig_field_binary_operations/target/witness.tr differ diff --git a/crates/nargo_cli/tests/execution_success/brillig_integer_binary_operations/target/brillig_integer_binary_operations.json b/crates/nargo_cli/tests/execution_success/brillig_integer_binary_operations/target/brillig_integer_binary_operations.json index cb58e907a68..5a9a1494b04 100644 --- a/crates/nargo_cli/tests/execution_success/brillig_integer_binary_operations/target/brillig_integer_binary_operations.json +++ b/crates/nargo_cli/tests/execution_success/brillig_integer_binary_operations/target/brillig_integer_binary_operations.json @@ -1 +1 @@ -{"backend":"acvm-backend-barretenberg","abi":{"parameters":[],"param_witnesses":{},"return_type":null,"return_witnesses":[]},"bytecode":"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","proving_key":null,"verification_key":null} \ No newline at end of file +{"backend":"acvm-backend-barretenberg","abi":{"parameters":[],"param_witnesses":{},"return_type":null,"return_witnesses":[]},"bytecode":"H4sIAAAAAAAA/+2Uy27CMBBFB5JCobwKhdI/6NLOgzi7/kqjhv//hCqqF6M0ZjPXki3Zm1hZ3Jy5E50PIvqk/2dmn1/2WapbVfVN0etSf6ui7Uytqrq7GW10beqfwpRlbyrTtF3bqFZXZa/vdVve1d+ZsSwlPBnje8SsZEcjmTnvnN3ziXeZfS48zESj74x73E28g37cx5LmHnIzwv38vubO8DtSLDL4Tl2c0uyc4pMVkpnzPrF7kpUwM7eFonMXFLascvuDgHfkVVboTl2c0uwlxScrJDPnfWb3JCth5tIWis5dUdiyGuZe4XfkVVboTl2c0uw1xScrJDPnfWH3JCth5toWis7dUNiyGube4HfkVVboTl2c0uwtxScrJDPn3bF7kpUwczuCRuXuKWxZDXPv8TvyKit0py5OafaB4pMVkpnzvrJ7kpUw82ALReceKWxZDXMf8TvyKit0py5OafaJ4pMVkpnzvrF7kpUw82QLReeeKWxZDXOf8TvyKit0py5OafaF4pMVkpnzvrN7kpUw82ILRedeKWxZDXNf8TvyKit0p1PnF6lyuYaMJQAA","proving_key":null,"verification_key":null} \ No newline at end of file diff --git a/crates/nargo_cli/tests/execution_success/brillig_integer_binary_operations/target/witness.tr b/crates/nargo_cli/tests/execution_success/brillig_integer_binary_operations/target/witness.tr index 95ca3d20bb4..a52e8a76601 100644 Binary files a/crates/nargo_cli/tests/execution_success/brillig_integer_binary_operations/target/witness.tr and b/crates/nargo_cli/tests/execution_success/brillig_integer_binary_operations/target/witness.tr differ diff --git a/crates/nargo_cli/tests/execution_success/brillig_keccak/target/brillig_keccak.json b/crates/nargo_cli/tests/execution_success/brillig_keccak/target/brillig_keccak.json index 2479c628955..9a72d776292 100644 --- a/crates/nargo_cli/tests/execution_success/brillig_keccak/target/brillig_keccak.json +++ b/crates/nargo_cli/tests/execution_success/brillig_keccak/target/brillig_keccak.json @@ -1 +1 @@ -{"backend":"acvm-backend-barretenberg","abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"result","type":{"kind":"array","length":32,"type":{"kind":"integer","sign":"unsigned","width":8}},"visibility":"private"}],"param_witnesses":{"result":[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33],"x":[1]},"return_type":null,"return_witnesses":[]},"bytecode":"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","proving_key":null,"verification_key":null} \ No newline at end of file +{"backend":"acvm-backend-barretenberg","abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"result","type":{"kind":"array","length":32,"type":{"kind":"integer","sign":"unsigned","width":8}},"visibility":"private"}],"param_witnesses":{"result":[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33],"x":[1]},"return_type":null,"return_witnesses":[]},"bytecode":"H4sIAAAAAAAA/+3de7hPZRYH8HXOcb/f79dyv57fuTnHnZIIIYQQhyOEEEIIIYQQQgghhBBCNE1TU1NNNdVUU0011VRTTTXVVFNNzV7jPU+vzcw/+7v2867neX/Psx/vplnnu9b6nc8+zkiPJBGlJNN/X8GRks1VxLpPCd0XCN0XDN0XCt0XDt0XCd0XDd0XC90XD92XCN2XDN2XCt2XDt2XCd2XDd2XC92XD91XCN1XDN1XCt1XDt1XCd1XDd1XC91XD93XCN3XDN3XCt3XDt3XCd3XNfe8RzK/TvTL7guYXytk7bKo2VNxs4+SZu6lzXzLmjmWN/OqaOZS2fRf1fRZ3fRT0+SubT5+XfOxU+j8V5L5saP5MTXaK5GEq5V6gbiY2gnB2kH1ZKvmRebHi62fK2p+zPeBX4WsfeTvid8rP9P5u0qyzsnmn0n5P/9M0v+oU9T6ufz/fSkrC+FmklqI4O+11FJWTXTgRP4nNC/wG/rlE5wXWST0MVMEPnZ+rfTUrIyMvFZpeYn0xIjUtJzc7MzUjMzcrOxEdiIzO3NUWnZ6el52RnarnNycVqk5iYz0vMTozJz0vNH8Sk1cBKg12gS7GNdjagr98uZPusAi0TAhs9MFXqic9idtHXOuF1z1g6tBcDUMrkbB1Ti4mgRX0+BqFlzNg6tFcLXkDMGVCK403nlwZQRXZnBlBVer4MoOrpzgah1cbYKrbXC1C672wdXB9NIpuDoH1yVWj6XMjwxHGBz+ORs+fglAkpCAhB/Iha0+8l/JVt/8cQtCP25aKn+sAnTuK/x50NE653/8EnTuw8N+YPCrjtVLodCv2XvK/7XCJAipXTMMZVSQkoE56wFzXUpYKM9bDuGBRGa283axzvlvwuQLvCcEPrHPe++F5yj6FYTUkroI1L2McG9+qb4vw+8oNfwGIeAcUoD91wfm6kr6cEJmtvNebp09ThFrdjUDRdftRm7jxH13w+9IFKcCwP4bAHN1J304ITPbea+wzh6niDW7m4Gi6/Ygt3HivnvgdySSlb/C63aBulHn2pN0oFwQOMuGwFy9SB/KyMx23iuts0c5Ys1eZqDour3JbZS57974HYlk5YdHT8Kj3Id0oFwIOMtGwFx9SR/KyMx23quss0c5Ys2+ZqDouv3IbZS57374HYlk5YdHH8Kj3J90oFwYOMvGwFwDSB/KyMx23quts0c5Ys0BZqDougPJbZS574H4HYlk5YdHf8KjPIh0oFwEOMsmwFyDSR/KyMx23muss0c5Ys3BZqDoukPIbZS57yH4HYlk5YfHIMKjPJR0oFwUOMumwFzDSB/KyMx23muts0c5Ys1hZqDousPJbZS57+H4HYlk5YfHUMKjPIJ0oFwMOMtmwFy5pA9lZGY770jr7FGOWDPXDBRddxS5jTL3PQq/I5Gsw4MaIwiPch7pQLk4cJbNgblGkz6UkZntvNdZZ49yxJqjzUDRdceQ2yhz32PwOxLJyg+PPMKjPJZ0oFwCOMsWwFzjSB/KyMx23uuts0c5Ys1xZqDouuPJbZS57/H4HYlk5YfHWMKjPIF0oFwSOMuWwFwTSR/KyMx23huss0c5Ys2JZqDoupPIbZS570n4HYlk5YfHBMKjPJl0oFwKOMtUYK4ppA9lZGY7743W2aMcseYUM1B03ankNsrc91T8jkSy8sNjMuFRnkY6UC4NnGUCmGs66UMZmdnOe5N19ihHrDndDBRddwa5jTL3PQO/I5Gs/PCYRniUZ5IOlMsAZ5kGzDWL9KGMzGznvdk6e5Qj1pxlBoquO5vcRpn7no3fkUhWfnjMJDzKc0gHymWBs0wH5ppL+lBGZrbz3mKdPcoRa841A0XXnUduo8x9z8PvSCQrPzzmEB7l+aQD5XLAWWYAcy0gfSgjM9t5b7XOHuWINReYgaLrLiS3Uea+F+J3JJJ1XlBjPuFRXkQ6UC4PnGUmMNdi0ocyMrOd9zbr7FGOWHOxGSi67hJyG2Xuewl+RyJZ+eGxiPAoLyUdKFcAzjILmGsZ6UMZmdnOe7t19ihHrLnMDBRddzm5jTL3vRy/I5Gs/PBYSniUV5AOlCsCZ9kKmGsl6UMZmdnOe4d19ihHrLnSDBRddxW5jTL3vQq/I5Gs/PBYQXiUV5MOlCsBZ5kNzLWG9KGMzGznvdM6e5Qj1lxjBoquu5bcRpn7XovfkUhWfnisJjzK60gHypWBs8wB5lpP+lBGZrbz3mWdPcoRa643A0XX3UBuo8x9b8DvSCQrPzzWER7ljaQD5SrAWbYG5tpE+lBGZrbz3m2dPcoRa24yA0XX3Uxuo8x9b8bvSCQrPzw2Eh7lLaQD5arAWbYB5tpK+lBGZrbz3mOdPcoRa241A0XX3UZuo8x9b8PvSCQrPzy2EB7l7aQD5WrAWbYF5tpB+lBGZrbz3mudPcoRa+4wA0XX3Uluo8x978TvSCTrtqDGdsKjvIt0oFwdOMt2wFy7SR/KyMx23vuss0c5Ys3dZqDounvIbZS57z34HYlk5YfHLsKjvJd0oFwDOMv2wFz7SB/KyMx23vuts0c5Ys19ZqDouvvJbZS57/34HYlk5YfHXsKjfIB0oFwTOMsOwFwHSR/KyMx23gess0c5Ys2DZqDouofIbZS570P4HYlk5YfHAcKjfJh0oFwLOMuOwFxHSB/KyMx23gets0c5Ys0jZqDoukfJbZS576P4HYlk5YfHYcKjfIx0oFwbOMtOwFzHSR/KyMx23oess0c5Ys3jZqDouifIbZS57xP4HYlk5YfHMcKjfJJ0oFwHOMvOwFynSB/KyMx23oets0c5Ys1TZqDouqfJbZS579P4HYlk5YfHScKjfIZ0oFwXOMtLgLkeIX0oIzPbeX9lnT3KEWs+YgaKrvsouY0y9/0ofkciWfnhcYbwKP/a8b4fDWr8+gI7Qryf8l/omT5G+qBGZrbz/sY6e6gj1nzMDBRd93FyG2ru+3H8jkQReIL0IYDMbOf9rXX2CESs+YQZKLruk+Q2Atz3k/gdiSLwFOlDAJnZzvs76+wRiFjzKTNQdN2nyW0EuO+n8TsSycpfsTxJeFSeIR34PUv68ENmtvP+3jp7/CLWfNYMFF33OXIbP+77OfyORLIy0s8QHpXnSQd+L5A+/JCZ7bx/sM4ev4g1XzADRdd9kdzGj/t+Eb8jkayM9POER+Ul0oHfy6QPP2RmO+8frbPHL2LNl81A0XVfIbfx475fwe9IJCsj/RLhUXmVdOD3GunDD5nZzvsn6+zxi1jzNTNQdN3XyW38uO/X8TsSycpIv0p4VN4gHfi9SfrwQ2a28/7ZOnv8ItZ80wwUXfctchs/7vst/I5EsjLSbxAelbdJB37vkD78kJntvH+xzh6/iDXfMQNF132X3MaP+34XvyORrIz024RH5T3Sgd/7pA8/ZGY771+ts8cvYs33zUDRdT8gt/Hjvj/A70gkKyP9HuFR+ZB04PcR6cMPmdnO+zfr7PGLWPMjM1B03Y/Jbfy474/xOxLJykh/SHhUPiEd+H1K+vBDZrbz/t06e/wi1vzUDBRd9zNyGz/u+zP8jkSyMtKfEB6Vz0kHfl+QPvyQme28/7DOHr+INb8wA0XX/ZLcxo/7/hK/I5GsjPTnhEflK9KB39ekDz9kZjvvP62zxy9iza/NQNF1vyG38eO+v8HvSCQrI/0V4VH5lnTg9x3pww+Z2c77L+vs8YtY8zszUHTd78lt/Ljv7/E7EsnKSH9LeFR+IB34/Uj68ENmtvP+2zp7/CLW/NEMFF33J3IbP+77J/yORLIy0j8QHpWfSQd+XBCVMy78kJntvEnWjccvas2kswNF101Ochs/LpicBN+RSFZG+mfCo5KSpAO/AgrxKyCEX0GPH3ZJBQXwK+Q4ftx3ISX4MdIpSXhUCivBr4hC/IoI4VfU44ddUlEB/Io5jh/3XUwJfox0YQH8iivBr4RC/EoI4VfS44ddUkkB/Eo5jh/3XUoJfox0cQH8SivBr4xC/MoI4VfW44ddUlkB/Mo5jh/3XU4Jfox0aQH8yivBr4JC/CoI4VfR44ddUkUB/Co5jh/3XUkJfox0eQH8KivBr4pC/KoI4VfV44ddUlUB/Ko5jh/3XU0Jfox0ZQH8qivBr4ZC/GoI4VfT44ddUk0B/Go5jh/3XUsJfox0dQH8aivBr45C/OoI4VfX44ddUl0B/C5yHD/u+yIl+DHStQXwu1gJfvUU4ldPCL/6Hj/skuoL4NfAcfy47wZK8GOkLxbAr6ES/BopxK+REH6NPX7YJTUWwK+J4/hx302U4MdINxTAr6kS/JopxK+ZEH7NPX7YJTUXwK+F4/hx3y2U4MdINxXAr6US/FIV4pcqhF/C44ddUkIAvzTH8eO+05Tgx0i3FMAvXQl+GQrxyxDCL9Pjh11SpgB+WY7jx31nKcGPkU4XwK+VEvyyFeKXLYRfjscPu6QcAfxaO44f991aCX6MdCsB/Noowa+tQvzaCuHXzuOHXVI7AfzaO44f991eCX6MdBsB/Dokud0376fDBXaEeD/lv9Az7QibaU5WXFB3FIK6k4cau6ROAlB3dhxq7ruzENT5LzQCl8A+oVqNiAsBXOZzEbjUI4Bd0qUCCHRxHAHuu4syBC6DfUKl58WFAC7zuQh09Qhgl9RVAIHLHUeA+75cyW/ZOhuw0Kh0U/L9qu6A91LeaH7lZcSFX3ch/K7w+GGXdIUAfj0cx4/77qEEP0a6mwB+PZXg1wuGX3ZuXPj1EsLvSo8fdklXCuDX23H8uO/eSvBjpHsK4NdHCX59YfjlZMaFX18h/K7y+GGXdJUAfv0cx4/77qcEP0a6jwB+/ZXgNwCG38jsuPAbIITf1R4/7JKuFsBvoOP4cd8DleDHSPcXwG+QEvwGw3LG95XfYCH8rvH4YZd0jQB+QxzHj/seogQ/RnqQAH5DleA3DPeVX2zf8xsmhN+1Hj/skq4VwG+44/hx38OV4MdIDxXAb4QS/HJh+I0YGRd+uUL4jfT4YZc0UgC/UY7jx32PUoIfIz1CAL88JfiNhuE3KrY/8T9aCL/rPH7YJV0ngN8Yx/HjvscowY+RzhPAb6wS/MbB8MuJ7Su/cUL4Xe/xwy7pegH8xjuOH/c9Xgl+jPRYAfwmKMFvIgy/vNi+5zdRCL8bPH7YJd0ggN8kx/HjvicpwY+RniCA32Ql+E2B4ZeVExd+U4Twu9Hjh13SjQL4TXUcP+57qhL8GOnJAvhNU4LfdFjOrNi+5zddCL+bPH7YJd0kgN8Mx/HjvmcowY+RniaA30wl+M3C4ZcWF36zhPC72eOHXdLNAvjNdhw/7nu2EvwY6ZkC+M1Rgt9cWM702L7nN1cIv1s8ftgl3SKA3zzH8eO+5ynBj5GeI4DffCX4LYDlzBwVF34LhPC71eOHXdKtAvgtdBw/7nuhEvwY6fkC+C1Sgt9iXM7Y/t3exUL43ebxwy7pNgH8ljiOH/e9RAl+jPQiAfyWKsFvGSxndnpc+C0Twu92jx92SbcL4Lfccfy47+VK8GOklwrgt0IJfithOROxfeW3Ugi/Ozx+2CXdIYDfKsfx475XKcGPkV4hgN9qJfitAbyXzv45v9zY/hr7NUL43enxwy7pTgH81jqOH/e9Vgl+jPRqAfzWKcFvPSxneiIu/NYL4XeXxw+7pLsE8NvgOH7c9wYl+DHS6wTw26gEv02wnKNi+2/ObhLC726PH3ZJdwvgt9lx/LjvzUrwY6Q3CuC3RQl+W3Ff+cX2Pb+tQvjd4/HDLukeAfy2OY4f971NCX6M9BYB/LYrwW8H7Ht+8f22d4cQfvd6/LBLulcAv52O48d971SCHyO9XQC/XUrw2w3LmRbbH3LeLYTffR4/7JLuE8Bvj+P4cd97lODHSO8SwG+vEvz2wXJmxvYXG+wTwu9+jx92SfcL4Lffcfy47/1K8GOk9wrgd0AJfgdxv+2N7f/wOCiE3wMeP+ySHhDA75Dj+HHfh5Tgx0gfEMDvsBL8jsDwy4vt7/M7IoTfgx4/7JIeFMDvqOP4cd9HleDHSB8WwO+YEvyO437bG9tXfseF8HvI44dd0kMC+J1wHD/u+4QS/BjpYwL4nVSC3ynYV34jYvt3e08J4fewxw+7pIcF8DvtOH7c92kl+DHSJwXwO5Mk23fUfLyfMwI7qmvqcGmGgz+RGZKCdBaNwsFVJLiKBlex4CoeXCWCqySd/QQvHVxlgqtscJULrvLBVSG4KgZXpeCqHFxVgqtqcFULrurBVSO4agZXreCqHVx1gqsunf/6D5/MYOLG+QEA","proving_key":null,"verification_key":null} \ No newline at end of file diff --git a/crates/nargo_cli/tests/execution_success/brillig_keccak/target/witness.tr b/crates/nargo_cli/tests/execution_success/brillig_keccak/target/witness.tr index e681896f1c8..6543a668e28 100644 Binary files a/crates/nargo_cli/tests/execution_success/brillig_keccak/target/witness.tr and b/crates/nargo_cli/tests/execution_success/brillig_keccak/target/witness.tr differ diff --git a/crates/nargo_cli/tests/execution_success/brillig_loop/target/brillig_loop.json b/crates/nargo_cli/tests/execution_success/brillig_loop/target/brillig_loop.json index 9810a0ceca1..5fccad8581f 100644 --- a/crates/nargo_cli/tests/execution_success/brillig_loop/target/brillig_loop.json +++ b/crates/nargo_cli/tests/execution_success/brillig_loop/target/brillig_loop.json @@ -1 +1 @@ -{"backend":"acvm-backend-barretenberg","abi":{"parameters":[{"name":"sum","type":{"kind":"integer","sign":"unsigned","width":32},"visibility":"private"}],"param_witnesses":{"sum":[1]},"return_type":null,"return_witnesses":[]},"bytecode":"H4sIAAAAAAAA/+1Xy26DMBA0GJMASQ6t1ENP3HK1eSRw41calfz/JzSktpg4DpXKOonUroS8ONbO7IMwpIyxNfu24HSFes1PF9f+Lev0KudZhRgh+Fu9Jtb+YDE9DxlDvlQxBXBNHHlu9O8RLW4xYHF2aXYvO/AjqKug5SITNtaAA86CFkcF7HouzL3ByoCD8Mglc2D7rq2AXr5qP7DwczgfQk/MmXeI8ab99HQt4exgCRvneQl7Ifgm9xTuOfBK2Wg5nMHnJQMOZm9lxRrOm/+wTq9ynp1naQX8Org3WDhLmUcumQP7HjnnbOzFGupv9sxZe94urCMihzG5FbuUu6rq90WvSvUhi/bQ1LKqD7tGNapu6s+iKcu+qZp9e2j3slVV2atj3ZZHHTwg5BkS8uJ0vOStlznxC09Rcka+kcMPHTPh4SV+NXt2HTfM8+D7aFLkIa5gdMPvK29B3yMJIZ+6ptzBczBUTVvHXkdUJ18K2/R0SmFzWtzC9VUypbDxD4pY7Z9VoLDyjtnYQyKcs0Kwnx9U0YNlwCHyyCVzYPuubQTrC2ASfz1UgZVTDhwMFoczP6l2VOW/Ue3m9ynVjs/glGrH+XyUag/B98XlkapdWLg4w6jazQz/edUeE/Ja0PG6m2qn5Ix8l+D/q/aZMRe6oNRxE/bcqn3IO6HvkVfVTlnTwOKI9gW5/dAvwBcAAA==","proving_key":null,"verification_key":null} \ No newline at end of file +{"backend":"acvm-backend-barretenberg","abi":{"parameters":[{"name":"sum","type":{"kind":"integer","sign":"unsigned","width":32},"visibility":"private"}],"param_witnesses":{"sum":[1]},"return_type":null,"return_witnesses":[]},"bytecode":"H4sIAAAAAAAA/+1UbQrCMAxN9ykInsEjJGu7pf+8isPu/kdQWYWs+G8pTPBBSWjhJS8p7wQAZ1hhXqdK8QpbVCneUrQ4OhenIZKlOw5hZo/OzyMTk2f/GNjayI6nMIcJAzkbafHBLrjCKHJVggv3YayT/hwm04/7QIo9o+y3FnmT7U6+dwU0QVYnn+Ply51q8RJLqgvwNqD3+UvpbvR3hILy8DP94MjG18LvGZ9izxvj60T+N76dnG2hQfVwbON76+71d1TU+DRnarIeJZ6CTQW3KgoAAA==","proving_key":null,"verification_key":null} \ No newline at end of file diff --git a/crates/nargo_cli/tests/execution_success/brillig_loop/target/witness.tr b/crates/nargo_cli/tests/execution_success/brillig_loop/target/witness.tr index 2c6ad2a37f5..c366b559bbe 100644 Binary files a/crates/nargo_cli/tests/execution_success/brillig_loop/target/witness.tr and b/crates/nargo_cli/tests/execution_success/brillig_loop/target/witness.tr differ diff --git a/crates/nargo_cli/tests/execution_success/brillig_modulo/target/brillig_modulo.json b/crates/nargo_cli/tests/execution_success/brillig_modulo/target/brillig_modulo.json index 28c1c1c791a..cf6aa51a479 100644 --- a/crates/nargo_cli/tests/execution_success/brillig_modulo/target/brillig_modulo.json +++ b/crates/nargo_cli/tests/execution_success/brillig_modulo/target/brillig_modulo.json @@ -1 +1 @@ -{"backend":"acvm-backend-barretenberg","abi":{"parameters":[],"param_witnesses":{},"return_type":null,"return_witnesses":[]},"bytecode":"H4sIAAAAAAAA/+2X306zQBDFjy2ltp/l06pVqyZee7X8s3Dnq9hI3/8RLGGbDAbjxZ5poO4khGmBA3NmJ7/sM4BXNDHeHyP8HO/2bJwi2elpm/RMaMp8Yc8zW6e8PuuoO+R/mwnFO1mak73GVNRxiEM9kX3nxP4O9seLuC69CMWzL+IZeU8gcqkTim8IxbsO/32/b4p2f5imxOgwgqV9JrRS85Zl1Sap4jT+MEm5LXKT5du3Ii7ivMg/kyJNqyIrNuW23JgyztIq3uVlurNiI4JWtWti3GUo6AsuHvG0jPzesciDb72T1xWGqLVeunyMoLxYNZo0VtANwFv8WnUH/B4ZIdlrT49DcJMoarcIPhG5J3gTnuCO2hPwBi7E8AgeQofgU5F7gjtqhmhPGEv3HP0meF33Ob9HqgRnenokgueK2i2CS6J5gjdR1x7gd4IH+J3gUufPEHwG3sDNMTyCz6FD8H8i9wR31JxbQ9m6F+g3weu6L/g9UiU409NT24MvOnJPcE9wJ+0FeAMXYXgEj6BD8P8i9wR31IysoWzdS/Sb4HXdl/weqRKc6ekJ7ME/5aBdidwTvAlPcEftK/AGbonhEXwJHYJfi9wT3FFzaQ1l696g3wSv677h90iV4ExPj0TwraJ2aw9+K3JP8CY8wR21b8EbuBVoBE+PRfAVdAh+J3JPcEfNlTWUrXuPfhO8rvue3yNVgjM9PQGCt/bgDyL3BG/CE9xR+wG8gVtjeARfQ4fgjyL3BHfUXFtD2bpP6DfB67qf+D1SJTjb0674AuvcvRByNgAA","proving_key":null,"verification_key":null} \ No newline at end of file +{"backend":"acvm-backend-barretenberg","abi":{"parameters":[],"param_witnesses":{},"return_type":null,"return_witnesses":[]},"bytecode":"H4sIAAAAAAAA/+2UTW6DMBCFJ4EmTZq/Jk0v0AvYGILZ9SpFJfc/QoXqxYjgbOZZsiV7g8Xi8c0b9H0S0Rc9noV7frunUbe6Htpq0Eb/qKrrbaPqpr9ZbXVjm9/KGjPY2rZd37Wq07UZ9L3pzF39nwXLUsJTML5nzEp2NJKZ8y7ZvZx5V7jnKsBMNPnOtMfDzDvox0MsaRkgtyDczx9q7gK/I8Uio+/UxynNLik9WSGZOe8Lu2dZCTNLVyg6d0Vxy6p0Pwh4R0Flhe7UxynNXlN6skIyc95Xds+yEmauXaHo3A3FLatx7g1+R0Flhe7UxynN3lJ6skIyc943ds+yEmZuXaHo3B3FLatx7h1+R0Flhe7UxynN3lN6skIyc94Du2dZCTP3E2hU7pHiltU49xG/o6CyQnfq45Rmnyg9WSGZOe87u2dZCTNPrlB07pniltU49xm/o6CyQnfq45RmXyg9WSGZOe8Hu2dZCTMvrlB07pXiltU49xW/o6CyQnc6d/4A6Mmsp98gAAA=","proving_key":null,"verification_key":null} \ No newline at end of file diff --git a/crates/nargo_cli/tests/execution_success/brillig_modulo/target/witness.tr b/crates/nargo_cli/tests/execution_success/brillig_modulo/target/witness.tr index 1c2eb70c848..43dd343e821 100644 Binary files a/crates/nargo_cli/tests/execution_success/brillig_modulo/target/witness.tr and b/crates/nargo_cli/tests/execution_success/brillig_modulo/target/witness.tr differ diff --git a/crates/nargo_cli/tests/execution_success/brillig_to_bits/target/brillig_to_bits.json b/crates/nargo_cli/tests/execution_success/brillig_to_bits/target/brillig_to_bits.json deleted file mode 100644 index 01a973d5190..00000000000 --- a/crates/nargo_cli/tests/execution_success/brillig_to_bits/target/brillig_to_bits.json +++ /dev/null @@ -1 +0,0 @@ -{"backend":"acvm-backend-barretenberg","abi":{"parameters":[],"param_witnesses":{},"return_type":null,"return_witnesses":[]},"bytecode":"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","proving_key":null,"verification_key":null} \ No newline at end of file diff --git a/crates/nargo_cli/tests/execution_success/brillig_to_bits/target/witness.tr b/crates/nargo_cli/tests/execution_success/brillig_to_bits/target/witness.tr deleted file mode 100644 index 4e90289d5e1..00000000000 Binary files a/crates/nargo_cli/tests/execution_success/brillig_to_bits/target/witness.tr and /dev/null differ diff --git a/crates/nargo_cli/tests/execution_success/references/src/main.nr b/crates/nargo_cli/tests/execution_success/references/src/main.nr index aeed8e0b901..ec23f2f3a38 100644 --- a/crates/nargo_cli/tests/execution_success/references/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/references/src/main.nr @@ -34,6 +34,13 @@ fn main(mut x: Field) { regression_1887(); regression_2054(); regression_2030(); + regression_2255(); + + assert(x == 3); + regression_2218_if_inner_if(x, 10); + regression_2218_if_inner_else(20, x); + regression_2218_else(x, 3); + regression_2218_loop(x, 10); } fn add1(x: &mut Field) { @@ -97,3 +104,129 @@ fn regression_2030() { let _ = *array[0]; *array[0] = 1; } + +// The `mut x: &mut ...` caught a bug handling lvalues where a double-dereference would occur internally +// in one step rather than being tracked by two separate steps. This lead to assigning the 1 value to the +// incorrect outer `mut` reference rather than the correct `&mut` reference. +fn regression_2255() { + let x = &mut 0; + regression_2255_helper(x); + assert(*x == 1); +} + +fn regression_2255_helper(mut x: &mut Field) { + *x = 1; +} + +fn regression_2218(x: Field, y: Field) -> Field { + let q = &mut &mut 0; + let q1 = *q; + let q2 = *q; + + if x != y { + *q1 = 1; + // Make sure that we correct load reference aliases through multiple blocks + if x != 20 { + *q1 = 10; + *q2 = 2; // now we'd expect q1 == q2 == 2 + + assert(*q1 == 2); + } else { + *q2 = 15; + assert(*q1 == 15); + } + } else { + *q2 = 20; + assert(*q1 == 20); + } + // Have to assign value to return it + let value = *q1; + value +} + +fn regression_2218_if_inner_if(x: Field, y: Field) { + let value = regression_2218(x, y); + assert(value == 2); +} + +fn regression_2218_if_inner_else(x: Field, y: Field) { + let value = regression_2218(x, y); + assert(value == 15); +} + +fn regression_2218_else(x: Field, y: Field) { + let value = regression_2218(x, y); + assert(value == 20); +} + +fn regression_2218_loop(x: Field, y: Field) { + let q = &mut &mut 0; + let q1 = *q; + let q2 = *q; + + for _ in 0..1 { + if x != y { + *q1 = 10; + *q2 = 2; // now we'd expect q1 == q2 == 2 + + assert(*q1 == 2); + } else { + *q2 = 20; + assert(*q1 == 20); + } + } + assert(*q1 == 2); + + for _ in 0..1 { + for _ in 0..5 { + if x != y { + *q1 = 1; + // Make sure that we correct load reference aliases through multiple blocks + if x != 20 { + *q1 = 10; + *q2 = 2; // now we'd expect q1 == q2 == 2 + + assert(*q1 == 2); + } + } else { + *q2 = 20; + assert(*q1 == 20); + } + } + if x != y { + *q1 = 1; + for _ in 0..5 { + // Make sure that we correct load reference aliases through multiple blocks + if x != 20 { + *q1 = 10; + *q2 = 2; // now we'd expect q1 == q2 == 2 + + assert(*q1 == 2); + } + } + } else { + *q2 = 20; + assert(*q1 == 20); + } + } + assert(*q1 == 2); + + if x != y { + for _ in 0..5 { + if x != y { + *q1 = 1; + // Make sure that we correct load reference aliases through multiple blocks + if x != 20 { + *q1 = 10; + *q2 = 2; // now we'd expect q1 == q2 == 2 + + assert(*q1 == 2); + } + } + } + } else { + *q2 = 20; + assert(*q1 == 20); + } + assert(*q1 == 2); +} \ No newline at end of file diff --git a/crates/nargo_cli/tests/execution_success/regression_2099/target/regression_2099.json b/crates/nargo_cli/tests/execution_success/regression_2099/target/regression_2099.json deleted file mode 100644 index 4c3bb072cb3..00000000000 --- a/crates/nargo_cli/tests/execution_success/regression_2099/target/regression_2099.json +++ /dev/null @@ -1 +0,0 @@ -{"backend":"acvm-backend-barretenberg","abi":{"parameters":[],"param_witnesses":{},"return_type":null,"return_witnesses":[]},"bytecode":"H4sIAAAAAAAA/2NkIAwAQGbG/yQAAAA=","proving_key":null,"verification_key":null} \ No newline at end of file diff --git a/crates/nargo_cli/tests/execution_success/regression_2099/target/witness.tr b/crates/nargo_cli/tests/execution_success/regression_2099/target/witness.tr deleted file mode 100644 index 4e90289d5e1..00000000000 Binary files a/crates/nargo_cli/tests/execution_success/regression_2099/target/witness.tr and /dev/null differ diff --git a/crates/nargo_cli/tests/execution_success/str_as_bytes/Nargo.toml b/crates/nargo_cli/tests/execution_success/str_as_bytes/Nargo.toml new file mode 100644 index 00000000000..31f7ab5d9b4 --- /dev/null +++ b/crates/nargo_cli/tests/execution_success/str_as_bytes/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "str_as_bytes" +type = "bin" +authors = [""] +compiler_version = "0.9.0" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/execution_success/str_as_bytes/src/main.nr b/crates/nargo_cli/tests/execution_success/str_as_bytes/src/main.nr new file mode 100644 index 00000000000..6890b156b15 --- /dev/null +++ b/crates/nargo_cli/tests/execution_success/str_as_bytes/src/main.nr @@ -0,0 +1,18 @@ +use dep::std; +fn main() { + let a = "hello"; + let b = a.as_bytes(); + assert(b[0]==104); + assert(b[1]==101); + assert(b[2]==108); + assert(b[3]==108); + assert(b[4]==111); + assert(b.len()==5); + let mut c = a.as_bytes_vec(); + assert(c.get(0)==104); + assert(c.get(1)==101); + assert(c.get(2)==108); + assert(c.get(3)==108); + assert(c.get(4)==111); + assert(c.len()==5); +} diff --git a/crates/nargo_cli/tests/execution_success/unconstrained_empty/Prover.toml b/crates/nargo_cli/tests/execution_success/unconstrained_empty/Prover.toml deleted file mode 100644 index 11497a473bc..00000000000 --- a/crates/nargo_cli/tests/execution_success/unconstrained_empty/Prover.toml +++ /dev/null @@ -1 +0,0 @@ -x = "0" diff --git a/crates/nargo_cli/tests/execution_success/unconstrained_empty/target/unconstrained_empty.json b/crates/nargo_cli/tests/execution_success/unconstrained_empty/target/unconstrained_empty.json deleted file mode 100644 index d6e9953cd42..00000000000 --- a/crates/nargo_cli/tests/execution_success/unconstrained_empty/target/unconstrained_empty.json +++ /dev/null @@ -1 +0,0 @@ -{"backend":"acvm-backend-barretenberg","abi":{"parameters":[],"param_witnesses":{},"return_type":null,"return_witnesses":[]},"bytecode":"H4sIAAAAAAAA/62Puw0AIAhEj6jYOYtu4P5TqZHiGip4CbmE4j4CQPAp8GmmSr9tOoModcjyfH2reY17nTKyByw4HCZCYW5dAQAA","proving_key":null,"verification_key":null} \ No newline at end of file diff --git a/crates/nargo_cli/tests/execution_success/unconstrained_empty/target/witness.tr b/crates/nargo_cli/tests/execution_success/unconstrained_empty/target/witness.tr deleted file mode 100644 index 4e90289d5e1..00000000000 Binary files a/crates/nargo_cli/tests/execution_success/unconstrained_empty/target/witness.tr and /dev/null differ diff --git a/crates/nargo_cli/tests/test_libraries/bad_name/Nargo.toml b/crates/nargo_cli/tests/test_libraries/bad_name/Nargo.toml new file mode 100644 index 00000000000..313e2411e83 --- /dev/null +++ b/crates/nargo_cli/tests/test_libraries/bad_name/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "bad-name" +type = "lib" +authors = [""] +compiler_version = "0.7.1" + +[dependencies] diff --git a/crates/nargo_cli/tests/execution_success/brillig_cast/Prover.toml b/crates/nargo_cli/tests/test_libraries/bad_name/src/lib.nr similarity index 100% rename from crates/nargo_cli/tests/execution_success/brillig_cast/Prover.toml rename to crates/nargo_cli/tests/test_libraries/bad_name/src/lib.nr diff --git a/crates/nargo_toml/src/errors.rs b/crates/nargo_toml/src/errors.rs index 42c5a59cba6..2b68f681f92 100644 --- a/crates/nargo_toml/src/errors.rs +++ b/crates/nargo_toml/src/errors.rs @@ -38,9 +38,11 @@ pub enum ManifestError { )] MissingDefaultEntryFile { toml: PathBuf, entry: PathBuf, package_type: PackageType }, - /// Invalid character `-` in package name - #[error("invalid character `-` in package name")] - InvalidPackageName, + #[error("{} found in {toml}", if name.is_empty() { "Empty package name".into() } else { format!("Invalid package name `{name}`") })] + InvalidPackageName { toml: PathBuf, name: String }, + + #[error("{} found in {toml}", if name.is_empty() { "Empty dependency name".into() } else { format!("Invalid dependency name `{name}`") })] + InvalidDependencyName { toml: PathBuf, name: String }, /// Encountered error while downloading git repository. #[error("{0}")] @@ -57,4 +59,7 @@ pub enum ManifestError { #[error("Missing `name` field in {toml}")] MissingNameField { toml: PathBuf }, + + #[error("No common ancestor between {root} and {current}")] + NoCommonAncestor { root: PathBuf, current: PathBuf }, } diff --git a/crates/nargo_toml/src/lib.rs b/crates/nargo_toml/src/lib.rs index 4a9238ee021..8372942931b 100644 --- a/crates/nargo_toml/src/lib.rs +++ b/crates/nargo_toml/src/lib.rs @@ -1,7 +1,6 @@ use std::{ collections::BTreeMap, - fs::ReadDir, - path::{Path, PathBuf}, + path::{Component, Path, PathBuf}, }; use fm::{NormalizePath, FILE_EXTENSION}; @@ -18,11 +17,12 @@ mod git; pub use errors::ManifestError; use git::clone_git_repo; -/// Returns the path of the root directory of the package containing `current_path`. +/// Returns the [PathBuf] of the directory containing the `Nargo.toml` by searching from `current_path` to the root of its [Path]. /// -/// Returns a `CliError` if no parent directories of `current_path` contain a manifest file. +/// Returns a [ManifestError] if no parent directories of `current_path` contain a manifest file. pub fn find_package_root(current_path: &Path) -> Result { - let manifest_path = find_package_manifest(current_path)?; + let root = path_root(current_path); + let manifest_path = find_package_manifest(&root, current_path)?; let package_root = manifest_path.parent().expect("infallible: manifest file path can't be root directory"); @@ -30,36 +30,58 @@ pub fn find_package_root(current_path: &Path) -> Result Ok(package_root.to_path_buf()) } -/// Returns the path of the manifest file (`Nargo.toml`) of the package containing `current_path`. -/// -/// Returns a `CliError` if no parent directories of `current_path` contain a manifest file. -pub fn find_package_manifest(current_path: &Path) -> Result { - current_path - .ancestors() - .find_map(|dir| find_file(dir, "Nargo", "toml")) - .ok_or_else(|| ManifestError::MissingFile(current_path.to_path_buf())) -} - -// Looks for file named `file_name` in path -fn find_file>(path: P, file_name: &str, extension: &str) -> Option { - let entries = list_files_and_folders_in(path)?; - let file_name = format!("{file_name}.{extension}"); +// TODO(#2323): We are probably going to need a "filepath utils" crate soon +fn path_root(path: &Path) -> PathBuf { + let mut components = path.components(); - find_artifact(entries, &file_name) + match (components.next(), components.next()) { + // Preserve prefix if one exists + (Some(prefix @ Component::Prefix(_)), Some(root @ Component::RootDir)) => { + PathBuf::from(prefix.as_os_str()).join(root.as_os_str()) + } + (Some(root @ Component::RootDir), _) => PathBuf::from(root.as_os_str()), + _ => PathBuf::new(), + } } -// There is no distinction between files and folders -fn find_artifact(entries: ReadDir, artifact_name: &str) -> Option { - let entry = entries - .into_iter() - .flatten() - .find(|entry| entry.file_name().to_str() == Some(artifact_name))?; +/// Returns the [PathBuf] of the `Nargo.toml` file by searching from `current_path` and stopping at `root_path`. +/// +/// Returns a [ManifestError] if no parent directories of `current_path` contain a manifest file. +pub fn find_package_manifest( + root_path: &Path, + current_path: &Path, +) -> Result { + if current_path.starts_with(root_path) { + let mut found_toml_paths = Vec::new(); + for path in current_path.ancestors() { + if let Ok(toml_path) = get_package_manifest(path) { + found_toml_paths.push(toml_path); + } + // While traversing, break once we process the root specified + if path == root_path { + break; + } + } - Some(entry.path()) + // Return the shallowest Nargo.toml, which will be the last in the list + found_toml_paths.pop().ok_or_else(|| ManifestError::MissingFile(current_path.to_path_buf())) + } else { + Err(ManifestError::NoCommonAncestor { + root: root_path.to_path_buf(), + current: current_path.to_path_buf(), + }) + } } - -fn list_files_and_folders_in>(path: P) -> Option { - std::fs::read_dir(path).ok() +/// Returns the [PathBuf] of the `Nargo.toml` file in the `current_path` directory. +/// +/// Returns a [ManifestError] if `current_path` does not contain a manifest file. +pub fn get_package_manifest(current_path: &Path) -> Result { + let toml_path = current_path.join("Nargo.toml"); + if toml_path.exists() { + Ok(toml_path) + } else { + Err(ManifestError::MissingFile(current_path.to_path_buf())) + } } #[derive(Debug, Deserialize, Clone)] @@ -72,14 +94,20 @@ struct PackageConfig { impl PackageConfig { fn resolve_to_package(&self, root_dir: &Path) -> Result { let name = if let Some(name) = &self.package.name { - name.parse().map_err(|_| ManifestError::InvalidPackageName)? + name.parse().map_err(|_| ManifestError::InvalidPackageName { + toml: root_dir.join("Nargo.toml"), + name: name.into(), + })? } else { return Err(ManifestError::MissingNameField { toml: root_dir.join("Nargo.toml") }); }; let mut dependencies: BTreeMap = BTreeMap::new(); for (name, dep_config) in self.dependencies.iter() { - let name = name.parse().map_err(|_| ManifestError::InvalidPackageName)?; + let name = name.parse().map_err(|_| ManifestError::InvalidDependencyName { + toml: root_dir.join("Nargo.toml"), + name: name.into(), + })?; let resolved_dep = dep_config.resolve_to_dependency(root_dir)?; dependencies.insert(name, resolved_dep); @@ -243,19 +271,20 @@ impl DependencyConfig { fn toml_to_workspace( nargo_toml: NargoToml, - selected_package: Option, + package_selection: PackageSelection, ) -> Result { let workspace = match nargo_toml.config { Config::Package { package_config } => { let member = package_config.resolve_to_package(&nargo_toml.root_dir)?; - if selected_package.is_none() || Some(&member.name) == selected_package.as_ref() { - Workspace { + match &package_selection { + PackageSelection::Selected(selected_name) if selected_name != &member.name => { + return Err(ManifestError::MissingSelectedPackage(member.name)) + } + _ => Workspace { root_dir: nargo_toml.root_dir, selected_package_index: Some(0), members: vec![member], - } - } else { - return Err(ManifestError::MissingSelectedPackage(member.name)); + }, } } Config::Workspace { workspace_config } => { @@ -266,17 +295,18 @@ fn toml_to_workspace( let package_toml_path = package_root_dir.join("Nargo.toml"); let member = resolve_package_from_toml(&package_toml_path)?; - match selected_package.as_ref() { - Some(selected_name) => { + match &package_selection { + PackageSelection::Selected(selected_name) => { if &member.name == selected_name { selected_package_index = Some(index); } } - None => { + PackageSelection::DefaultOrAll => { if Some(&member_path) == workspace_config.default_member.as_ref() { selected_package_index = Some(index); } } + PackageSelection::All => selected_package_index = None, } members.push(member); @@ -284,13 +314,21 @@ fn toml_to_workspace( // If the selected_package_index is still `None` but we have see a default_member or selected package, // we want to present an error to users - if selected_package_index.is_none() { - if let Some(selected_name) = selected_package { - return Err(ManifestError::MissingSelectedPackage(selected_name)); - } - if let Some(default_path) = workspace_config.default_member { - return Err(ManifestError::MissingDefaultPackage(default_path)); + match package_selection { + PackageSelection::Selected(selected_name) => { + if selected_package_index.is_none() { + return Err(ManifestError::MissingSelectedPackage(selected_name)); + } } + PackageSelection::DefaultOrAll => match workspace_config.default_member { + // If `default-member` is specified but we don't have a selected_package_index, we need to fail + Some(default_path) if selected_package_index.is_none() => { + return Err(ManifestError::MissingDefaultPackage(default_path)); + } + // However, if there wasn't a `default-member`, we select All, so no error is needed + _ => (), + }, + PackageSelection::All => (), } Workspace { root_dir: nargo_toml.root_dir, members, selected_package_index } @@ -325,14 +363,21 @@ fn resolve_package_from_toml(toml_path: &Path) -> Result } } +#[derive(Debug, PartialEq, Eq)] +pub enum PackageSelection { + Selected(CrateName), + DefaultOrAll, + All, +} + /// Resolves a Nargo.toml file into a `Workspace` struct as defined by our `nargo` core. pub fn resolve_workspace_from_toml( toml_path: &Path, - selected_package: Option, + package_selection: PackageSelection, ) -> Result { let nargo_toml = read_toml(toml_path)?; - toml_to_workspace(nargo_toml, selected_package) + toml_to_workspace(nargo_toml, package_selection) } #[test] diff --git a/crates/noirc_abi/src/input_parser/json.rs b/crates/noirc_abi/src/input_parser/json.rs index 6468b48c857..c8a421c8353 100644 --- a/crates/noirc_abi/src/input_parser/json.rs +++ b/crates/noirc_abi/src/input_parser/json.rs @@ -96,7 +96,7 @@ impl JsonTypes { (InputValue::String(s), AbiType::String { .. }) => JsonTypes::String(s.to_string()), - (InputValue::Struct(map), AbiType::Struct { fields }) => { + (InputValue::Struct(map), AbiType::Struct { fields, .. }) => { let map_with_json_types = try_btree_map(fields, |(key, field_type)| { JsonTypes::try_from_input_value(&map[key], field_type) .map(|json_value| (key.to_owned(), json_value)) @@ -155,7 +155,7 @@ impl InputValue { InputValue::Vec(array_elements) } - (JsonTypes::Table(table), AbiType::Struct { fields }) => { + (JsonTypes::Table(table), AbiType::Struct { fields, .. }) => { let native_table = try_btree_map(fields, |(field_name, abi_type)| { // Check that json contains a value for each field of the struct. let field_id = format!("{arg_name}.{field_name}"); diff --git a/crates/noirc_abi/src/input_parser/mod.rs b/crates/noirc_abi/src/input_parser/mod.rs index e4adbb3d8cf..3a317697534 100644 --- a/crates/noirc_abi/src/input_parser/mod.rs +++ b/crates/noirc_abi/src/input_parser/mod.rs @@ -162,6 +162,7 @@ mod serialization_tests { AbiParameter { name: "bar".into(), typ: AbiType::Struct { + name: "MyStruct".into(), fields: vec![ ("field1".into(), AbiType::Integer { sign: Sign::Unsigned, width: 8 }), ( diff --git a/crates/noirc_abi/src/input_parser/toml.rs b/crates/noirc_abi/src/input_parser/toml.rs index d96496410d9..3f7ec30e355 100644 --- a/crates/noirc_abi/src/input_parser/toml.rs +++ b/crates/noirc_abi/src/input_parser/toml.rs @@ -94,7 +94,7 @@ impl TomlTypes { (InputValue::String(s), AbiType::String { .. }) => TomlTypes::String(s.to_string()), - (InputValue::Struct(map), AbiType::Struct { fields }) => { + (InputValue::Struct(map), AbiType::Struct { fields, .. }) => { let map_with_toml_types = try_btree_map(fields, |(key, field_type)| { TomlTypes::try_from_input_value(&map[key], field_type) .map(|toml_value| (key.to_owned(), toml_value)) @@ -138,7 +138,7 @@ impl InputValue { InputValue::Vec(array_elements) } - (TomlTypes::Table(table), AbiType::Struct { fields }) => { + (TomlTypes::Table(table), AbiType::Struct { fields, .. }) => { let native_table = try_btree_map(fields, |(field_name, abi_type)| { // Check that json contains a value for each field of the struct. let field_id = format!("{arg_name}.{field_name}"); diff --git a/crates/noirc_abi/src/lib.rs b/crates/noirc_abi/src/lib.rs index 5f8c22a6652..4c394fb9ff0 100644 --- a/crates/noirc_abi/src/lib.rs +++ b/crates/noirc_abi/src/lib.rs @@ -55,6 +55,7 @@ pub enum AbiType { }, Boolean, Struct { + name: String, #[serde( serialize_with = "serialization::serialize_struct_fields", deserialize_with = "serialization::deserialize_struct_fields" @@ -308,7 +309,7 @@ impl Abi { encoded_value.extend(str_as_fields); } - (InputValue::Struct(object), AbiType::Struct { fields }) => { + (InputValue::Struct(object), AbiType::Struct { fields, .. }) => { for (field, typ) in fields { encoded_value.extend(Self::encode_value(object[field].clone(), typ)?); } diff --git a/crates/noirc_abi/src/serialization.rs b/crates/noirc_abi/src/serialization.rs index 3fdf5507383..7d32874bb7e 100644 --- a/crates/noirc_abi/src/serialization.rs +++ b/crates/noirc_abi/src/serialization.rs @@ -92,6 +92,7 @@ mod tests { \"name\":\"thing3\", \"type\": { \"kind\":\"struct\", + \"name\": \"MyStruct\", \"fields\": [ { \"name\": \"field1\", @@ -119,6 +120,7 @@ mod tests { let expected_struct = AbiParameter { name: "thing3".to_string(), typ: AbiType::Struct { + name: "MyStruct".to_string(), fields: vec![ ("field1".to_string(), AbiType::Integer { sign: Sign::Unsigned, width: 3 }), ( diff --git a/crates/noirc_driver/src/contract.rs b/crates/noirc_driver/src/contract.rs index a25d258c9be..a1820ff2e47 100644 --- a/crates/noirc_driver/src/contract.rs +++ b/crates/noirc_driver/src/contract.rs @@ -1,6 +1,7 @@ use crate::program::{deserialize_circuit, serialize_circuit}; use acvm::acir::circuit::Circuit; use noirc_abi::Abi; +use noirc_errors::debug_info::DebugInfo; use serde::{Deserialize, Serialize}; /// Describes the types of smart contract functions that are allowed. @@ -47,6 +48,8 @@ pub struct ContractFunction { #[serde(serialize_with = "serialize_circuit", deserialize_with = "deserialize_circuit")] pub bytecode: Circuit, + + pub debug: DebugInfo, } impl ContractFunctionType { diff --git a/crates/noirc_driver/src/lib.rs b/crates/noirc_driver/src/lib.rs index 2919d2fd3ea..a4341e3a37b 100644 --- a/crates/noirc_driver/src/lib.rs +++ b/crates/noirc_driver/src/lib.rs @@ -151,12 +151,10 @@ pub fn compile_main( Some(m) => m, None => { // TODO(#2155): This error might be a better to exist in Nargo - let err = FileDiagnostic { - file_id: FileId::default(), - diagnostic: CustomDiagnostic::from_message( - "cannot compile crate into a program as it does not contain a `main` function", - ), - }; + let err = CustomDiagnostic::from_message( + "cannot compile crate into a program as it does not contain a `main` function", + ) + .in_file(FileId::default()); return Err(vec![err]); } }; @@ -226,13 +224,13 @@ fn compile_contract( options: &CompileOptions, ) -> Result> { let mut functions = Vec::new(); - let mut errs = Vec::new(); + let mut errors = Vec::new(); for function_id in &contract.functions { let name = context.function_name(function_id).to_owned(); let function = match compile_no_check(context, options, *function_id) { Ok(function) => function, - Err(err) => { - errs.push(err); + Err(new_error) => { + errors.push(new_error); continue; } }; @@ -249,13 +247,14 @@ fn compile_contract( is_internal: func_meta.is_internal.unwrap_or(false), abi: function.abi, bytecode: function.circuit, + debug: function.debug, }); } - if errs.is_empty() { + if errors.is_empty() { Ok(CompiledContract { name: contract.name, functions }) } else { - Err(errs) + Err(errors) } } diff --git a/crates/noirc_errors/src/debug_info.rs b/crates/noirc_errors/src/debug_info.rs index 3217dba7a38..56c9699042e 100644 --- a/crates/noirc_errors/src/debug_info.rs +++ b/crates/noirc_errors/src/debug_info.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::BTreeMap; use crate::Location; use serde::{Deserialize, Serialize}; @@ -6,11 +6,11 @@ use serde::{Deserialize, Serialize}; #[derive(Default, Debug, Clone, Deserialize, Serialize)] pub struct DebugInfo { /// Map opcode index of an ACIR circuit into the source code location - pub locations: HashMap, + pub locations: BTreeMap>, } impl DebugInfo { - pub fn new(locations: HashMap) -> Self { + pub fn new(locations: BTreeMap>) -> Self { DebugInfo { locations } } @@ -24,16 +24,16 @@ impl DebugInfo { /// This is the case during fallback or width 'optimization' /// opcode_indices is this list of mixed indices pub fn update_acir(&mut self, opcode_indices: Vec) { - let mut new_locations = HashMap::new(); + let mut new_locations = BTreeMap::new(); for (i, idx) in opcode_indices.iter().enumerate() { if self.locations.contains_key(idx) { - new_locations.insert(i, self.locations[idx]); + new_locations.insert(i, self.locations[idx].clone()); } } self.locations = new_locations; } - pub fn opcode_location(&self, idx: usize) -> Option<&Location> { - self.locations.get(&idx) + pub fn opcode_location(&self, idx: usize) -> Option> { + self.locations.get(&idx).cloned() } } diff --git a/crates/noirc_errors/src/lib.rs b/crates/noirc_errors/src/lib.rs index 43d5715fc54..6eaa96e0cb1 100644 --- a/crates/noirc_errors/src/lib.rs +++ b/crates/noirc_errors/src/lib.rs @@ -13,6 +13,21 @@ pub use reporter::{CustomDiagnostic, DiagnosticKind}; pub struct FileDiagnostic { pub file_id: fm::FileId, pub diagnostic: CustomDiagnostic, + + /// An optional call stack to display the full runtime call stack + /// leading up to a runtime error. If this is empty it will not be displayed. + pub call_stack: Vec, +} + +impl FileDiagnostic { + pub fn new(file_id: fm::FileId, diagnostic: CustomDiagnostic) -> FileDiagnostic { + FileDiagnostic { file_id, diagnostic, call_stack: Vec::new() } + } + + pub fn with_call_stack(mut self, call_stack: Vec) -> Self { + self.call_stack = call_stack; + self + } } impl From for Vec { diff --git a/crates/noirc_errors/src/reporter.rs b/crates/noirc_errors/src/reporter.rs index 23b6970a913..8f4cdd9b551 100644 --- a/crates/noirc_errors/src/reporter.rs +++ b/crates/noirc_errors/src/reporter.rs @@ -1,4 +1,4 @@ -use crate::{FileDiagnostic, Span}; +use crate::{FileDiagnostic, Location, Span}; use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::term; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; @@ -60,7 +60,7 @@ impl CustomDiagnostic { } pub fn in_file(self, file_id: fm::FileId) -> FileDiagnostic { - FileDiagnostic { file_id, diagnostic: self } + FileDiagnostic::new(file_id, self) } pub fn add_note(&mut self, message: String) { @@ -75,7 +75,7 @@ impl CustomDiagnostic { matches!(self.kind, DiagnosticKind::Error) } - pub fn is_warrning(&self) -> bool { + pub fn is_warning(&self) -> bool { matches!(self.kind, DiagnosticKind::Warning) } } @@ -115,25 +115,31 @@ pub fn report_all( diagnostics: &[FileDiagnostic], deny_warnings: bool, ) -> ReportedErrors { - let error_count = diagnostics - .iter() - .map(|error| report(files, &error.diagnostic, Some(error.file_id), deny_warnings) as u32) - .sum(); + let error_count = + diagnostics.iter().map(|error| error.report(files, deny_warnings) as u32).sum(); ReportedErrors { error_count } } +impl FileDiagnostic { + pub fn report(&self, files: &fm::FileManager, deny_warnings: bool) -> bool { + report(files, &self.diagnostic, Some(self.file_id), &self.call_stack, deny_warnings) + } +} + /// Report the given diagnostic, and return true if it was an error pub fn report( files: &fm::FileManager, custom_diagnostic: &CustomDiagnostic, file: Option, + call_stack: &[Location], deny_warnings: bool, ) -> bool { let writer = StandardStream::stderr(ColorChoice::Always); let config = codespan_reporting::term::Config::default(); - let diagnostic = convert_diagnostic(custom_diagnostic, file, deny_warnings); + let stack_trace = stack_trace(files, call_stack); + let diagnostic = convert_diagnostic(custom_diagnostic, file, stack_trace, deny_warnings); term::emit(&mut writer.lock(), &config, files.as_simple_files(), &diagnostic).unwrap(); deny_warnings || custom_diagnostic.is_error() @@ -142,6 +148,7 @@ pub fn report( fn convert_diagnostic( cd: &CustomDiagnostic, file: Option, + stack_trace: String, deny_warnings: bool, ) -> Diagnostic { let diagnostic = match (cd.kind, deny_warnings) { @@ -162,5 +169,46 @@ fn convert_diagnostic( vec![] }; - diagnostic.with_message(&cd.message).with_labels(secondary_labels).with_notes(cd.notes.clone()) + let mut notes = cd.notes.clone(); + notes.push(stack_trace); + + diagnostic.with_message(&cd.message).with_labels(secondary_labels).with_notes(notes) +} + +fn stack_trace(files: &fm::FileManager, call_stack: &[Location]) -> String { + if call_stack.is_empty() { + return String::new(); + } + + let mut result = "Call stack:\n".to_string(); + + for (i, call_item) in call_stack.iter().enumerate() { + let path = files.path(call_item.file); + let source = files.fetch_file(call_item.file).source(); + + let (line, column) = location(source, call_item.span.start()); + result += &format!("{}. {}.nr:{}:{}\n", i + 1, path.display(), line, column); + } + + result +} + +fn location(source: &str, span_start: u32) -> (u32, u32) { + let mut line = 1; + let mut column = 0; + + for (i, char) in source.chars().enumerate() { + column += 1; + + if char == '\n' { + line += 1; + column = 0; + } + + if span_start <= i as u32 { + break; + } + } + + (line, column) } diff --git a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 943826201d2..99af0b4eed0 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -109,7 +109,7 @@ impl<'block> BrilligBlock<'block> { self.create_block_label_for_current_function(*else_destination), ); } - TerminatorInstruction::Jmp { destination, arguments, location: _ } => { + TerminatorInstruction::Jmp { destination, arguments, call_stack: _ } => { let target = &dfg[*destination]; for (src, dest) in arguments.iter().zip(target.parameters()) { // Destination variable might have already been created by another block that jumps to this target diff --git a/crates/noirc_evaluator/src/errors.rs b/crates/noirc_evaluator/src/errors.rs index 981f0c9f8a7..847bff7f46c 100644 --- a/crates/noirc_evaluator/src/errors.rs +++ b/crates/noirc_evaluator/src/errors.rs @@ -8,92 +8,105 @@ //! //! An Error of the latter is an error in the implementation of the compiler use acvm::FieldElement; -use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic, Location}; +use iter_extended::vecmap; +use noirc_errors::{CustomDiagnostic as Diagnostic, FileDiagnostic}; use thiserror::Error; +use crate::ssa::ir::dfg::CallStack; + #[derive(Debug, PartialEq, Eq, Clone, Error)] pub enum RuntimeError { // We avoid showing the actual lhs and rhs since most of the time they are just 0 // and 1 respectively. This would confuse users if a constraint such as // assert(foo < bar) fails with "failed constraint: 0 = 1." #[error("Failed constraint")] - FailedConstraint { lhs: FieldElement, rhs: FieldElement, location: Option }, + FailedConstraint { lhs: FieldElement, rhs: FieldElement, call_stack: CallStack }, #[error(transparent)] InternalError(#[from] InternalError), #[error("Index out of bounds, array has size {index:?}, but index was {array_size:?}")] - IndexOutOfBounds { index: usize, array_size: usize, location: Option }, + IndexOutOfBounds { index: usize, array_size: usize, call_stack: CallStack }, #[error("Range constraint of {num_bits} bits is too large for the Field size")] - InvalidRangeConstraint { num_bits: u32, location: Option }, + InvalidRangeConstraint { num_bits: u32, call_stack: CallStack }, #[error("Expected array index to fit into a u64")] - TypeConversion { from: String, into: String, location: Option }, + TypeConversion { from: String, into: String, call_stack: CallStack }, #[error("{name:?} is not initialized")] - UnInitialized { name: String, location: Option }, + UnInitialized { name: String, call_stack: CallStack }, #[error("Integer sized {num_bits:?} is over the max supported size of {max_num_bits:?}")] - UnsupportedIntegerSize { num_bits: u32, max_num_bits: u32, location: Option }, + UnsupportedIntegerSize { num_bits: u32, max_num_bits: u32, call_stack: CallStack }, #[error("Could not determine loop bound at compile-time")] - UnknownLoopBound { location: Option }, + UnknownLoopBound { call_stack: CallStack }, + #[error("Argument is not constant")] + AssertConstantFailed { call_stack: CallStack }, } #[derive(Debug, PartialEq, Eq, Clone, Error)] pub enum InternalError { #[error("ICE: Both expressions should have degree<=1")] - DegreeNotReduced { location: Option }, + DegreeNotReduced { call_stack: CallStack }, #[error("Try to get element from empty array")] - EmptyArray { location: Option }, + EmptyArray { call_stack: CallStack }, #[error("ICE: {message:?}")] - General { message: String, location: Option }, + General { message: String, call_stack: CallStack }, #[error("ICE: {name:?} missing {arg:?} arg")] - MissingArg { name: String, arg: String, location: Option }, + MissingArg { name: String, arg: String, call_stack: CallStack }, #[error("ICE: {name:?} should be a constant")] - NotAConstant { name: String, location: Option }, + NotAConstant { name: String, call_stack: CallStack }, #[error("ICE: Undeclared AcirVar")] - UndeclaredAcirVar { location: Option }, + UndeclaredAcirVar { call_stack: CallStack }, #[error("ICE: Expected {expected:?}, found {found:?}")] - UnExpected { expected: String, found: String, location: Option }, + UnExpected { expected: String, found: String, call_stack: CallStack }, } impl RuntimeError { - fn location(&self) -> Option { + fn call_stack(&self) -> &CallStack { match self { RuntimeError::InternalError( - InternalError::DegreeNotReduced { location } - | InternalError::EmptyArray { location } - | InternalError::General { location, .. } - | InternalError::MissingArg { location, .. } - | InternalError::NotAConstant { location, .. } - | InternalError::UndeclaredAcirVar { location } - | InternalError::UnExpected { location, .. }, + InternalError::DegreeNotReduced { call_stack } + | InternalError::EmptyArray { call_stack } + | InternalError::General { call_stack, .. } + | InternalError::MissingArg { call_stack, .. } + | InternalError::NotAConstant { call_stack, .. } + | InternalError::UndeclaredAcirVar { call_stack } + | InternalError::UnExpected { call_stack, .. }, ) - | RuntimeError::FailedConstraint { location, .. } - | RuntimeError::IndexOutOfBounds { location, .. } - | RuntimeError::InvalidRangeConstraint { location, .. } - | RuntimeError::TypeConversion { location, .. } - | RuntimeError::UnInitialized { location, .. } - | RuntimeError::UnknownLoopBound { location, .. } - | RuntimeError::UnsupportedIntegerSize { location, .. } => *location, + | RuntimeError::FailedConstraint { call_stack, .. } + | RuntimeError::IndexOutOfBounds { call_stack, .. } + | RuntimeError::InvalidRangeConstraint { call_stack, .. } + | RuntimeError::TypeConversion { call_stack, .. } + | RuntimeError::UnInitialized { call_stack, .. } + | RuntimeError::UnknownLoopBound { call_stack } + | RuntimeError::AssertConstantFailed { call_stack } + | RuntimeError::UnsupportedIntegerSize { call_stack, .. } => call_stack, } } } impl From for FileDiagnostic { - fn from(error: RuntimeError) -> Self { - let file_id = error.location().map(|loc| loc.file).unwrap(); - FileDiagnostic { file_id, diagnostic: error.into() } + fn from(error: RuntimeError) -> FileDiagnostic { + let call_stack = vecmap(error.call_stack(), |location| *location); + let diagnostic = error.into_diagnostic(); + let file_id = call_stack.last().map(|location| location.file).unwrap_or_default(); + + diagnostic.in_file(file_id).with_call_stack(call_stack) } } -impl From for Diagnostic { - fn from(error: RuntimeError) -> Diagnostic { - match error { - RuntimeError::InternalError(_) => Diagnostic::simple_error( - "Internal Consistency Evaluators Errors: \n - This is likely a bug. Consider Opening an issue at https://github.com/noir-lang/noir/issues".to_owned(), - "".to_string(), - noirc_errors::Span::new(0..0) - ), +impl RuntimeError { + fn into_diagnostic(self) -> Diagnostic { + match self { + RuntimeError::InternalError(_) => { + Diagnostic::simple_error( + "Internal Consistency Evaluators Errors: \n + This is likely a bug. Consider Opening an issue at https://github.com/noir-lang/noir/issues".to_owned(), + "".to_string(), + noirc_errors::Span::new(0..0) + ) + } _ => { - let span = if let Some(loc) = error.location() { loc.span } else { noirc_errors::Span::new(0..0) }; - Diagnostic::simple_error("".to_owned(), error.to_string(), span) + let message = self.to_string(); + let location = self.call_stack().back().expect("Expected RuntimeError to have a location"); + + Diagnostic::simple_error(message, String::new(), location.span) } } } diff --git a/crates/noirc_evaluator/src/ssa.rs b/crates/noirc_evaluator/src/ssa.rs index 5f29027669a..557df2a727a 100644 --- a/crates/noirc_evaluator/src/ssa.rs +++ b/crates/noirc_evaluator/src/ssa.rs @@ -49,12 +49,21 @@ pub(crate) fn optimize_into_acir( ssa = ssa .inline_functions() .print(print_ssa_passes, "After Inlining:") + // Run mem2reg with the CFG separated into blocks + .mem2reg() + .print(print_ssa_passes, "After Mem2Reg:") + .evaluate_assert_constant()? .unroll_loops()? .print(print_ssa_passes, "After Unrolling:") .simplify_cfg() .print(print_ssa_passes, "After Simplifying:") + // Run mem2reg before flattening to handle any promotion + // of values that can be accessed after loop unrolling + .mem2reg() + .print(print_ssa_passes, "After Mem2Reg:") .flatten_cfg() .print(print_ssa_passes, "After Flattening:") + // Run mem2reg once more with the flattened CFG to catch any remaining loads/stores .mem2reg() .print(print_ssa_passes, "After Mem2Reg:") .fold_constants() @@ -103,6 +112,13 @@ pub fn create_circuit( public_parameters, return_values, }; + + // This converts each im::Vector in the BTreeMap to a Vec + let locations = locations + .into_iter() + .map(|(index, locations)| (index, locations.into_iter().collect())) + .collect(); + let debug_info = DebugInfo::new(locations); Ok((circuit, debug_info, abi)) diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 03dc414f3fb..382d12e7c74 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -2,6 +2,7 @@ use super::generated_acir::GeneratedAcir; use crate::brillig::brillig_gen::brillig_directive; use crate::errors::{InternalError, RuntimeError}; use crate::ssa::acir_gen::{AcirDynamicArray, AcirValue}; +use crate::ssa::ir::dfg::CallStack; use crate::ssa::ir::types::Type as SsaType; use crate::ssa::ir::{instruction::Endian, types::NumericType}; use acvm::acir::circuit::opcodes::{BlockId, MemOp}; @@ -10,6 +11,7 @@ use acvm::acir::{ brillig::Opcode as BrilligOpcode, circuit::brillig::{BrilligInputs, BrilligOutputs}, }; +use acvm::brillig_vm::{brillig::Value, Registers, VMStatus, VM}; use acvm::{ acir::{ circuit::opcodes::FunctionInput, @@ -18,8 +20,8 @@ use acvm::{ }, FieldElement, }; +use acvm::{BlackBoxFunctionSolver, BlackBoxResolutionError}; use iter_extended::{try_vecmap, vecmap}; -use noirc_errors::Location; use std::collections::HashMap; use std::{borrow::Cow, hash::Hash}; @@ -151,12 +153,12 @@ impl AcirContext { self.add_data(var_data) } - pub(crate) fn get_location(&self) -> Option { - self.acir_ir.current_location + pub(crate) fn get_call_stack(&self) -> CallStack { + self.acir_ir.call_stack.clone() } - pub(crate) fn set_location(&mut self, location: Option) { - self.acir_ir.current_location = location; + pub(crate) fn set_call_stack(&mut self, call_stack: CallStack) { + self.acir_ir.call_stack = call_stack; } /// Converts an [`AcirVar`] to a [`Witness`] @@ -169,7 +171,9 @@ impl AcirContext { fn var_to_expression(&self, var: AcirVar) -> Result { let var_data = match self.vars.get(&var) { Some(var_data) => var_data, - None => return Err(InternalError::UndeclaredAcirVar { location: self.get_location() }), + None => { + return Err(InternalError::UndeclaredAcirVar { call_stack: self.get_call_stack() }) + } }; Ok(var_data.to_expression().into_owned()) } @@ -336,7 +340,7 @@ impl AcirContext { Err(RuntimeError::FailedConstraint { lhs: *lhs_const, rhs: *rhs_const, - location: self.get_location(), + call_stack: self.get_call_stack(), }) } } else { @@ -671,7 +675,7 @@ impl AcirContext { return Err(RuntimeError::InternalError(InternalError::MissingArg { name: "pedersen call".to_string(), arg: "domain separator".to_string(), - location: self.get_location(), + call_stack: self.get_call_stack(), })) } }; @@ -681,7 +685,7 @@ impl AcirContext { None => { return Err(RuntimeError::InternalError(InternalError::NotAConstant { name: "domain separator".to_string(), - location: self.get_location(), + call_stack: self.get_call_stack(), })) } }; @@ -747,7 +751,7 @@ impl AcirContext { None => { return Err(RuntimeError::InternalError(InternalError::NotAConstant { name: "radix".to_string(), - location: self.get_location(), + call_stack: self.get_call_stack(), })); } }; @@ -757,7 +761,7 @@ impl AcirContext { None => { return Err(RuntimeError::InternalError(InternalError::NotAConstant { name: "limb_size".to_string(), - location: self.get_location(), + call_stack: self.get_call_stack(), })); } }; @@ -865,6 +869,14 @@ impl AcirContext { } })?; + // Optimistically try executing the brillig now, if we can complete execution they just return the results. + // This is a temporary measure pending SSA optimizations being applied to Brillig which would remove constant-input opcodes (See #2066) + if let Some(brillig_outputs) = self.execute_brillig(code.clone(), &b_inputs, &outputs) { + return Ok(brillig_outputs); + } + + // Otherwise we must generate ACIR for it and execute at runtime. + let mut b_outputs = Vec::new(); let outputs_var = vecmap(outputs, |output| match output { AcirType::NumericType(_) => { @@ -951,6 +963,66 @@ impl AcirContext { (AcirValue::Array(array_values), witnesses) } + fn execute_brillig( + &mut self, + code: Vec, + inputs: &[BrilligInputs], + outputs_types: &[AcirType], + ) -> Option> { + let (registers, memory) = execute_brillig(code, inputs)?; + + let outputs_var = vecmap(outputs_types.iter().enumerate(), |(index, output)| { + let register_value = registers.get(index.into()); + match output { + AcirType::NumericType(_) => { + let var = self.add_data(AcirVarData::Const(register_value.to_field())); + AcirValue::Var(var, output.clone()) + } + AcirType::Array(element_types, size) => { + let mem_ptr = register_value.to_usize(); + self.brillig_constant_array_output( + element_types, + *size, + &mut memory.iter().skip(mem_ptr), + ) + } + } + }); + + Some(outputs_var) + } + + /// Recursively create [`AcirValue`]s for returned arrays. This is necessary because a brillig returned array can have nested arrays as elements. + fn brillig_constant_array_output<'a>( + &mut self, + element_types: &[AcirType], + size: usize, + memory_iter: &mut impl Iterator, + ) -> AcirValue { + let mut array_values = im::Vector::new(); + for _ in 0..size { + for element_type in element_types { + match element_type { + AcirType::Array(nested_element_types, nested_size) => { + let nested_acir_value = self.brillig_constant_array_output( + nested_element_types, + *nested_size, + memory_iter, + ); + array_values.push_back(nested_acir_value); + } + AcirType::NumericType(_) => { + let memory_value = + memory_iter.next().expect("ICE: Unexpected end of memory"); + let var = self.add_data(AcirVarData::Const(memory_value.to_field())); + array_values.push_back(AcirValue::Var(var, element_type.clone())); + } + } + } + } + AcirValue::Array(array_values) + } + /// Generate output variables that are constrained to be the sorted inputs /// The outputs are the sorted inputs iff /// outputs are sorted and @@ -1112,3 +1184,92 @@ impl AcirVarData { /// A Reference to an `AcirVarData` #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub(crate) struct AcirVar(usize); + +/// Attempts to execute the provided [`Brillig`][`acvm::acir::brillig`] bytecode +/// +/// Returns the finished state of the Brillig VM if execution can complete. +/// +/// Returns `None` if complete execution of the Brillig bytecode is not possible. +fn execute_brillig( + code: Vec, + inputs: &[BrilligInputs], +) -> Option<(Registers, Vec)> { + struct NullBbSolver; + + impl BlackBoxFunctionSolver for NullBbSolver { + fn schnorr_verify( + &self, + _public_key_x: &FieldElement, + _public_key_y: &FieldElement, + _signature: &[u8], + _message: &[u8], + ) -> Result { + Err(BlackBoxResolutionError::Unsupported(BlackBoxFunc::SchnorrVerify)) + } + fn pedersen( + &self, + _inputs: &[FieldElement], + _domain_separator: u32, + ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { + Err(BlackBoxResolutionError::Unsupported(BlackBoxFunc::Pedersen)) + } + fn fixed_base_scalar_mul( + &self, + _input: &FieldElement, + ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { + Err(BlackBoxResolutionError::Unsupported(BlackBoxFunc::FixedBaseScalarMul)) + } + } + + // Set input values + let mut input_register_values: Vec = Vec::with_capacity(inputs.len()); + let mut input_memory: Vec = Vec::new(); + // Each input represents a constant or array of constants. + // Iterate over each input and push it into registers and/or memory. + for input in inputs { + match input { + BrilligInputs::Single(expr) => { + input_register_values.push(expr.to_const()?.into()); + } + BrilligInputs::Array(expr_arr) => { + // Attempt to fetch all array input values + let memory_pointer = input_memory.len(); + for expr in expr_arr.iter() { + input_memory.push(expr.to_const()?.into()); + } + + // Push value of the array pointer as a register + input_register_values.push(Value::from(memory_pointer)); + } + } + } + + // Instantiate a Brillig VM given the solved input registers and memory, along with the Brillig bytecode. + let input_registers = Registers::load(input_register_values); + let mut vm = VM::new(input_registers, input_memory, code, Vec::new(), &NullBbSolver); + + // Run the Brillig VM on these inputs, bytecode, etc! + let vm_status = vm.process_opcodes(); + + // Check the status of the Brillig VM. + // It may be finished, in-progress, failed, or may be waiting for results of a foreign call. + // If it's finished then we can omit the opcode and just write in the return values. + match vm_status { + VMStatus::Finished => Some((vm.get_registers().clone(), vm.get_memory().clone())), + VMStatus::InProgress => unreachable!("Brillig VM has not completed execution"), + VMStatus::Failure { .. } => { + // TODO: Return an error stating that the brillig function failed. + None + } + VMStatus::ForeignCallWait { .. } => { + // If execution can't complete then keep the opcode + + // TODO: We could bake in all the execution up to this point by replacing the inputs + // such that they initialize the registers/memory to the current values and then discard + // any opcodes prior to the one which performed this foreign call. + // + // Seems overkill for now however. + None + } + } +} diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index b425eab42d3..8fc46db2b1a 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -1,10 +1,11 @@ //! `GeneratedAcir` is constructed as part of the `acir_gen` pass to accumulate all of the ACIR //! program as it is being converted from SSA form. -use std::collections::HashMap; +use std::collections::BTreeMap; use crate::{ brillig::brillig_gen::brillig_directive, errors::{InternalError, RuntimeError}, + ssa::ir::dfg::CallStack, }; use acvm::acir::{ @@ -22,7 +23,6 @@ use acvm::{ FieldElement, }; use iter_extended::vecmap; -use noirc_errors::Location; use num_bigint::BigUint; #[derive(Debug, Default)] @@ -45,12 +45,12 @@ pub(crate) struct GeneratedAcir { /// All witness indices which are inputs to the main function pub(crate) input_witnesses: Vec, - /// Correspondance between an opcode index (in opcodes) and the source code location which generated it - pub(crate) locations: HashMap, + /// Correspondance between an opcode index (in opcodes) and the source code call stack which generated it + pub(crate) locations: BTreeMap, /// Source code location of the current instruction being processed /// None if we do not know the location - pub(crate) current_location: Option, + pub(crate) call_stack: CallStack, } impl GeneratedAcir { @@ -62,8 +62,8 @@ impl GeneratedAcir { /// Adds a new opcode into ACIR. fn push_opcode(&mut self, opcode: AcirOpcode) { self.opcodes.push(opcode); - if let Some(location) = self.current_location { - self.locations.insert(self.opcodes.len() - 1, location); + if !self.call_stack.is_empty() { + self.locations.insert(self.opcodes.len() - 1, self.call_stack.clone()); } } @@ -195,7 +195,7 @@ impl GeneratedAcir { return Err(InternalError::MissingArg { name: "".to_string(), arg: "message_size".to_string(), - location: self.current_location, + call_stack: self.call_stack.clone(), }); } }; @@ -706,7 +706,7 @@ impl GeneratedAcir { if num_bits >= FieldElement::max_num_bits() { return Err(RuntimeError::InvalidRangeConstraint { num_bits: FieldElement::max_num_bits(), - location: self.current_location, + call_stack: self.call_stack.clone(), }); }; diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/sort.rs b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/sort.rs index 42a6a5f1a4a..52640d32337 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/sort.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/sort.rs @@ -50,7 +50,9 @@ impl GeneratedAcir { if n % 2 == 1 { in_sub2.push(match in_expr.last() { Some(in_expr) => in_expr.clone(), - None => return Err(InternalError::EmptyArray { location: self.current_location }), + None => { + return Err(InternalError::EmptyArray { call_stack: self.call_stack.clone() }) + } }); } let mut out_expr = Vec::new(); @@ -70,12 +72,14 @@ impl GeneratedAcir { if n % 2 == 0 { out_expr.push(match b1.last() { Some(b1) => b1.clone(), - None => return Err(InternalError::EmptyArray { location: self.current_location }), + None => { + return Err(InternalError::EmptyArray { call_stack: self.call_stack.clone() }) + } }); } out_expr.push(match b2.last() { Some(b2) => b2.clone(), - None => return Err(InternalError::EmptyArray { location: self.current_location }), + None => return Err(InternalError::EmptyArray { call_stack: self.call_stack.clone() }), }); conf.extend(w1); conf.extend(w2); diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs b/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs index 2802993549f..1586d933132 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -6,6 +6,7 @@ use std::fmt::Debug; use std::ops::RangeInclusive; use self::acir_ir::acir_variable::{AcirContext, AcirType, AcirVar}; +use super::ir::dfg::CallStack; use super::{ ir::{ dfg::DataFlowGraph, @@ -93,7 +94,7 @@ impl AcirValue { AcirValue::Var(var, _) => Ok(var), AcirValue::DynamicArray(_) | AcirValue::Array(_) => Err(InternalError::General { message: "Called AcirValue::into_var on an array".to_string(), - location: None, + call_stack: CallStack::new(), }), } } @@ -320,7 +321,7 @@ impl Context { last_array_uses: &HashMap, ) -> Result<(), RuntimeError> { let instruction = &dfg[instruction_id]; - self.acir_context.set_location(dfg.get_location(&instruction_id)); + self.acir_context.set_call_stack(dfg.get_call_stack(instruction_id)); match instruction { Instruction::Binary(binary) => { let result_acir_var = self.convert_ssa_binary(binary, dfg)?; @@ -426,7 +427,7 @@ impl Context { unreachable!("Expected all load instructions to be removed before acir_gen") } } - self.acir_context.set_location(None); + self.acir_context.set_call_stack(CallStack::new()); Ok(()) } @@ -449,7 +450,7 @@ impl Context { None => { return Err(InternalError::General { message: format!("Cannot find linked fn {unresolved_fn_label}"), - location: None, + call_stack: CallStack::new(), }) } }; @@ -478,7 +479,7 @@ impl Context { return Err(RuntimeError::InternalError(InternalError::UnExpected { expected: "an array value".to_string(), found: format!("{acir_var:?}"), - location: self.acir_context.get_location(), + call_stack: self.acir_context.get_call_stack(), })) } AcirValue::Array(array) => { @@ -487,11 +488,11 @@ impl Context { let index = match index_const.try_to_u64() { Some(index_const) => index_const as usize, None => { - let location = self.acir_context.get_location(); + let call_stack = self.acir_context.get_call_stack(); return Err(RuntimeError::TypeConversion { from: "array index".to_string(), into: "u64".to_string(), - location, + call_stack, }); } }; @@ -499,11 +500,11 @@ impl Context { // Ignore the error if side effects are disabled. if self.acir_context.is_constant_one(&self.current_side_effects_enabled_var) { - let location = self.acir_context.get_location(); + let call_stack = self.acir_context.get_call_stack(); return Err(RuntimeError::IndexOutOfBounds { index, array_size, - location, + call_stack, }); } let result_type = @@ -558,7 +559,7 @@ impl Context { _ => { return Err(RuntimeError::UnInitialized { name: "array".to_string(), - location: self.acir_context.get_location(), + call_stack: self.acir_context.get_call_stack(), }) } } @@ -620,7 +621,7 @@ impl Context { _ => { return Err(InternalError::General { message: format!("Array {array} should be initialized"), - location: self.acir_context.get_location(), + call_stack: self.acir_context.get_call_stack(), }) } } @@ -770,12 +771,12 @@ impl Context { AcirValue::Array(array) => Err(InternalError::UnExpected { expected: "a numeric value".to_string(), found: format!("{array:?}"), - location: self.acir_context.get_location(), + call_stack: self.acir_context.get_call_stack(), }), AcirValue::DynamicArray(_) => Err(InternalError::UnExpected { expected: "a numeric value".to_string(), found: "an array".to_string(), - location: self.acir_context.get_location(), + call_stack: self.acir_context.get_call_stack(), }), } } @@ -801,7 +802,7 @@ impl Context { return Err(RuntimeError::UnsupportedIntegerSize { num_bits: *bit_size, max_num_bits: max_integer_bit_size, - location: self.acir_context.get_location(), + call_stack: self.acir_context.get_call_stack(), }); } } diff --git a/crates/noirc_evaluator/src/ssa/ir/basic_block.rs b/crates/noirc_evaluator/src/ssa/ir/basic_block.rs index 4886e3f5da8..998591f7210 100644 --- a/crates/noirc_evaluator/src/ssa/ir/basic_block.rs +++ b/crates/noirc_evaluator/src/ssa/ir/basic_block.rs @@ -73,6 +73,11 @@ impl BasicBlock { &mut self.instructions } + /// Take the instructions in this block, replacing it with an empty Vec + pub(crate) fn take_instructions(&mut self) -> Vec { + std::mem::take(&mut self.instructions) + } + /// Sets the terminator instruction of this block. /// /// A properly-constructed block will always terminate with a TerminatorInstruction - diff --git a/crates/noirc_evaluator/src/ssa/ir/cfg.rs b/crates/noirc_evaluator/src/ssa/ir/cfg.rs index 8faa0fa8565..eccb9ce587c 100644 --- a/crates/noirc_evaluator/src/ssa/ir/cfg.rs +++ b/crates/noirc_evaluator/src/ssa/ir/cfg.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeSet, HashMap}; use super::{ basic_block::{BasicBlock, BasicBlockId}, @@ -10,13 +10,14 @@ use super::{ struct CfgNode { /// Set of blocks that containing jumps that target this block. /// The predecessor set has no meaningful order. - pub(crate) predecessors: HashSet, + pub(crate) predecessors: BTreeSet, /// Set of blocks that are the targets of jumps in this block. /// The successors set has no meaningful order. - pub(crate) successors: HashSet, + pub(crate) successors: BTreeSet, } +#[derive(Clone)] /// The Control Flow Graph maintains a mapping of blocks to their predecessors /// and successors where predecessors are basic blocks and successors are /// basic blocks. @@ -31,7 +32,7 @@ impl ControlFlowGraph { // therefore we must ensure that a node exists for the entry block, regardless of whether // it later comes to describe any edges after calling compute. let entry_block = func.entry_block(); - let empty_node = CfgNode { predecessors: HashSet::new(), successors: HashSet::new() }; + let empty_node = CfgNode { predecessors: BTreeSet::new(), successors: BTreeSet::new() }; let data = HashMap::from([(entry_block, empty_node)]); let mut cfg = ControlFlowGraph { data }; @@ -220,7 +221,7 @@ mod tests { func.dfg[block2_id].set_terminator(TerminatorInstruction::Jmp { destination: ret_block_id, arguments: vec![], - location: None, + call_stack: im::Vector::new(), }); func.dfg[block0_id].set_terminator(TerminatorInstruction::JmpIf { condition: cond, diff --git a/crates/noirc_evaluator/src/ssa/ir/dfg.rs b/crates/noirc_evaluator/src/ssa/ir/dfg.rs index 4a935e9e7b8..7dcf652c6e6 100644 --- a/crates/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/crates/noirc_evaluator/src/ssa/ir/dfg.rs @@ -73,11 +73,19 @@ pub(crate) struct DataFlowGraph { /// Source location of each instruction for debugging and issuing errors. /// + /// The `CallStack` here corresponds to the entire callstack of locations. Initially this + /// only contains the actual location of the instruction. During inlining, a new location + /// will be pushed to each instruction for the location of the function call of the function + /// the instruction was originally located in. Once inlining is complete, the locations Vec + /// here should contain the entire callstack for each instruction. + /// /// Instructions inserted by internal SSA passes that don't correspond to user code /// may not have a corresponding location. - locations: HashMap, + locations: HashMap, } +pub(crate) type CallStack = im::Vector; + impl DataFlowGraph { /// Creates a new basic block with no parameters. /// After being created, the block is unreachable in the current function @@ -149,7 +157,7 @@ impl DataFlowGraph { instruction: Instruction, block: BasicBlockId, ctrl_typevars: Option>, - location: Option, + call_stack: CallStack, ) -> InsertInstructionResult { use InsertInstructionResult::*; match instruction.simplify(self, block) { @@ -162,9 +170,7 @@ impl DataFlowGraph { let instruction = result.instruction().unwrap_or(instruction); let id = self.make_instruction(instruction, ctrl_typevars); self.blocks[block].insert_instruction(id); - if let Some(location) = location { - self.locations.insert(id, location); - } + self.locations.insert(id, call_stack); InsertInstructionResult::Results(self.instruction_results(id)) } } @@ -399,7 +405,7 @@ impl DataFlowGraph { /// source block. pub(crate) fn inline_block(&mut self, source: BasicBlockId, destination: BasicBlockId) { let source = &mut self.blocks[source]; - let mut instructions = std::mem::take(source.instructions_mut()); + let mut instructions = source.take_instructions(); let terminator = source.take_terminator(); let destination = &mut self.blocks[destination]; @@ -407,16 +413,25 @@ impl DataFlowGraph { destination.set_terminator(terminator); } - pub(crate) fn get_location(&self, id: &InstructionId) -> Option { - self.locations.get(id).copied() + pub(crate) fn get_call_stack(&self, instruction: InstructionId) -> CallStack { + self.locations.get(&instruction).cloned().unwrap_or_default() } - pub(crate) fn get_value_location(&self, id: &ValueId) -> Option { - match &self.values[self.resolve(*id)] { - Value::Instruction { instruction, .. } => self.get_location(instruction), - _ => None, + pub(crate) fn add_location(&mut self, instruction: InstructionId, location: Location) { + self.locations.entry(instruction).or_default().push_back(location); + } + + pub(crate) fn get_value_call_stack(&self, value: ValueId) -> CallStack { + match &self.values[self.resolve(value)] { + Value::Instruction { instruction, .. } => self.get_call_stack(*instruction), + _ => im::Vector::new(), } } + + /// True if the given ValueId refers to a constant value + pub(crate) fn is_constant(&self, argument: ValueId) -> bool { + !matches!(&self[self.resolve(argument)], Value::Instruction { .. } | Value::Param { .. }) + } } impl std::ops::Index for DataFlowGraph { diff --git a/crates/noirc_evaluator/src/ssa/ir/function.rs b/crates/noirc_evaluator/src/ssa/ir/function.rs index ab04c1497bd..ab97d418783 100644 --- a/crates/noirc_evaluator/src/ssa/ir/function.rs +++ b/crates/noirc_evaluator/src/ssa/ir/function.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::collections::BTreeSet; use iter_extended::vecmap; @@ -107,8 +107,8 @@ impl Function { /// Note that self.dfg.basic_blocks_iter() iterates over all blocks, /// whether reachable or not. This function should be used if you /// want to iterate only reachable blocks. - pub(crate) fn reachable_blocks(&self) -> HashSet { - let mut blocks = HashSet::new(); + pub(crate) fn reachable_blocks(&self) -> BTreeSet { + let mut blocks = BTreeSet::new(); let mut stack = vec![self.entry_block]; while let Some(block) = stack.pop() { diff --git a/crates/noirc_evaluator/src/ssa/ir/function_inserter.rs b/crates/noirc_evaluator/src/ssa/ir/function_inserter.rs index 15c755f40c2..bc0084b6d4e 100644 --- a/crates/noirc_evaluator/src/ssa/ir/function_inserter.rs +++ b/crates/noirc_evaluator/src/ssa/ir/function_inserter.rs @@ -1,11 +1,10 @@ use std::collections::HashMap; use iter_extended::vecmap; -use noirc_errors::Location; use super::{ basic_block::BasicBlockId, - dfg::InsertInstructionResult, + dfg::{CallStack, InsertInstructionResult}, function::Function, instruction::{Instruction, InstructionId}, value::ValueId, @@ -56,10 +55,10 @@ impl<'f> FunctionInserter<'f> { self.values.insert(key, value); } - pub(crate) fn map_instruction(&mut self, id: InstructionId) -> (Instruction, Option) { + pub(crate) fn map_instruction(&mut self, id: InstructionId) -> (Instruction, CallStack) { ( self.function.dfg[id].clone().map_values(|id| self.resolve(id)), - self.function.dfg.get_location(&id), + self.function.dfg.get_call_stack(id), ) } @@ -73,7 +72,7 @@ impl<'f> FunctionInserter<'f> { instruction: Instruction, id: InstructionId, block: BasicBlockId, - location: Option, + call_stack: CallStack, ) -> InsertInstructionResult { let results = self.function.dfg.instruction_results(id); let results = vecmap(results, |id| self.function.dfg.resolve(*id)); @@ -86,7 +85,7 @@ impl<'f> FunctionInserter<'f> { instruction, block, ctrl_typevars, - location, + call_stack, ); Self::insert_new_instruction_results(&mut self.values, &results, &new_results); diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction.rs b/crates/noirc_evaluator/src/ssa/ir/instruction.rs index cb6bb83ab61..bc6c1d27e4b 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction.rs @@ -1,11 +1,10 @@ use acvm::{acir::BlackBoxFunc, FieldElement}; use iter_extended::vecmap; -use noirc_errors::Location; use num_bigint::BigUint; use super::{ basic_block::BasicBlockId, - dfg::DataFlowGraph, + dfg::{CallStack, DataFlowGraph}, map::Id, types::{NumericType, Type}, value::{Value, ValueId}, @@ -34,12 +33,14 @@ pub(crate) type InstructionId = Id; pub(crate) enum Intrinsic { Sort, ArrayLen, + AssertConstant, SlicePushBack, SlicePushFront, SlicePopBack, SlicePopFront, SliceInsert, SliceRemove, + StrAsBytes, Println, AssertEq, ToBits(Endian), @@ -50,16 +51,18 @@ pub(crate) enum Intrinsic { impl std::fmt::Display for Intrinsic { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Intrinsic::AssertEq => write!(f, "assert_eq"), Intrinsic::Println => write!(f, "println"), Intrinsic::Sort => write!(f, "arraysort"), Intrinsic::ArrayLen => write!(f, "array_len"), + Intrinsic::AssertConstant => write!(f, "assert_constant"), + Intrinsic::AssertEq => write!(f, "assert_eq"), Intrinsic::SlicePushBack => write!(f, "slice_push_back"), Intrinsic::SlicePushFront => write!(f, "slice_push_front"), Intrinsic::SlicePopBack => write!(f, "slice_pop_back"), Intrinsic::SlicePopFront => write!(f, "slice_pop_front"), Intrinsic::SliceInsert => write!(f, "slice_insert"), Intrinsic::SliceRemove => write!(f, "slice_remove"), + Intrinsic::StrAsBytes => write!(f, "str_as_bytes"), Intrinsic::ToBits(Endian::Big) => write!(f, "to_be_bits"), Intrinsic::ToBits(Endian::Little) => write!(f, "to_le_bits"), Intrinsic::ToRadix(Endian::Big) => write!(f, "to_be_radix"), @@ -70,20 +73,46 @@ impl std::fmt::Display for Intrinsic { } impl Intrinsic { + /// Returns whether the `Intrinsic` has side effects. + /// + /// If there are no side effects then the `Intrinsic` can be removed if the result is unused. + pub(crate) fn has_side_effects(&self) -> bool { + match self { + Intrinsic::Println | Intrinsic::AssertConstant | Intrinsic::AssertEq => true, + + Intrinsic::Sort + | Intrinsic::ArrayLen + | Intrinsic::SlicePushBack + | Intrinsic::SlicePushFront + | Intrinsic::SlicePopBack + | Intrinsic::SlicePopFront + | Intrinsic::SliceInsert + | Intrinsic::SliceRemove + | Intrinsic::StrAsBytes + | Intrinsic::ToBits(_) + | Intrinsic::ToRadix(_) => false, + + // Some black box functions have side-effects + Intrinsic::BlackBox(func) => matches!(func, BlackBoxFunc::RecursiveAggregation), + } + } + /// Lookup an Intrinsic by name and return it if found. /// If there is no such intrinsic by that name, None is returned. pub(crate) fn lookup(name: &str) -> Option { match name { - "assert_eq" => Some(Intrinsic::AssertEq), "println" => Some(Intrinsic::Println), "arraysort" => Some(Intrinsic::Sort), "array_len" => Some(Intrinsic::ArrayLen), + "assert_constant" => Some(Intrinsic::AssertConstant), + "assert_eq" => Some(Intrinsic::AssertEq), "slice_push_back" => Some(Intrinsic::SlicePushBack), "slice_push_front" => Some(Intrinsic::SlicePushFront), "slice_pop_back" => Some(Intrinsic::SlicePopBack), "slice_pop_front" => Some(Intrinsic::SlicePopFront), "slice_insert" => Some(Intrinsic::SliceInsert), "slice_remove" => Some(Intrinsic::SliceRemove), + "str_as_bytes" => Some(Intrinsic::StrAsBytes), "to_le_radix" => Some(Intrinsic::ToRadix(Endian::Little)), "to_be_radix" => Some(Intrinsic::ToRadix(Endian::Big)), "to_le_bits" => Some(Intrinsic::ToBits(Endian::Little)), @@ -181,6 +210,29 @@ impl Instruction { matches!(self.result_type(), InstructionResultType::Unknown) } + pub(crate) fn has_side_effects(&self, dfg: &DataFlowGraph) -> bool { + use Instruction::*; + + match self { + Binary(_) + | Cast(_, _) + | Not(_) + | Truncate { .. } + | Allocate + | Load { .. } + | ArrayGet { .. } + | ArraySet { .. } => false, + + Constrain(_) | Store { .. } | EnableSideEffects { .. } => true, + + // Some `Intrinsic`s have side effects so we must check what kind of `Call` this is. + Call { func, .. } => match dfg[*func] { + Value::Intrinsic(intrinsic) => intrinsic.has_side_effects(), + _ => false, + }, + } + } + /// Maps each ValueId inside this instruction to a new ValueId, returning the new instruction. /// Note that the returned instruction is fresh and will not have an assigned InstructionId /// until it is manually inserted in a DataFlowGraph later. @@ -424,9 +476,9 @@ pub(crate) enum TerminatorInstruction { /// Unconditional Jump /// /// Jumps to specified `destination` with `arguments`. - /// The optional Location here is expected to be used to issue an error when the start range of + /// The CallStack here is expected to be used to issue an error when the start range of /// a for loop cannot be deduced at compile-time. - Jmp { destination: BasicBlockId, arguments: Vec, location: Option }, + Jmp { destination: BasicBlockId, arguments: Vec, call_stack: CallStack }, /// Return from the current function with the given return values. /// @@ -451,10 +503,10 @@ impl TerminatorInstruction { then_destination: *then_destination, else_destination: *else_destination, }, - Jmp { destination, arguments, location } => Jmp { + Jmp { destination, arguments, call_stack } => Jmp { destination: *destination, arguments: vecmap(arguments, |value| f(*value)), - location: *location, + call_stack: call_stack.clone(), }, Return { return_values } => { Return { return_values: vecmap(return_values, |value| f(*value)) } diff --git a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs index 054be262969..3f19ab17f33 100644 --- a/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/crates/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -176,6 +176,17 @@ pub(super) fn simplify_call( SimplifyResult::None } } + Intrinsic::StrAsBytes => { + // Strings are already represented as bytes internally + SimplifyResult::SimplifiedTo(arguments[0]) + } + Intrinsic::AssertConstant => { + if arguments.iter().all(|argument| dfg.is_constant(*argument)) { + SimplifyResult::Remove + } else { + SimplifyResult::None + } + } Intrinsic::BlackBox(bb_func) => simplify_black_box_func(bb_func, arguments, dfg), Intrinsic::Sort => simplify_sort(dfg, arguments), Intrinsic::Println => SimplifyResult::None, diff --git a/crates/noirc_evaluator/src/ssa/ir/printer.rs b/crates/noirc_evaluator/src/ssa/ir/printer.rs index e6faf4570c5..e8cea151ad1 100644 --- a/crates/noirc_evaluator/src/ssa/ir/printer.rs +++ b/crates/noirc_evaluator/src/ssa/ir/printer.rs @@ -100,7 +100,7 @@ pub(crate) fn display_terminator( f: &mut Formatter, ) -> Result { match terminator { - Some(TerminatorInstruction::Jmp { destination, arguments, location: _ }) => { + Some(TerminatorInstruction::Jmp { destination, arguments, call_stack: _ }) => { writeln!(f, " jmp {}({})", destination, value_list(function, arguments)) } Some(TerminatorInstruction::JmpIf { condition, then_destination, else_destination }) => { diff --git a/crates/noirc_evaluator/src/ssa/opt/assert_constant.rs b/crates/noirc_evaluator/src/ssa/opt/assert_constant.rs new file mode 100644 index 00000000000..cd3a509a62e --- /dev/null +++ b/crates/noirc_evaluator/src/ssa/opt/assert_constant.rs @@ -0,0 +1,83 @@ +use crate::{ + errors::RuntimeError, + ssa::{ + ir::{ + function::Function, + instruction::{Instruction, InstructionId, Intrinsic}, + value::ValueId, + }, + ssa_gen::Ssa, + }, +}; + +impl Ssa { + /// A simple SSA pass to go through each instruction and evaluate each call + /// to `assert_constant`, issuing an error if any arguments to the function are + /// not constants. + /// + /// Note that this pass must be placed directly before loop unrolling to be + /// useful. Any optimization passes between this and loop unrolling will cause + /// the constants that this pass sees to be potentially different than the constants + /// seen by loop unrolling. Furthermore, this pass cannot be a part of loop unrolling + /// since we must go through every instruction to find all references to `assert_constant` + /// while loop unrolling only touches blocks with loops in them. + pub(crate) fn evaluate_assert_constant(mut self) -> Result { + for function in self.functions.values_mut() { + for block in function.reachable_blocks() { + // Unfortunately we can't just use instructions.retain(...) here since + // check_instruction can also return an error + let instructions = function.dfg[block].take_instructions(); + let mut filtered_instructions = Vec::with_capacity(instructions.len()); + + for instruction in instructions { + if check_instruction(function, instruction)? { + filtered_instructions.push(instruction); + } + } + + *function.dfg[block].instructions_mut() = filtered_instructions; + } + } + Ok(self) + } +} + +/// During the loop unrolling pass we also evaluate calls to `assert_constant`. +/// This is done in this pass because loop unrolling is the only pass that will error +/// if a value (the loop bounds) are not known constants. +/// +/// This returns Ok(true) if the given instruction should be kept in the block and +/// Ok(false) if it should be removed. +fn check_instruction( + function: &mut Function, + instruction: InstructionId, +) -> Result { + let assert_constant_id = function.dfg.import_intrinsic(Intrinsic::AssertConstant); + match &function.dfg[instruction] { + Instruction::Call { func, arguments } => { + if *func == assert_constant_id { + evaluate_assert_constant(function, instruction, arguments) + } else { + Ok(true) + } + } + _ => Ok(true), + } +} + +/// Evaluate a call to `assert_constant`, returning an error if any of the elements are not +/// constants. If all of the elements are constants, Ok(false) is returned. This signifies a +/// success but also that the instruction need not be reinserted into the block being unrolled +/// since it has already been evaluated. +fn evaluate_assert_constant( + function: &Function, + instruction: InstructionId, + arguments: &[ValueId], +) -> Result { + if arguments.iter().all(|arg| function.dfg.is_constant(*arg)) { + Ok(false) + } else { + let call_stack = function.dfg.get_call_stack(instruction); + Err(RuntimeError::AssertConstantFailed { call_stack }) + } +} diff --git a/crates/noirc_evaluator/src/ssa/opt/constant_folding.rs b/crates/noirc_evaluator/src/ssa/opt/constant_folding.rs index ea46ddf1d4f..75ff92ebbf1 100644 --- a/crates/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/crates/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -50,7 +50,7 @@ struct Context { impl Context { fn fold_constants_in_block(&mut self, function: &mut Function, block: BasicBlockId) { - let instructions = std::mem::take(function.dfg[block].instructions_mut()); + let instructions = function.dfg[block].take_instructions(); for instruction in instructions { self.push_instruction(function, block, instruction); @@ -71,12 +71,12 @@ impl Context { .requires_ctrl_typevars() .then(|| vecmap(&old_results, |result| function.dfg.type_of_value(*result))); - let location = function.dfg.get_location(&id); + let call_stack = function.dfg.get_call_stack(id); let new_results = match function.dfg.insert_instruction_and_results( instruction, block, ctrl_typevars, - location, + call_stack, ) { InsertInstructionResult::SimplifiedTo(new_result) => vec![new_result], InsertInstructionResult::SimplifiedToMultiple(new_results) => new_results, diff --git a/crates/noirc_evaluator/src/ssa/opt/die.rs b/crates/noirc_evaluator/src/ssa/opt/die.rs index 935568af2db..ee78fa82256 100644 --- a/crates/noirc_evaluator/src/ssa/opt/die.rs +++ b/crates/noirc_evaluator/src/ssa/opt/die.rs @@ -7,7 +7,7 @@ use crate::ssa::{ basic_block::{BasicBlock, BasicBlockId}, dfg::DataFlowGraph, function::Function, - instruction::{Instruction, InstructionId}, + instruction::InstructionId, post_order::PostOrder, value::{Value, ValueId}, }, @@ -88,20 +88,15 @@ impl Context { /// An instruction can be removed as long as it has no side-effects, and none of its result /// values have been referenced. fn is_unused(&self, instruction_id: InstructionId, function: &Function) -> bool { - use Instruction::*; - let instruction = &function.dfg[instruction_id]; - // These instruction types cannot be removed - if matches!( - instruction, - Constrain(_) | Call { .. } | Store { .. } | EnableSideEffects { .. } - ) { - return false; + if instruction.has_side_effects(&function.dfg) { + // If the instruction has side effects we should never remove it. + false + } else { + let results = function.dfg.instruction_results(instruction_id); + results.iter().all(|result| !self.used_values.contains(result)) } - - let results = function.dfg.instruction_results(instruction_id); - results.iter().all(|result| !self.used_values.contains(result)) } /// Adds values referenced by the terminator to the set of used values. @@ -134,7 +129,12 @@ impl Context { #[cfg(test)] mod test { use crate::ssa::{ - ir::{function::RuntimeType, instruction::BinaryOp, map::Id, types::Type}, + ir::{ + function::RuntimeType, + instruction::{BinaryOp, Intrinsic}, + map::Id, + types::Type, + }, ssa_builder::FunctionBuilder, }; @@ -159,7 +159,6 @@ mod test { // return v9 // } let main_id = Id::test_new(0); - let println_id = Id::test_new(1); // Compiling main let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); @@ -187,6 +186,8 @@ mod test { let v9 = builder.insert_binary(v7, BinaryOp::Add, two); let v10 = builder.insert_binary(v7, BinaryOp::Add, three); let _v11 = builder.insert_binary(v10, BinaryOp::Add, v10); + + let println_id = builder.import_intrinsic_id(Intrinsic::Println); builder.insert_call(println_id, vec![v8], vec![]); builder.terminate_with_return(vec![v9]); diff --git a/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 8481f5610bb..3df27868737 100644 --- a/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -135,13 +135,12 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use acvm::FieldElement; use iter_extended::vecmap; -use noirc_errors::Location; use crate::ssa::{ ir::{ basic_block::BasicBlockId, cfg::ControlFlowGraph, - dfg::InsertInstructionResult, + dfg::{CallStack, InsertInstructionResult}, function::Function, function_inserter::FunctionInserter, instruction::{BinaryOp, Instruction, InstructionId, TerminatorInstruction}, @@ -260,7 +259,7 @@ impl<'f> Context<'f> { self.inline_branch(block, then_block, old_condition, then_condition, one); let else_condition = - self.insert_instruction(Instruction::Not(then_condition), None); + self.insert_instruction(Instruction::Not(then_condition), CallStack::new()); let zero = FieldElement::zero(); // Make sure the else branch sees the previous values of each store @@ -285,7 +284,7 @@ impl<'f> Context<'f> { let end = self.branch_ends[&block]; self.inline_branch_end(end, then_branch, else_branch) } - TerminatorInstruction::Jmp { destination, arguments, location: _ } => { + TerminatorInstruction::Jmp { destination, arguments, call_stack: _ } => { if let Some((end_block, _)) = self.conditions.last() { if destination == end_block { return block; @@ -317,7 +316,7 @@ impl<'f> Context<'f> { if let Some((_, previous_condition)) = self.conditions.last() { let and = Instruction::binary(BinaryOp::And, *previous_condition, condition); - let new_condition = self.insert_instruction(and, None); + let new_condition = self.insert_instruction(and, CallStack::new()); self.conditions.push((end_block, new_condition)); } else { self.conditions.push((end_block, condition)); @@ -327,16 +326,12 @@ impl<'f> Context<'f> { /// Insert a new instruction into the function's entry block. /// Unlike push_instruction, this function will not map any ValueIds. /// within the given instruction, nor will it modify self.values in any way. - fn insert_instruction( - &mut self, - instruction: Instruction, - location: Option, - ) -> ValueId { + fn insert_instruction(&mut self, instruction: Instruction, call_stack: CallStack) -> ValueId { let block = self.inserter.function.entry_block(); self.inserter .function .dfg - .insert_instruction_and_results(instruction, block, None, location) + .insert_instruction_and_results(instruction, block, None, call_stack) .first() } @@ -354,7 +349,7 @@ impl<'f> Context<'f> { instruction, block, ctrl_typevars, - None, + CallStack::new(), ) } @@ -465,22 +460,27 @@ impl<'f> Context<'f> { "Expected values merged to be of the same type but found {then_type} and {else_type}" ); - let then_location = self.inserter.function.dfg.get_value_location(&then_value); - let else_location = self.inserter.function.dfg.get_value_location(&else_value); - let merge_location = then_location.or(else_location); + let then_call_stack = self.inserter.function.dfg.get_value_call_stack(then_value); + let else_call_stack = self.inserter.function.dfg.get_value_call_stack(else_value); + + let call_stack = if then_call_stack.is_empty() { + else_call_stack.clone() + } else { + then_call_stack.clone() + }; // We must cast the bool conditions to the actual numeric type used by each value. let then_condition = - self.insert_instruction(Instruction::Cast(then_condition, then_type), then_location); + self.insert_instruction(Instruction::Cast(then_condition, then_type), then_call_stack); let else_condition = - self.insert_instruction(Instruction::Cast(else_condition, else_type), else_location); + self.insert_instruction(Instruction::Cast(else_condition, else_type), else_call_stack); let mul = Instruction::binary(BinaryOp::Mul, then_condition, then_value); let then_value = self .inserter .function .dfg - .insert_instruction_and_results(mul, block, None, merge_location) + .insert_instruction_and_results(mul, block, None, call_stack.clone()) .first(); let mul = Instruction::binary(BinaryOp::Mul, else_condition, else_value); @@ -488,14 +488,14 @@ impl<'f> Context<'f> { .inserter .function .dfg - .insert_instruction_and_results(mul, block, None, merge_location) + .insert_instruction_and_results(mul, block, None, call_stack.clone()) .first(); let add = Instruction::binary(BinaryOp::Add, then_value, else_value); self.inserter .function .dfg - .insert_instruction_and_results(add, block, None, merge_location) + .insert_instruction_and_results(add, block, None, call_stack) .first() } @@ -681,12 +681,12 @@ impl<'f> Context<'f> { /// with a different InstructionId from the original. The results of the given instruction /// will also be mapped to the results of the new instruction. fn push_instruction(&mut self, id: InstructionId) { - let (instruction, location) = self.inserter.map_instruction(id); - let instruction = self.handle_instruction_side_effects(instruction, location); + let (instruction, call_stack) = self.inserter.map_instruction(id); + let instruction = self.handle_instruction_side_effects(instruction, call_stack.clone()); let is_allocate = matches!(instruction, Instruction::Allocate); let entry = self.inserter.function.entry_block(); - let results = self.inserter.push_instruction_value(instruction, id, entry, location); + let results = self.inserter.push_instruction_value(instruction, id, entry, call_stack); // Remember an allocate was created local to this branch so that we do not try to merge store // values across branches for it later. @@ -700,18 +700,18 @@ impl<'f> Context<'f> { fn handle_instruction_side_effects( &mut self, instruction: Instruction, - location: Option, + call_stack: CallStack, ) -> Instruction { if let Some((_, condition)) = self.conditions.last().copied() { match instruction { Instruction::Constrain(value) => { let mul = self.insert_instruction( Instruction::binary(BinaryOp::Mul, value, condition), - location, + call_stack.clone(), ); let eq = self.insert_instruction( Instruction::binary(BinaryOp::Eq, mul, condition), - location, + call_stack, ); Instruction::Constrain(eq) } diff --git a/crates/noirc_evaluator/src/ssa/opt/inlining.rs b/crates/noirc_evaluator/src/ssa/opt/inlining.rs index 36398bd98fe..cf7b5860ea6 100644 --- a/crates/noirc_evaluator/src/ssa/opt/inlining.rs +++ b/crates/noirc_evaluator/src/ssa/opt/inlining.rs @@ -9,7 +9,7 @@ use iter_extended::vecmap; use crate::ssa::{ ir::{ basic_block::BasicBlockId, - dfg::InsertInstructionResult, + dfg::{CallStack, InsertInstructionResult}, function::{Function, FunctionId, RuntimeType}, instruction::{Instruction, InstructionId, TerminatorInstruction}, value::{Value, ValueId}, @@ -49,6 +49,8 @@ struct InlineContext { recursion_level: u32, builder: FunctionBuilder, + call_stack: CallStack, + /// True if we failed to inline at least one call. If this is still false when finishing /// inlining we can remove all other functions from the resulting Ssa struct and keep only /// the function that was inlined into. @@ -98,7 +100,12 @@ impl InlineContext { fn new(ssa: &Ssa) -> InlineContext { let main_name = ssa.main().name().to_owned(); let builder = FunctionBuilder::new(main_name, ssa.next_id.next(), RuntimeType::Acir); - Self { builder, recursion_level: 0, failed_to_inline_a_call: false } + Self { + builder, + recursion_level: 0, + call_stack: CallStack::new(), + failed_to_inline_a_call: false, + } } /// Start inlining the main function and all functions reachable from it. @@ -370,7 +377,21 @@ impl<'function> PerFunctionContext<'function> { ) { let old_results = self.source_function.dfg.instruction_results(call_id); let arguments = vecmap(arguments, |arg| self.translate_value(*arg)); + + let mut call_stack = self.source_function.dfg.get_call_stack(call_id); + let has_location = !call_stack.is_empty(); + + // Function calls created by the defunctionalization pass will not have source locations + if let Some(location) = call_stack.pop_back() { + self.context.call_stack.push_back(location); + } + let new_results = self.context.inline_function(ssa, function, &arguments); + + if has_location { + self.context.call_stack.pop_back(); + } + let new_results = InsertInstructionResult::Results(&new_results); Self::insert_new_instruction_results(&mut self.values, old_results, new_results); } @@ -379,7 +400,10 @@ impl<'function> PerFunctionContext<'function> { /// function being inlined into. fn push_instruction(&mut self, id: InstructionId) { let instruction = self.source_function.dfg[id].map_values(|id| self.translate_value(id)); - let location = self.source_function.dfg.get_location(&id); + + let mut call_stack = self.context.call_stack.clone(); + call_stack.append(self.source_function.dfg.get_call_stack(id)); + let results = self.source_function.dfg.instruction_results(id); let results = vecmap(results, |id| self.source_function.dfg.resolve(*id)); @@ -387,9 +411,8 @@ impl<'function> PerFunctionContext<'function> { .requires_ctrl_typevars() .then(|| vecmap(&results, |result| self.source_function.dfg.type_of_value(*result))); - if let Some(location) = location { - self.context.builder.set_location(location); - } + self.context.builder.set_call_stack(call_stack); + let new_results = self.context.builder.insert_instruction(instruction, ctrl_typevars); Self::insert_new_instruction_results(&mut self.values, &results, new_results); } @@ -433,14 +456,17 @@ impl<'function> PerFunctionContext<'function> { block_queue: &mut Vec, ) -> Option<(BasicBlockId, Vec)> { match self.source_function.dfg[block_id].unwrap_terminator() { - TerminatorInstruction::Jmp { destination, arguments, location } => { + TerminatorInstruction::Jmp { destination, arguments, call_stack } => { let destination = self.translate_block(*destination, block_queue); let arguments = vecmap(arguments, |arg| self.translate_value(*arg)); - self.context.builder.terminate_with_jmp_with_location( - destination, - arguments, - *location, - ); + + let mut new_call_stack = self.context.call_stack.clone(); + new_call_stack.append(call_stack.clone()); + + self.context + .builder + .set_call_stack(new_call_stack) + .terminate_with_jmp(destination, arguments); None } TerminatorInstruction::JmpIf { condition, then_destination, else_destination } => { diff --git a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs index b9e849bb77c..be0ded802b3 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -1,50 +1,86 @@ //! mem2reg implements a pass for promoting values stored in memory to values in registers where //! possible. This is particularly important for converting our memory-based representation of //! mutable variables into values that are easier to manipulate. -use std::collections::{BTreeMap, HashMap, HashSet}; - -use iter_extended::vecmap; +use std::collections::{BTreeMap, HashSet}; use crate::ssa::{ ir::{ basic_block::BasicBlockId, + cfg::ControlFlowGraph, dfg::DataFlowGraph, + dom::DominatorTree, + function::Function, instruction::{Instruction, InstructionId, TerminatorInstruction}, + post_order::PostOrder, value::{Value, ValueId}, }, ssa_gen::Ssa, }; +use super::unrolling::{find_all_loops, Loops}; + impl Ssa { /// Attempts to remove any load instructions that recover values that are already available in - /// scope, and attempts to remove store that are subsequently redundant, as long as they are - /// not stores on memory that will be passed into a function call or returned. + /// scope, and attempts to remove stores that are subsequently redundant. + /// As long as they are not stores on memory used inside of loops pub(crate) fn mem2reg(mut self) -> Ssa { for function in self.functions.values_mut() { let mut all_protected_allocations = HashSet::new(); - let contexts = vecmap(function.reachable_blocks(), |block| { - let mut context = PerBlockContext::new(block); - let allocations_protected_by_block = - context.analyze_allocations_and_eliminate_known_loads(&mut function.dfg); + + let mut context = PerFunctionContext::new(function); + + for block in function.reachable_blocks() { + // Maps Load instruction id -> value to replace the result of the load with + let mut loads_to_substitute_per_block = BTreeMap::new(); + + // Maps Load result id -> value to replace the result of the load with + let mut load_values_to_substitute = BTreeMap::new(); + + let allocations_protected_by_block = context + .analyze_allocations_and_eliminate_known_loads( + &mut function.dfg, + &mut loads_to_substitute_per_block, + &mut load_values_to_substitute, + block, + ); all_protected_allocations.extend(allocations_protected_by_block.into_iter()); - context - }); - // Now that we have a comprehensive list of used allocations across all the - // function's blocks, it is safe to remove any stores that do not touch such - // allocations. - for context in contexts { - context.remove_unused_stores(&mut function.dfg, &all_protected_allocations); } - } + for block in function.reachable_blocks() { + context.remove_unused_stores(&mut function.dfg, &all_protected_allocations, block); + } + } self } } -struct PerBlockContext { - block_id: BasicBlockId, - last_stores: BTreeMap, +struct PerFunctionContext { + last_stores_with_block: BTreeMap<(AllocId, BasicBlockId), ValueId>, + // Maps Load result id -> (value, block_id) + // Used to replace the result of a load with the appropriate block + load_values_to_substitute_per_func: BTreeMap, store_ids: Vec, + cfg: ControlFlowGraph, + post_order: PostOrder, + dom_tree: DominatorTree, + loops: Loops, +} + +impl PerFunctionContext { + fn new(function: &Function) -> Self { + let cfg = ControlFlowGraph::with_function(function); + let post_order = PostOrder::with_function(function); + let dom_tree = DominatorTree::with_cfg_and_post_order(&cfg, &post_order); + PerFunctionContext { + last_stores_with_block: BTreeMap::new(), + load_values_to_substitute_per_func: BTreeMap::new(), + store_ids: Vec::new(), + cfg, + post_order, + dom_tree, + loops: find_all_loops(function), + } + } } /// An AllocId is the ValueId returned from an allocate instruction. E.g. v0 in v0 = allocate. @@ -52,51 +88,57 @@ struct PerBlockContext { /// an allocate instruction. type AllocId = ValueId; -impl PerBlockContext { - fn new(block_id: BasicBlockId) -> Self { - PerBlockContext { block_id, last_stores: BTreeMap::new(), store_ids: Vec::new() } - } - +impl PerFunctionContext { // Attempts to remove load instructions for which the result is already known from previous // store instructions to the same address in the same block. fn analyze_allocations_and_eliminate_known_loads( &mut self, dfg: &mut DataFlowGraph, + loads_to_substitute: &mut BTreeMap, + load_values_to_substitute_per_block: &mut BTreeMap, + block_id: BasicBlockId, ) -> HashSet { let mut protected_allocations = HashSet::new(); - let block = &dfg[self.block_id]; - - // Maps Load instruction id -> value to replace the result of the load with - let mut loads_to_substitute = HashMap::new(); + let block = &dfg[block_id]; - // Maps Load result id -> value to replace the result of the load with - let mut load_values_to_substitute = HashMap::new(); + // Check whether the block has a common successor here to avoid analyzing + // the CFG for every block instruction. + let has_common_successor = self.has_common_successor(block_id); for instruction_id in block.instructions() { match &dfg[*instruction_id] { Instruction::Store { mut address, value } => { - if let Some(value) = load_values_to_substitute.get(&address) { - address = *value; + if has_common_successor { + protected_allocations.insert(address); } + address = self.fetch_load_value_to_substitute(block_id, address); - self.last_stores.insert(address, *value); + self.last_stores_with_block.insert((address, block_id), *value); self.store_ids.push(*instruction_id); + + if has_common_successor { + protected_allocations.insert(address); + } } Instruction::Load { mut address } => { - if let Some(value) = load_values_to_substitute.get(&address) { - address = *value; - } - - if let Some(last_value) = self.last_stores.get(&address) { - let result_value = *dfg - .instruction_results(*instruction_id) - .first() - .expect("ICE: Load instructions should have single result"); - - loads_to_substitute.insert(*instruction_id, *last_value); - load_values_to_substitute.insert(result_value, *last_value); - } else { + address = self.fetch_load_value_to_substitute(block_id, address); + + let found_last_value = self.find_load_to_substitute( + block_id, + address, + dfg, + instruction_id, + loads_to_substitute, + load_values_to_substitute_per_block, + ); + if !found_last_value { + // We want to protect allocations that do not have a load to substitute protected_allocations.insert(address); + // We also want to check for allocations that share a value + // with the one we are protecting. + // This check prevents the pass from removing stores to a value that + // is used by reference aliases in different blocks + protected_allocations.insert(dfg.resolve(address)); } } Instruction::Call { arguments, .. } => { @@ -122,21 +164,179 @@ impl PerBlockContext { } // Substitute load result values - for (result_value, new_value) in load_values_to_substitute { - let result_value = dfg.resolve(result_value); - dfg.set_value_from_id(result_value, new_value); + for (result_value, new_value) in load_values_to_substitute_per_block { + let result_value = dfg.resolve(*result_value); + dfg.set_value_from_id(result_value, *new_value); } // Delete load instructions // Even though we could let DIE handle this, doing it here makes the debug output easier // to read. - dfg[self.block_id] + dfg[block_id] .instructions_mut() .retain(|instruction| !loads_to_substitute.contains_key(instruction)); protected_allocations } + // This method will fetch already saved load values to substitute for a given address. + // The search starts at the block supplied as a parameter. If there is not a load to substitute + // the CFG is analyzed to determine whether a predecessor block has a load value to substitute. + // If there is no load value to substitute the original address is returned. + fn fetch_load_value_to_substitute( + &mut self, + block_id: BasicBlockId, + address: ValueId, + ) -> ValueId { + let mut stack = vec![block_id]; + let mut visited = HashSet::new(); + + while let Some(block) = stack.pop() { + visited.insert(block); + + // We do not want to substitute loads that take place within loops or yet to be simplified branches + // as this pass can occur before loop unrolling and flattening. + // The mem2reg pass should be ran again following all optimization passes as new values + // may be able to be promoted + for l in self.loops.yet_to_unroll.iter() { + // We do not want to substitute loads that take place within loops as this pass + // can occur before loop unrolling + // The pass should be ran again following loop unrolling as new values + if l.blocks.contains(&block) { + return address; + } + } + + // Check whether there is a load value to substitute in the current block. + // Return the value if found. + if let Some((value, load_block_id)) = + self.load_values_to_substitute_per_func.get(&address) + { + if *load_block_id == block { + return *value; + } + } + + // If no load values to substitute have been found in the current block, check the block's predecessors. + if let Some(predecessor) = self.block_has_predecessor(block, &visited) { + stack.push(predecessor); + } + } + address + } + + // This method determines which loads should be substituted and saves them + // to be substituted further in the pass. + // Starting at the block supplied as a parameter, we check whether a store has occurred with the given address. + // If no store has occurred in the supplied block, the CFG is analyzed to determine whether + // a predecessor block has a store at the given address. + fn find_load_to_substitute( + &mut self, + block_id: BasicBlockId, + address: ValueId, + dfg: &DataFlowGraph, + instruction_id: &InstructionId, + loads_to_substitute: &mut BTreeMap, + load_values_to_substitute_per_block: &mut BTreeMap, + ) -> bool { + let mut stack = vec![block_id]; + let mut visited = HashSet::new(); + + while let Some(block) = stack.pop() { + visited.insert(block); + + // We do not want to substitute loads that take place within loops or yet to be simplified branches + // as this pass can occur before loop unrolling and flattening. + // The mem2reg pass should be ran again following all optimization passes as new values + // may be able to be promoted + for l in self.loops.yet_to_unroll.iter() { + // We do not want to substitute loads that take place within loops as this pass + // can occur before loop unrolling + // The pass should be ran again following loop unrolling as new values + if l.blocks.contains(&block) { + return false; + } + } + + // Check whether there has been a store instruction in the current block + // If there has been a store, add a load to be substituted. + if let Some(last_value) = self.last_stores_with_block.get(&(address, block)) { + let result_value = *dfg + .instruction_results(*instruction_id) + .first() + .expect("ICE: Load instructions should have single result"); + + loads_to_substitute.insert(*instruction_id, *last_value); + load_values_to_substitute_per_block.insert(result_value, *last_value); + self.load_values_to_substitute_per_func.insert(result_value, (*last_value, block)); + return true; + } + + // If no load values to substitute have been found in the current block, check the block's predecessors. + if let Some(predecessor) = self.block_has_predecessor(block, &visited) { + stack.push(predecessor); + } + } + false + } + + // If no loads or stores have been found in the current block, check the block's predecessors. + // Only check blocks with one predecessor as otherwise a constant value could be propogated + // through successor blocks with multiple branches that rely on other simplification passes + // such as loop unrolling or flattening of the CFG. + fn block_has_predecessor( + &mut self, + block_id: BasicBlockId, + visited: &HashSet, + ) -> Option { + let mut predecessors = self.cfg.predecessors(block_id); + if predecessors.len() == 1 { + let predecessor = predecessors.next().unwrap(); + if self.dom_tree.is_reachable(predecessor) + && self.dom_tree.dominates(predecessor, block_id) + && !visited.contains(&predecessor) + { + return Some(predecessor); + } + } + None + } + + fn has_common_successor(&mut self, block_id: BasicBlockId) -> bool { + let mut predecessors = self.cfg.predecessors(block_id); + if let Some(predecessor) = predecessors.next() { + let pred_successors = self.find_all_successors(predecessor); + let current_successors: HashSet<_> = self.cfg.successors(block_id).collect(); + return pred_successors.into_iter().any(|b| current_successors.contains(&b)); + } + false + } + + fn find_all_successors(&self, block_id: BasicBlockId) -> HashSet { + let mut stack = vec![]; + let mut visited = HashSet::new(); + + // Fetch initial block successors + let successors = self.cfg.successors(block_id); + for successor in successors { + if !visited.contains(&successor) { + stack.push(successor); + } + } + + // Follow the CFG to fetch the remaining successors + while let Some(block) = stack.pop() { + visited.insert(block); + let successors = self.cfg.successors(block); + for successor in successors { + if !visited.contains(&successor) { + stack.push(successor); + } + } + } + visited + } + /// Checks whether the given value id refers to an allocation. fn value_is_from_allocation(value: ValueId, dfg: &DataFlowGraph) -> bool { match &dfg[value] { @@ -150,9 +350,10 @@ impl PerBlockContext { /// Removes all store instructions identified during analysis that aren't present in the /// provided `protected_allocations` `HashSet`. fn remove_unused_stores( - self, + &self, dfg: &mut DataFlowGraph, protected_allocations: &HashSet, + block_id: BasicBlockId, ) { // Scan for unused stores let mut stores_to_remove = HashSet::new(); @@ -169,7 +370,7 @@ impl PerBlockContext { } // Delete unused stores - dfg[self.block_id] + dfg[block_id] .instructions_mut() .retain(|instruction| !stores_to_remove.contains(instruction)); } @@ -187,7 +388,7 @@ mod tests { basic_block::BasicBlockId, dfg::DataFlowGraph, function::RuntimeType, - instruction::{Instruction, Intrinsic, TerminatorInstruction}, + instruction::{BinaryOp, Instruction, Intrinsic, TerminatorInstruction}, map::Id, types::Type, }, @@ -320,7 +521,7 @@ mod tests { .count() } - // Test that loads across multiple blocks are not removed + // Test that loads across multiple blocks are removed #[test] fn multiple_blocks() { // fn main { @@ -364,24 +565,22 @@ mod tests { // fn main { // b0(): // v0 = allocate - // store v0, Field 5 - // jmp b1(Field 5): // Optimized to constant 5 - // b1(v2: Field): - // v3 = load v0 // kept in program - // store v0, Field 6 - // return v2, v3, Field 6 // Optimized to constant 6 + // jmp b1(Field 5) + // b1(v3: Field): + // return v3, Field 5, Field 6 // Optimized to constants 5 and 6 // } let ssa = ssa.mem2reg(); + let main = ssa.main(); assert_eq!(main.reachable_blocks().len(), 2); - // Only the load from the entry block should be removed + // The loads should be removed assert_eq!(count_loads(main.entry_block(), &main.dfg), 0); - assert_eq!(count_loads(b1, &main.dfg), 1); + assert_eq!(count_loads(b1, &main.dfg), 0); - // All stores should be kept - assert_eq!(count_stores(main.entry_block(), &main.dfg), 1); - assert_eq!(count_stores(b1, &main.dfg), 1); + // All stores should be removed + assert_eq!(count_stores(main.entry_block(), &main.dfg), 0); + assert_eq!(count_stores(b1, &main.dfg), 0); // The jmp to b1 should also be a constant 5 now match main.dfg[main.entry_block()].terminator() { @@ -394,4 +593,95 @@ mod tests { _ => unreachable!(), }; } + + // Test that a load in a predecessor block has been removed if the value + // is later stored in a successor block + #[test] + fn store_with_load_in_predecessor_block() { + // fn main { + // b0(): + // v0 = allocate + // store Field 0 at v0 + // v2 = allocate + // store v0 at v2 + // v3 = load v2 + // v4 = load v2 + // jmp b1() + // b1(): + // store Field 1 at v3 + // store Field 2 at v4 + // v8 = load v3 + // v9 = eq v8, Field 2 + // return + // } + let main_id = Id::test_new(0); + let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + + let v0 = builder.insert_allocate(); + + let zero = builder.field_constant(0u128); + builder.insert_store(v0, zero); + + let v2 = builder.insert_allocate(); + builder.insert_store(v2, v0); + + let v3 = builder.insert_load(v2, Type::field()); + let v4 = builder.insert_load(v2, Type::field()); + let b1 = builder.insert_block(); + builder.terminate_with_jmp(b1, vec![]); + + builder.switch_to_block(b1); + + let one = builder.field_constant(1u128); + builder.insert_store(v3, one); + + let two = builder.field_constant(2u128); + builder.insert_store(v4, two); + + let v8 = builder.insert_load(v3, Type::field()); + let _ = builder.insert_binary(v8, BinaryOp::Eq, two); + + builder.terminate_with_return(vec![]); + + let ssa = builder.finish(); + assert_eq!(ssa.main().reachable_blocks().len(), 2); + + // Expected result: + // fn main { + // b0(): + // v0 = allocate + // v2 = allocate + // jmp b1() + // b1(): + // v8 = eq Field 2, Field 2 + // return + // } + let ssa = ssa.mem2reg(); + + let main = ssa.main(); + assert_eq!(main.reachable_blocks().len(), 2); + + // All loads should be removed + assert_eq!(count_loads(main.entry_block(), &main.dfg), 0); + assert_eq!(count_loads(b1, &main.dfg), 0); + + // All stores should be removed + assert_eq!(count_stores(main.entry_block(), &main.dfg), 0); + assert_eq!(count_stores(b1, &main.dfg), 0); + + let b1_instructions = main.dfg[b1].instructions(); + // The first instruction should be a binary operation + match &main.dfg[b1_instructions[0]] { + Instruction::Binary(binary) => { + let lhs = + main.dfg.get_numeric_constant(binary.lhs).expect("Expected constant value"); + let rhs = + main.dfg.get_numeric_constant(binary.rhs).expect("Expected constant value"); + + assert_eq!(lhs, rhs); + assert_eq!(lhs, FieldElement::from(2u128)); + } + _ => unreachable!(), + } + } } diff --git a/crates/noirc_evaluator/src/ssa/opt/mod.rs b/crates/noirc_evaluator/src/ssa/opt/mod.rs index 23b4c726a6b..9b9350118ad 100644 --- a/crates/noirc_evaluator/src/ssa/opt/mod.rs +++ b/crates/noirc_evaluator/src/ssa/opt/mod.rs @@ -4,6 +4,7 @@ //! simpler form until the IR only has a single function remaining with 1 block within it. //! Generally, these passes are also expected to minimize the final amount of instructions. mod array_use; +mod assert_constant; mod constant_folding; mod defunctionalize; mod die; diff --git a/crates/noirc_evaluator/src/ssa/opt/simplify_cfg.rs b/crates/noirc_evaluator/src/ssa/opt/simplify_cfg.rs index 26c7b161550..ad257362da5 100644 --- a/crates/noirc_evaluator/src/ssa/opt/simplify_cfg.rs +++ b/crates/noirc_evaluator/src/ssa/opt/simplify_cfg.rs @@ -13,7 +13,7 @@ use std::collections::HashSet; use crate::ssa::{ ir::{ - basic_block::BasicBlockId, cfg::ControlFlowGraph, function::Function, + basic_block::BasicBlockId, cfg::ControlFlowGraph, dfg::CallStack, function::Function, instruction::TerminatorInstruction, }, ssa_gen::Ssa, @@ -87,7 +87,8 @@ fn check_for_constant_jmpif( if constant.is_zero() { *else_destination } else { *then_destination }; let arguments = Vec::new(); - let jmp = TerminatorInstruction::Jmp { destination, arguments, location: None }; + let jmp = + TerminatorInstruction::Jmp { destination, arguments, call_stack: CallStack::new() }; function.dfg[block].set_terminator(jmp); cfg.recompute_block(function, block); } diff --git a/crates/noirc_evaluator/src/ssa/opt/unrolling.rs b/crates/noirc_evaluator/src/ssa/opt/unrolling.rs index ac38f102712..9bf62bda1cb 100644 --- a/crates/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/crates/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -14,15 +14,13 @@ //! program that will need to be removed by a later simplify cfg pass. use std::collections::{HashMap, HashSet}; -use noirc_errors::Location; - use crate::{ errors::RuntimeError, ssa::{ ir::{ basic_block::BasicBlockId, cfg::ControlFlowGraph, - dfg::DataFlowGraph, + dfg::{CallStack, DataFlowGraph}, dom::DominatorTree, function::{Function, RuntimeType}, function_inserter::FunctionInserter, @@ -46,7 +44,7 @@ impl Ssa { } } -struct Loop { +pub(crate) struct Loop { /// The header block of a loop is the block which dominates all the /// other blocks in the loop. header: BasicBlockId, @@ -56,15 +54,15 @@ struct Loop { back_edge_start: BasicBlockId, /// All the blocks contained within the loop, including `header` and `back_edge_start`. - blocks: HashSet, + pub(crate) blocks: HashSet, } -struct Loops { +pub(crate) struct Loops { /// The loops that failed to be unrolled so that we do not try to unroll them again. /// Each loop is identified by its header block id. failed_to_unroll: HashSet, - yet_to_unroll: Vec, + pub(crate) yet_to_unroll: Vec, modified_blocks: HashSet, cfg: ControlFlowGraph, dom_tree: DominatorTree, @@ -72,7 +70,7 @@ struct Loops { /// Find a loop in the program by finding a node that dominates any predecessor node. /// The edge where this happens will be the back-edge of the loop. -fn find_all_loops(function: &Function) -> Loops { +pub(crate) fn find_all_loops(function: &Function) -> Loops { let cfg = ControlFlowGraph::with_function(function); let post_order = PostOrder::with_function(function); let mut dom_tree = DominatorTree::with_cfg_and_post_order(&cfg, &post_order); @@ -126,8 +124,8 @@ impl Loops { if !self.failed_to_unroll.contains(&next_loop.header) { match unroll_loop(function, &self.cfg, &next_loop) { Ok(_) => self.modified_blocks.extend(next_loop.blocks), - Err(location) if abort_on_error => { - return Err(RuntimeError::UnknownLoopBound { location }); + Err(call_stack) if abort_on_error => { + return Err(RuntimeError::UnknownLoopBound { call_stack }); } Err(_) => { self.failed_to_unroll.insert(next_loop.header); @@ -176,7 +174,7 @@ fn unroll_loop( function: &mut Function, cfg: &ControlFlowGraph, loop_: &Loop, -) -> Result<(), Option> { +) -> Result<(), CallStack> { let mut unroll_into = get_pre_header(cfg, loop_); let mut jump_value = get_induction_variable(function, unroll_into)?; @@ -206,12 +204,9 @@ fn get_pre_header(cfg: &ControlFlowGraph, loop_: &Loop) -> BasicBlockId { /// /// Expects the current block to terminate in `jmp h(N)` where h is the loop header and N is /// a Field value. -fn get_induction_variable( - function: &Function, - block: BasicBlockId, -) -> Result> { +fn get_induction_variable(function: &Function, block: BasicBlockId) -> Result { match function.dfg[block].terminator() { - Some(TerminatorInstruction::Jmp { arguments, location, .. }) => { + Some(TerminatorInstruction::Jmp { arguments, call_stack: location, .. }) => { // This assumption will no longer be valid if e.g. mutable variables are represented as // block parameters. If that becomes the case we'll need to figure out which variable // is generally constant and increasing to guess which parameter is the induction @@ -221,10 +216,10 @@ fn get_induction_variable( if function.dfg.get_numeric_constant(value).is_some() { Ok(value) } else { - Err(*location) + Err(location.clone()) } } - _ => Err(None), + _ => Err(CallStack::new()), } } @@ -236,7 +231,7 @@ fn unroll_loop_header<'a>( loop_: &'a Loop, unroll_into: BasicBlockId, induction_value: ValueId, -) -> Result>, Option> { +) -> Result>, CallStack> { // We insert into a fresh block first and move instructions into the unroll_into block later // only once we verify the jmpif instruction has a constant condition. If it does not, we can // just discard this fresh block and leave the loop unmodified. @@ -269,7 +264,7 @@ fn unroll_loop_header<'a>( } else { // If this case is reached the loop either uses non-constant indices or we need // another pass, such as mem2reg to resolve them to constants. - Err(context.inserter.function.dfg.get_value_location(&condition)) + Err(context.inserter.function.dfg.get_value_call_stack(condition)) } } other => unreachable!("Expected loop header to terminate in a JmpIf to the loop body, but found {other:?} instead"), @@ -361,7 +356,7 @@ impl<'f> LoopIteration<'f> { TerminatorInstruction::JmpIf { condition, then_destination, else_destination } => { self.handle_jmpif(*condition, *then_destination, *else_destination) } - TerminatorInstruction::Jmp { destination, arguments, location: _ } => { + TerminatorInstruction::Jmp { destination, arguments, call_stack: _ } => { if self.get_original_block(*destination) == self.loop_.header { assert_eq!(arguments.len(), 1); self.induction_value = Some((self.insert_block, arguments[0])); @@ -391,7 +386,11 @@ impl<'f> LoopIteration<'f> { self.source_block = self.get_original_block(destination); let arguments = Vec::new(); - let jmp = TerminatorInstruction::Jmp { destination, arguments, location: None }; + let jmp = TerminatorInstruction::Jmp { + destination, + arguments, + call_stack: CallStack::new(), + }; self.inserter.function.dfg.set_block_terminator(self.insert_block, jmp); vec![destination] } diff --git a/crates/noirc_evaluator/src/ssa/ssa_builder/mod.rs b/crates/noirc_evaluator/src/ssa/ssa_builder/mod.rs index 61daa06e37b..0958b0d7a67 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_builder/mod.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_builder/mod.rs @@ -14,7 +14,7 @@ use crate::ssa::ir::{ use super::{ ir::{ basic_block::BasicBlock, - dfg::InsertInstructionResult, + dfg::{CallStack, InsertInstructionResult}, function::RuntimeType, instruction::{InstructionId, Intrinsic}, }, @@ -32,7 +32,7 @@ pub(crate) struct FunctionBuilder { pub(super) current_function: Function, current_block: BasicBlockId, finished_functions: Vec, - current_location: Option, + call_stack: CallStack, } impl FunctionBuilder { @@ -53,7 +53,7 @@ impl FunctionBuilder { current_function: new_function, current_block, finished_functions: Vec::new(), - current_location: None, + call_stack: CallStack::new(), } } @@ -150,7 +150,7 @@ impl FunctionBuilder { instruction, self.current_block, ctrl_typevars, - self.current_location, + self.call_stack.clone(), ) } @@ -174,7 +174,12 @@ impl FunctionBuilder { } pub(crate) fn set_location(&mut self, location: Location) -> &mut FunctionBuilder { - self.current_location = Some(location); + self.call_stack = im::Vector::unit(location); + self + } + + pub(crate) fn set_call_stack(&mut self, call_stack: CallStack) -> &mut FunctionBuilder { + self.call_stack = call_stack; self } @@ -281,19 +286,12 @@ impl FunctionBuilder { destination: BasicBlockId, arguments: Vec, ) { - self.terminate_with_jmp_with_location(destination, arguments, None); - } - - /// Terminate the current block with a jmp instruction to jmp to the given - /// block with the given arguments. This version also remembers the Location of the jmp - /// for issuing error messages. - pub(crate) fn terminate_with_jmp_with_location( - &mut self, - destination: BasicBlockId, - arguments: Vec, - location: Option, - ) { - self.terminate_block_with(TerminatorInstruction::Jmp { destination, arguments, location }); + let call_stack = self.call_stack.clone(); + self.terminate_block_with(TerminatorInstruction::Jmp { + destination, + arguments, + call_stack, + }); } /// Terminate the current block with a jmpif instruction to jmp with the given arguments diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs index 6de804a37b8..bcba7bf5992 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -537,7 +537,14 @@ impl<'a> FunctionContext<'a> { /// version, as it is only needed for recursion. pub(super) fn extract_current_value(&mut self, lvalue: &ast::LValue) -> LValue { match lvalue { - ast::LValue::Ident(ident) => LValue::Ident(self.ident_lvalue(ident)), + ast::LValue::Ident(ident) => { + let (reference, should_auto_deref) = self.ident_lvalue(ident); + if should_auto_deref { + LValue::Dereference { reference } + } else { + LValue::Ident + } + } ast::LValue::Index { array, index, .. } => self.index_lvalue(array, index).2, ast::LValue::MemberAccess { object, field_index } => { let (old_object, object_lvalue) = self.extract_current_value_recursive(object); @@ -551,18 +558,19 @@ impl<'a> FunctionContext<'a> { } } - pub(super) fn dereference(&mut self, values: &Values, element_type: &ast::Type) -> Values { + fn dereference_lvalue(&mut self, values: &Values, element_type: &ast::Type) -> Values { let element_types = Self::convert_type(element_type); values.map_both(element_types, |value, element_type| { - let reference = value.eval(self); + let reference = value.eval_reference(); self.builder.insert_load(reference, element_type).into() }) } - /// Compile the given identifier as a reference - ie. avoid calling .eval() - fn ident_lvalue(&self, ident: &ast::Ident) -> Values { + /// Compile the given identifier as a reference - ie. avoid calling .eval(). + /// Returns the variable's value and whether the variable is mutable. + fn ident_lvalue(&self, ident: &ast::Ident) -> (Values, bool) { match &ident.definition { - ast::Definition::Local(id) => self.lookup(*id), + ast::Definition::Local(id) => (self.lookup(*id), ident.mutable), other => panic!("Unexpected definition found for mutable value: {other}"), } } @@ -585,8 +593,13 @@ impl<'a> FunctionContext<'a> { fn extract_current_value_recursive(&mut self, lvalue: &ast::LValue) -> (Values, LValue) { match lvalue { ast::LValue::Ident(ident) => { - let variable = self.ident_lvalue(ident); - (variable.clone(), LValue::Ident(variable)) + let (variable, should_auto_deref) = self.ident_lvalue(ident); + if should_auto_deref { + let dereferenced = self.dereference_lvalue(&variable, &ident.typ); + (dereferenced, LValue::Dereference { reference: variable }) + } else { + (variable.clone(), LValue::Ident) + } } ast::LValue::Index { array, index, element_type, location } => { let (old_array, index, index_lvalue) = self.index_lvalue(array, index); @@ -601,7 +614,7 @@ impl<'a> FunctionContext<'a> { } ast::LValue::Dereference { reference, element_type } => { let (reference, _) = self.extract_current_value_recursive(reference); - let dereferenced = self.dereference(&reference, element_type); + let dereferenced = self.dereference_lvalue(&reference, element_type); (dereferenced, LValue::Dereference { reference }) } } @@ -614,7 +627,7 @@ impl<'a> FunctionContext<'a> { /// `extract_current_value` for more details. pub(super) fn assign_new_value(&mut self, lvalue: LValue, new_value: Values) { match lvalue { - LValue::Ident(references) => self.assign(references, new_value), + LValue::Ident => unreachable!("Cannot assign to a variable without a reference"), LValue::Index { old_array: mut array, index, array_lvalue } => { let element_size = self.builder.field_constant(self.element_size(array)); @@ -811,7 +824,7 @@ impl SharedContext { /// Used to remember the results of each step of extracting a value from an ast::LValue #[derive(Debug)] pub(super) enum LValue { - Ident(Values), + Ident, Index { old_array: ValueId, index: ValueId, array_lvalue: Box }, MemberAccess { old_object: Values, index: usize, object_lvalue: Box }, Dereference { reference: Values }, diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 11c42d8b051..790c99fee05 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -215,6 +215,14 @@ impl<'a> FunctionContext<'a> { } } + fn dereference(&mut self, values: &Values, element_type: &ast::Type) -> Values { + let element_types = Self::convert_type(element_type); + values.map_both(element_types, |value, element_type| { + let reference = value.eval(self); + self.builder.insert_load(reference, element_type).into() + }) + } + fn codegen_reference(&mut self, expr: &Expression) -> Values { match expr { Expression::Ident(ident) => self.codegen_ident_reference(ident), @@ -306,8 +314,8 @@ impl<'a> FunctionContext<'a> { // Set the location of the initial jmp instruction to the start range. This is the location // used to issue an error if the start range cannot be determined at compile-time. - let jmp_location = Some(for_expr.start_range_location); - self.builder.terminate_with_jmp_with_location(loop_entry, vec![start_index], jmp_location); + self.builder.set_location(for_expr.start_range_location); + self.builder.terminate_with_jmp(loop_entry, vec![start_index]); // Compile the loop entry block self.builder.switch_to_block(loop_entry); diff --git a/crates/noirc_frontend/src/ast/expression.rs b/crates/noirc_frontend/src/ast/expression.rs index b1170ff0ed0..c7ed4d0c766 100644 --- a/crates/noirc_frontend/src/ast/expression.rs +++ b/crates/noirc_frontend/src/ast/expression.rs @@ -364,11 +364,19 @@ pub struct FunctionDefinition { pub body: BlockExpression, pub span: Span, pub where_clause: Vec, - pub return_type: UnresolvedType, + pub return_type: FunctionReturnType, pub return_visibility: noirc_abi::AbiVisibility, pub return_distinctness: noirc_abi::AbiDistinctness, } +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum FunctionReturnType { + /// Returns type is not specified. + Default(Span), + /// Everything else. + Ty(UnresolvedType, Span), +} + /// Describes the types of smart contract functions that are allowed. /// - All Noir programs in the non-contract context can be seen as `Secret`. #[derive(serde::Serialize, serde::Deserialize, Debug, Copy, Clone, PartialEq, Eq)] @@ -636,3 +644,12 @@ impl Display for FunctionDefinition { ) } } + +impl Display for FunctionReturnType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + FunctionReturnType::Default(_) => f.write_str(""), + FunctionReturnType::Ty(ty, _) => write!(f, "{ty}"), + } + } +} diff --git a/crates/noirc_frontend/src/ast/function.rs b/crates/noirc_frontend/src/ast/function.rs index 02af960f7a8..43db3294dd9 100644 --- a/crates/noirc_frontend/src/ast/function.rs +++ b/crates/noirc_frontend/src/ast/function.rs @@ -1,6 +1,6 @@ use std::fmt::Display; -use crate::{token::Attribute, Ident, Pattern}; +use crate::{token::Attribute, FunctionReturnType, Ident, Pattern}; use super::{FunctionDefinition, UnresolvedType}; @@ -41,7 +41,10 @@ impl NoirFunction { } pub fn return_type(&self) -> UnresolvedType { - self.def.return_type.clone() + match &self.def.return_type { + FunctionReturnType::Default(_) => UnresolvedType::Unit, + FunctionReturnType::Ty(ty, _) => ty.clone(), + } } pub fn name(&self) -> &str { &self.name_ident().0.contents diff --git a/crates/noirc_frontend/src/ast/traits.rs b/crates/noirc_frontend/src/ast/traits.rs index bb5d2117037..4a649a64cc6 100644 --- a/crates/noirc_frontend/src/ast/traits.rs +++ b/crates/noirc_frontend/src/ast/traits.rs @@ -3,7 +3,10 @@ use std::fmt::Display; use iter_extended::vecmap; use noirc_errors::Span; -use crate::{BlockExpression, Expression, Ident, NoirFunction, UnresolvedGenerics, UnresolvedType}; +use crate::{ + BlockExpression, Expression, FunctionReturnType, Ident, NoirFunction, UnresolvedGenerics, + UnresolvedType, +}; /// AST node for trait definitions: /// `trait name { ... items ... }` @@ -24,7 +27,7 @@ pub enum TraitItem { name: Ident, generics: Vec, parameters: Vec<(Ident, UnresolvedType)>, - return_type: UnresolvedType, + return_type: FunctionReturnType, where_clause: Vec, body: Option, }, diff --git a/crates/noirc_frontend/src/graph/mod.rs b/crates/noirc_frontend/src/graph/mod.rs index 2c251432e9b..90490c47c36 100644 --- a/crates/noirc_frontend/src/graph/mod.rs +++ b/crates/noirc_frontend/src/graph/mod.rs @@ -29,6 +29,12 @@ impl CrateId { #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct CrateName(SmolStr); +impl CrateName { + fn is_valid_name(name: &str) -> bool { + !name.is_empty() && name.chars().all(|n| !CHARACTER_BLACK_LIST.contains(&n)) + } +} + impl Display for CrateName { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) @@ -54,11 +60,28 @@ impl FromStr for CrateName { type Err = String; fn from_str(name: &str) -> Result { - let is_invalid = name.chars().any(|n| CHARACTER_BLACK_LIST.contains(&n)); - if is_invalid { - Err(name.into()) - } else { + if Self::is_valid_name(name) { Ok(Self(SmolStr::new(name))) + } else { + Err("Package names must be non-empty and cannot contain hyphens".into()) + } + } +} + +#[cfg(test)] +mod crate_name { + use super::{CrateName, CHARACTER_BLACK_LIST}; + + #[test] + fn it_rejects_empty_string() { + assert!(!CrateName::is_valid_name("")); + } + + #[test] + fn it_rejects_blacklisted_chars() { + for bad_char in CHARACTER_BLACK_LIST { + let bad_char_string = bad_char.to_string(); + assert!(!CrateName::is_valid_name(&bad_char_string)); } } } diff --git a/crates/noirc_frontend/src/hir/def_collector/dc_crate.rs b/crates/noirc_frontend/src/hir/def_collector/dc_crate.rs index e256bbd8cb8..ac83e81ea71 100644 --- a/crates/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/crates/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -137,7 +137,7 @@ impl DefCollector { let current_def_map = context.def_maps.get(&crate_id).unwrap(); errors.extend(vecmap(unresolved_imports, |(error, module_id)| { - let file_id = current_def_map.modules[module_id.0].origin.file_id(); + let file_id = current_def_map.file_id(module_id); let error = DefCollectorErrorKind::PathResolutionError(error); error.into_file_diagnostic(file_id) })); @@ -229,7 +229,7 @@ fn collect_impls( let path_resolver = StandardPathResolver::new(ModuleId { local_id: *module_id, krate: crate_id }); - let file = def_maps[&crate_id].module_file_id(*module_id); + let file = def_maps[&crate_id].file_id(*module_id); for (generics, span, unresolved) in methods { let mut resolver = Resolver::new(interner, &path_resolver, def_maps, file); @@ -425,7 +425,7 @@ fn resolve_impls( let path_resolver = StandardPathResolver::new(ModuleId { local_id: module_id, krate: crate_id }); - let file = def_maps[&crate_id].module_file_id(module_id); + let file = def_maps[&crate_id].file_id(module_id); for (generics, _, functions) in methods { let mut resolver = Resolver::new(interner, &path_resolver, def_maps, file); diff --git a/crates/noirc_frontend/src/hir/def_collector/dc_mod.rs b/crates/noirc_frontend/src/hir/def_collector/dc_mod.rs index 545375dc64a..53ed397e647 100644 --- a/crates/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/crates/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -1,5 +1,5 @@ use fm::FileId; -use noirc_errors::FileDiagnostic; +use noirc_errors::{FileDiagnostic, Location}; use crate::{ graph::CrateId, hir::def_collector::dc_crate::UnresolvedStruct, node_interner::StructId, @@ -11,7 +11,7 @@ use super::{ dc_crate::{DefCollector, UnresolvedFunctions, UnresolvedGlobal, UnresolvedTypeAlias}, errors::{DefCollectorErrorKind, DuplicateType}, }; -use crate::hir::def_map::{parse_file, LocalModuleId, ModuleData, ModuleId, ModuleOrigin}; +use crate::hir::def_map::{parse_file, LocalModuleId, ModuleData, ModuleId}; use crate::hir::resolution::import::ImportDirective; use crate::hir::Context; @@ -315,7 +315,8 @@ impl<'a> ModCollector<'a> { errors: &mut Vec, ) -> Option { let parent = Some(self.module_id); - let new_module = ModuleData::new(parent, ModuleOrigin::File(file_id), is_contract); + let location = Location::new(mod_name.span(), file_id); + let new_module = ModuleData::new(parent, location, is_contract); let module_id = self.def_collector.def_map.modules.insert(new_module); let modules = &mut self.def_collector.def_map.modules; diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index b32f332bd25..f264d3f40b8 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -6,7 +6,7 @@ use crate::parser::{parse_program, ParsedModule}; use crate::token::Attribute; use arena::{Arena, Index}; use fm::{FileId, FileManager}; -use noirc_errors::FileDiagnostic; +use noirc_errors::{FileDiagnostic, Location}; use std::collections::HashMap; mod module_def; @@ -87,8 +87,8 @@ impl CrateDefMap { // Allocate a default Module for the root, giving it a ModuleId let mut modules: Arena = Arena::default(); - let origin = ModuleOrigin::CrateRoot(root_file_id); - let root = modules.insert(ModuleData::new(None, origin, false)); + let location = Location::new(Default::default(), root_file_id); + let root = modules.insert(ModuleData::new(None, location, false)); let def_map = CrateDefMap { root: LocalModuleId(root), @@ -120,13 +120,8 @@ impl CrateDefMap { root_module.find_func_with_name(&MAIN_FUNCTION.into()) } - pub fn root_file_id(&self) -> FileId { - let root_module = &self.modules()[self.root.0]; - root_module.origin.into() - } - - pub fn module_file_id(&self, module_id: LocalModuleId) -> FileId { - self.modules[module_id.0].origin.file_id() + pub fn file_id(&self, module_id: LocalModuleId) -> FileId { + self.modules[module_id.0].location.file } /// Go through all modules in this crate, and find all functions in diff --git a/crates/noirc_frontend/src/hir/def_map/module_data.rs b/crates/noirc_frontend/src/hir/def_map/module_data.rs index 5b93d04fea7..5382ca6ad15 100644 --- a/crates/noirc_frontend/src/hir/def_map/module_data.rs +++ b/crates/noirc_frontend/src/hir/def_map/module_data.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use fm::FileId; +use noirc_errors::Location; use crate::{ node_interner::{FuncId, StmtId, StructId, TypeAliasId}, @@ -23,24 +23,20 @@ pub struct ModuleData { /// Contains only the definitions directly defined in the current module definitions: ItemScope, - pub origin: ModuleOrigin, + pub location: Location, /// True if this module is a `contract Foo { ... }` module containing contract functions pub is_contract: bool, } impl ModuleData { - pub fn new( - parent: Option, - origin: ModuleOrigin, - is_contract: bool, - ) -> ModuleData { + pub fn new(parent: Option, location: Location, is_contract: bool) -> ModuleData { ModuleData { parent, children: HashMap::new(), scope: ItemScope::default(), definitions: ItemScope::default(), - origin, + location, is_contract, } } @@ -99,30 +95,3 @@ impl ModuleData { self.definitions.values().values().map(|(id, _)| *id) } } - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub enum ModuleOrigin { - CrateRoot(FileId), - File(FileId), -} - -impl ModuleOrigin { - pub fn file_id(&self) -> FileId { - match self { - ModuleOrigin::CrateRoot(file_id) => *file_id, - ModuleOrigin::File(file_id) => *file_id, - } - } -} - -impl From for FileId { - fn from(origin: ModuleOrigin) -> Self { - origin.file_id() - } -} - -impl Default for ModuleOrigin { - fn default() -> Self { - ModuleOrigin::CrateRoot(FileId::default()) - } -} diff --git a/crates/noirc_frontend/src/hir/mod.rs b/crates/noirc_frontend/src/hir/mod.rs index 91a6006d096..497935c8ece 100644 --- a/crates/noirc_frontend/src/hir/mod.rs +++ b/crates/noirc_frontend/src/hir/mod.rs @@ -25,6 +25,13 @@ pub struct Context { pub storage_slots: HashMap, } +#[derive(Debug, Copy, Clone)] +pub enum FunctionNameMatch<'a> { + Anything, + Exact(&'a str), + Contains(&'a str), +} + pub type StorageSlot = u32; impl Context { @@ -52,10 +59,29 @@ impl Context { self.crate_graph.iter_keys() } + // TODO: Decide if we actually need `function_name` and `fully_qualified_function_name` pub fn function_name(&self, id: &FuncId) -> &str { self.def_interner.function_name(id) } + pub fn fully_qualified_function_name(&self, crate_id: &CrateId, id: &FuncId) -> String { + let def_map = self.def_map(crate_id).expect("The local crate should be analyzed already"); + + let name = self.def_interner.function_name(id); + + let meta = self.def_interner.function_meta(id); + let module = self.module(meta.module_id); + + let parent = + def_map.get_module_path_with_separator(meta.module_id.local_id.0, module.parent, "::"); + + if parent.is_empty() { + name.into() + } else { + format!("{parent}::{name}") + } + } + pub fn function_meta(&self, func_id: &FuncId) -> FuncMeta { self.def_interner.function_meta(func_id) } @@ -76,7 +102,7 @@ impl Context { pub fn get_all_test_functions_in_crate_matching( &self, crate_id: &CrateId, - pattern: &str, + pattern: FunctionNameMatch, ) -> Vec<(String, FuncId)> { let interner = &self.def_interner; let def_map = self.def_map(crate_id).expect("The local crate should be analyzed already"); @@ -84,20 +110,16 @@ impl Context { def_map .get_all_test_functions(interner) .filter_map(|id| { - let name = interner.function_name(&id); - - let meta = interner.function_meta(&id); - let module = self.module(meta.module_id); - - let parent = def_map.get_module_path_with_separator( - meta.module_id.local_id.0, - module.parent, - "::", - ); - let path = - if parent.is_empty() { name.into() } else { format!("{parent}::{name}") }; - - path.contains(pattern).then_some((path, id)) + let fully_qualified_name = self.fully_qualified_function_name(crate_id, &id); + match &pattern { + FunctionNameMatch::Anything => Some((fully_qualified_name, id)), + FunctionNameMatch::Exact(pattern) => { + (&fully_qualified_name == pattern).then_some((fully_qualified_name, id)) + } + FunctionNameMatch::Contains(pattern) => { + fully_qualified_name.contains(pattern).then_some((fully_qualified_name, id)) + } + } }) .collect() } diff --git a/crates/noirc_frontend/src/hir/resolution/resolver.rs b/crates/noirc_frontend/src/hir/resolution/resolver.rs index 4bfb5428ed7..945d11aae51 100644 --- a/crates/noirc_frontend/src/hir/resolution/resolver.rs +++ b/crates/noirc_frontend/src/hir/resolution/resolver.rs @@ -726,6 +726,7 @@ impl<'a> Resolver<'a> { location, typ, parameters: parameters.into(), + return_type: func.def.return_type.clone(), return_visibility: func.def.return_visibility, return_distinctness: func.def.return_distinctness, has_body: !func.def.body.is_empty(), @@ -1485,8 +1486,9 @@ mod test { use fm::FileId; use iter_extended::vecmap; + use noirc_errors::Location; - use crate::hir::def_map::{ModuleData, ModuleId, ModuleOrigin}; + use crate::hir::def_map::{ModuleData, ModuleId}; use crate::hir::resolution::errors::ResolverError; use crate::hir::resolution::import::PathResolutionError; use crate::hir::resolution::resolver::StmtId; @@ -1520,7 +1522,8 @@ mod test { let file = FileId::default(); let mut modules = arena::Arena::new(); - modules.insert(ModuleData::new(None, ModuleOrigin::File(file), false)); + let location = Location::new(Default::default(), file); + modules.insert(ModuleData::new(None, location, false)); let path_resolver = TestPathResolver(HashMap::new()); @@ -1924,7 +1927,7 @@ mod test { fn main() { let string = f"this is i: {i}"; println(string); - + println(f"I want to print {0}"); let new_val = 10; diff --git a/crates/noirc_frontend/src/hir/type_check/errors.rs b/crates/noirc_frontend/src/hir/type_check/errors.rs index 84bf511d314..9e085422316 100644 --- a/crates/noirc_frontend/src/hir/type_check/errors.rs +++ b/crates/noirc_frontend/src/hir/type_check/errors.rs @@ -1,3 +1,4 @@ +use acvm::FieldElement; use noirc_errors::CustomDiagnostic as Diagnostic; use noirc_errors::Span; use thiserror::Error; @@ -5,6 +6,7 @@ use thiserror::Error; use crate::hir::resolution::errors::ResolverError; use crate::hir_def::expr::HirBinaryOp; use crate::hir_def::types::Type; +use crate::FunctionReturnType; use crate::Signedness; #[derive(Error, Debug, Clone, PartialEq, Eq)] @@ -23,12 +25,16 @@ pub enum Source { Comparison, #[error("BinOp")] BinOp, + #[error("Return")] + Return(FunctionReturnType, Span), } #[derive(Error, Debug, Clone, PartialEq, Eq)] pub enum TypeCheckError { #[error("Operator {op:?} cannot be used in a {place:?}")] OpCannotBeUsed { op: HirBinaryOp, place: &'static str, span: Span }, + #[error("The literal `{expr:?}` cannot fit into `{ty}` which has range `{range}`")] + OverflowingAssignment { expr: FieldElement, ty: Type, range: String, span: Span }, #[error("Type {typ:?} cannot be used in a {place:?}")] TypeCannotBeUsed { typ: Type, place: &'static str, span: Span }, #[error("Expected type {expected_typ:?} is not the same as {expr_typ:?}")] @@ -170,7 +176,8 @@ impl From for Diagnostic { | TypeCheckError::IntegerTypeMismatch { span, .. } | TypeCheckError::FieldComparison { span, .. } | TypeCheckError::AmbiguousBitWidth { span, .. } - | TypeCheckError::IntegerAndFieldBinaryOperation { span } => { + | TypeCheckError::IntegerAndFieldBinaryOperation { span } + | TypeCheckError::OverflowingAssignment { span, .. } => { Diagnostic::simple_error(error.to_string(), String::new(), span) } TypeCheckError::PublicReturnType { typ, span } => Diagnostic::simple_error( @@ -195,6 +202,21 @@ impl From for Diagnostic { Source::StringLen => format!("Can only compare strings of the same length. Here LHS is of length {lhs}, and RHS is {rhs}"), Source::Comparison => format!("Unsupported types for comparison: {lhs} and {rhs}"), Source::BinOp => format!("Unsupported types for binary operation: {lhs} and {rhs}"), + Source::Return(ret_ty, expr_span) => { + let ret_ty_span = match ret_ty { + FunctionReturnType::Default(span) | FunctionReturnType::Ty(_, span) => span + }; + + let mut diagnostic = Diagnostic::simple_error(format!("expected type {lhs}, found type {rhs}"), format!("expected {lhs} because of return type"), ret_ty_span); + + if let FunctionReturnType::Default(_) = ret_ty { + diagnostic.add_note(format!("help: try adding a return type: `-> {rhs}`")); + } + + diagnostic.add_secondary(format!("{rhs} returned here"), expr_span); + + return diagnostic + }, }; Diagnostic::simple_error(message, String::new(), span) diff --git a/crates/noirc_frontend/src/hir/type_check/mod.rs b/crates/noirc_frontend/src/hir/type_check/mod.rs index 8768120af2f..a047b417b32 100644 --- a/crates/noirc_frontend/src/hir/type_check/mod.rs +++ b/crates/noirc_frontend/src/hir/type_check/mod.rs @@ -14,10 +14,13 @@ mod stmt; pub use errors::TypeCheckError; use crate::{ + hir_def::{expr::HirExpression, stmt::HirStatement}, node_interner::{ExprId, FuncId, NodeInterner, StmtId}, Type, }; +use self::errors::Source; + type TypeCheckFn = Box Result<(), TypeCheckError>>; pub struct TypeChecker<'interner> { @@ -57,16 +60,29 @@ pub fn type_check_func(interner: &mut NodeInterner, func_id: FuncId) -> Vec Vec (noirc_errors::Span, bool) { + let (expr_span, empty_function) = + if let HirExpression::Block(block) = interner.expression(function_body_id) { + let last_stmt = block.statements().last(); + let mut span = interner.expr_span(function_body_id); + + if let Some(last_stmt) = last_stmt { + if let HirStatement::Expression(expr) = interner.statement(last_stmt) { + span = interner.expr_span(&expr); + } + } + + (span, last_stmt.is_none()) + } else { + (interner.expr_span(function_body_id), false) + }; + (expr_span, empty_function) +} + impl<'interner> TypeChecker<'interner> { fn new(interner: &'interner mut NodeInterner) -> Self { Self { delayed_type_checks: Vec::new(), interner, errors: vec![] } @@ -137,7 +175,7 @@ mod test { use noirc_errors::{Location, Span}; use crate::graph::CrateId; - use crate::hir::def_map::{ModuleData, ModuleId, ModuleOrigin}; + use crate::hir::def_map::{ModuleData, ModuleId}; use crate::hir::resolution::import::PathResolutionError; use crate::hir_def::expr::HirIdent; use crate::hir_def::stmt::HirLetStatement; @@ -149,7 +187,6 @@ mod test { stmt::HirStatement, }; use crate::node_interner::{DefinitionKind, FuncId, NodeInterner}; - use crate::BinaryOpKind; use crate::{ hir::{ def_map::{CrateDefMap, LocalModuleId, ModuleDefId}, @@ -157,6 +194,7 @@ mod test { }, parse_program, FunctionKind, Path, }; + use crate::{BinaryOpKind, FunctionReturnType}; #[test] fn basic_let() { @@ -237,6 +275,7 @@ mod test { return_visibility: noirc_abi::AbiVisibility::Private, return_distinctness: noirc_abi::AbiDistinctness::DuplicationAllowed, has_body: true, + return_type: FunctionReturnType::Default(Span::default()), }; interner.push_fn_meta(func_meta, func_id); @@ -380,7 +419,8 @@ mod test { let file = FileId::default(); let mut modules = arena::Arena::new(); - modules.insert(ModuleData::new(None, ModuleOrigin::File(file), false)); + let location = Location::new(Default::default(), file); + modules.insert(ModuleData::new(None, location, false)); def_maps.insert( CrateId::dummy_id(), diff --git a/crates/noirc_frontend/src/hir/type_check/stmt.rs b/crates/noirc_frontend/src/hir/type_check/stmt.rs index b3a672cb05b..b279e454620 100644 --- a/crates/noirc_frontend/src/hir/type_check/stmt.rs +++ b/crates/noirc_frontend/src/hir/type_check/stmt.rs @@ -1,6 +1,6 @@ use noirc_errors::{Location, Span}; -use crate::hir_def::expr::HirIdent; +use crate::hir_def::expr::{HirExpression, HirIdent, HirLiteral}; use crate::hir_def::stmt::{ HirAssignStatement, HirConstrainStatement, HirLValue, HirLetStatement, HirPattern, HirStatement, }; @@ -259,9 +259,39 @@ impl<'interner> TypeChecker<'interner> { expr_span, } }); + if annotated_type.is_unsigned() { + self.lint_overflowing_uint(&rhs_expr, &annotated_type); + } annotated_type } else { expr_type } } + + /// Check if an assignment is overflowing with respect to `annotated_type` + /// in a declaration statement where `annotated_type` is an unsigned integer + fn lint_overflowing_uint(&mut self, rhs_expr: &ExprId, annotated_type: &Type) { + let expr = self.interner.expression(rhs_expr); + let span = self.interner.expr_span(rhs_expr); + match expr { + HirExpression::Literal(HirLiteral::Integer(value)) => { + let v = value.to_u128(); + if let Type::Integer(_, bit_count) = annotated_type { + let max = 1 << bit_count; + if v >= max { + self.errors.push(TypeCheckError::OverflowingAssignment { + expr: value, + ty: annotated_type.clone(), + range: format!("0..={}", max - 1), + span, + }); + }; + }; + } + HirExpression::Prefix(_) => self + .errors + .push(TypeCheckError::InvalidUnaryOp { kind: annotated_type.to_string(), span }), + _ => {} + } + } } diff --git a/crates/noirc_frontend/src/hir_def/function.rs b/crates/noirc_frontend/src/hir_def/function.rs index 225731626f0..3a4cb2a1036 100644 --- a/crates/noirc_frontend/src/hir_def/function.rs +++ b/crates/noirc_frontend/src/hir_def/function.rs @@ -7,7 +7,7 @@ use super::stmt::HirPattern; use crate::hir::def_map::ModuleId; use crate::node_interner::{ExprId, NodeInterner}; use crate::{token::Attribute, FunctionKind}; -use crate::{ContractFunctionType, Type}; +use crate::{ContractFunctionType, FunctionReturnType, Type}; /// A Hir function is a block expression /// with a list of statements @@ -137,6 +137,8 @@ pub struct FuncMeta { pub parameters: Parameters, + pub return_type: FunctionReturnType, + pub return_visibility: AbiVisibility, pub return_distinctness: AbiDistinctness, diff --git a/crates/noirc_frontend/src/hir_def/types.rs b/crates/noirc_frontend/src/hir_def/types.rs index d938421cb0e..b29b01e1ed2 100644 --- a/crates/noirc_frontend/src/hir_def/types.rs +++ b/crates/noirc_frontend/src/hir_def/types.rs @@ -447,6 +447,10 @@ impl Type { matches!(self.follow_bindings(), Type::Integer(Signedness::Signed, _)) } + pub fn is_unsigned(&self) -> bool { + matches!(self.follow_bindings(), Type::Integer(Signedness::Unsigned, _)) + } + fn contains_numeric_typevar(&self, target_id: TypeVariableId) -> bool { // True if the given type is a NamedGeneric with the target_id let named_generic_id_matches_target = |typ: &Type| { @@ -986,7 +990,7 @@ impl Type { let struct_type = def.borrow(); let fields = struct_type.get_fields(args); let fields = vecmap(fields, |(name, typ)| (name, typ.as_abi_type())); - AbiType::Struct { fields } + AbiType::Struct { fields, name: struct_type.name.to_string() } } Type::Tuple(_) => todo!("as_abi_type not yet implemented for tuple types"), Type::TypeVariable(_, _) => unreachable!(), diff --git a/crates/noirc_frontend/src/monomorphization/mod.rs b/crates/noirc_frontend/src/monomorphization/mod.rs index 783d9f3133e..998f3093d49 100644 --- a/crates/noirc_frontend/src/monomorphization/mod.rs +++ b/crates/noirc_frontend/src/monomorphization/mod.rs @@ -1303,13 +1303,12 @@ mod tests { use fm::FileId; use iter_extended::vecmap; + use noirc_errors::Location; use crate::{ graph::CrateId, hir::{ - def_map::{ - CrateDefMap, LocalModuleId, ModuleData, ModuleDefId, ModuleId, ModuleOrigin, - }, + def_map::{CrateDefMap, LocalModuleId, ModuleData, ModuleDefId, ModuleId}, resolution::{ import::PathResolutionError, path_resolver::PathResolver, resolver::Resolver, }, @@ -1349,7 +1348,8 @@ mod tests { let file = FileId::default(); let mut modules = arena::Arena::new(); - modules.insert(ModuleData::new(None, ModuleOrigin::File(file), false)); + let location = Location::new(Default::default(), file); + modules.insert(ModuleData::new(None, location, false)); def_maps.insert( CrateId::dummy_id(), diff --git a/crates/noirc_frontend/src/parser/parser.rs b/crates/noirc_frontend/src/parser/parser.rs index efa4e694cdc..6824446dbfe 100644 --- a/crates/noirc_frontend/src/parser/parser.rs +++ b/crates/noirc_frontend/src/parser/parser.rs @@ -23,6 +23,7 @@ //! prevent other parsers from being tried afterward since there is no longer an error. Thus, they should //! be limited to cases like the above `fn` example where it is clear we shouldn't back out of the //! current parser to try alternative parsers in a `choice` expression. +use super::spanned; use super::{ foldl_with_span, labels::ParsingRuleLabel, parameter_name_recovery, parameter_recovery, parenthesized, then_commit, then_commit_ignore, top_level_statement_recovery, ExprParser, @@ -34,10 +35,11 @@ use crate::lexer::Lexer; use crate::parser::{force, ignore_then_commit, statement_recovery}; use crate::token::{Attribute, Keyword, Token, TokenKind}; use crate::{ - BinaryOp, BinaryOpKind, BlockExpression, ConstrainStatement, FunctionDefinition, Ident, - IfExpression, InfixExpression, LValue, Lambda, Literal, NoirFunction, NoirStruct, NoirTrait, - NoirTypeAlias, Path, PathKind, Pattern, Recoverable, TraitConstraint, TraitImpl, TraitImplItem, - TraitItem, TypeImpl, UnaryOp, UnresolvedTypeExpression, UseTree, UseTreeKind, + BinaryOp, BinaryOpKind, BlockExpression, ConstrainStatement, FunctionDefinition, + FunctionReturnType, Ident, IfExpression, InfixExpression, LValue, Lambda, Literal, + NoirFunction, NoirStruct, NoirTrait, NoirTypeAlias, Path, PathKind, Pattern, Recoverable, + TraitConstraint, TraitImpl, TraitImplItem, TraitItem, TypeImpl, UnaryOp, + UnresolvedTypeExpression, UseTree, UseTreeKind, }; use chumsky::prelude::*; @@ -258,17 +260,19 @@ fn lambda_return_type() -> impl NoirParser { .map(|ret| ret.unwrap_or(UnresolvedType::Unspecified)) } -fn function_return_type() -> impl NoirParser<((AbiDistinctness, AbiVisibility), UnresolvedType)> { +fn function_return_type() -> impl NoirParser<((AbiDistinctness, AbiVisibility), FunctionReturnType)> +{ just(Token::Arrow) .ignore_then(optional_distinctness()) .then(optional_visibility()) - .then(parse_type()) + .then(spanned(parse_type())) .or_not() - .map(|ret| { - ret.unwrap_or(( + .map_with_span(|ret, span| match ret { + Some((head, (ty, span))) => (head, FunctionReturnType::Ty(ty, span)), + None => ( (AbiDistinctness::DuplicationAllowed, AbiVisibility::Private), - UnresolvedType::Unit, - )) + FunctionReturnType::Default(span), + ), }) } @@ -1468,16 +1472,33 @@ mod test { { vecmap(programs, move |program| { let message = format!("Failed to parse:\n{}", program); - let (op_t, errors) = parse_recover(&parser, program); - for e in errors { - if !e.is_warrning() { - panic!("{}", &message); + let (op_t, diagnostics) = parse_recover(&parser, program); + diagnostics.iter().for_each(|diagnostic| { + if diagnostic.is_error() { + panic!("{} with error {}", &message, diagnostic); } - } + }); op_t.expect(&message) }) } + fn parse_all_warnings(parser: P, programs: Vec<&str>) -> Vec + where + P: NoirParser, + T: std::fmt::Display, + { + programs + .into_iter() + .flat_map(|program| { + let (_expr, diagnostics) = parse_recover(&parser, program); + diagnostics.iter().for_each(|diagnostic| if diagnostic.is_error() { + unreachable!("Expected this input to pass with warning:\n{program}\nYet it failed with error:\n{diagnostic}") + }); + diagnostics + }) + .collect() + } + fn parse_all_failing(parser: P, programs: Vec<&str>) -> Vec where P: NoirParser, @@ -1486,11 +1507,21 @@ mod test { programs .into_iter() .flat_map(|program| match parse_with(&parser, program) { - Ok(expr) => unreachable!( - "Expected this input to fail:\n{}\nYet it successfully parsed as:\n{}", - program, expr - ), - Err(error) => error, + Ok(expr) => { + unreachable!( + "Expected this input to fail:\n{}\nYet it successfully parsed as:\n{}", + program, expr + ) + } + Err(diagnostics) => { + if diagnostics.iter().all(|diagnostic: &CustomDiagnostic| diagnostic.is_warning()) { + unreachable!( + "Expected at least one error when parsing:\n{}\nYet it successfully parsed without errors:\n", + program + ) + }; + diagnostics + } }) .collect() } @@ -1679,7 +1710,7 @@ mod test { // The first (inner) `==` is a predicate which returns 0/1 // The outer layer is an infix `==` which is // associated with the Constrain statement - let errors = parse_all_failing( + let errors = parse_all_warnings( constrain(expression()), vec![ "constrain ((x + y) == k) + z == y", @@ -1690,7 +1721,9 @@ mod test { ], ); assert_eq!(errors.len(), 5); - assert!(errors.iter().all(|err| { format!("{}", err).contains("deprecated") })); + assert!(errors + .iter() + .all(|err| { err.is_warning() && err.to_string().contains("deprecated") })); } /// This is the standard way to declare an assert statement @@ -1784,6 +1817,8 @@ mod test { "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 func_name(f: Field, y : T) where T: SomeTrait {}", + // The following should produce compile error on later stage. From the parser's perspective it's fine + "fn func_name(f: Field, y : Field, z : Field) where T: SomeTrait {}", ], ); @@ -1798,9 +1833,6 @@ mod test { "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) where SomeTrait {}", "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) SomeTrait {}", "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) where T: SomeTrait {}", - // TODO(GenericParameterNotFoundInFunction) - // Consider making this a compilation error: - // "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) where T: SomeTrait {}", ], ); } @@ -1820,6 +1852,9 @@ mod test { "trait TraitWithAssociatedType { type Element; fn item(self, index: Field) -> Self::Element; }", "trait TraitWithAssociatedConstant { let Size: Field; }", "trait TraitWithAssociatedConstantWithDefaultValue { let Size: Field = 10; }", + "trait GenericTrait { fn elem(&mut self, index: Field) -> T; }", + "trait GenericTraitWithConstraints where T: SomeTrait { fn elem(self, index: Field) -> T; }", + "trait TraitWithMultipleGenericParams where A: SomeTrait, B: AnotherTrait { let Size: Field; fn zero() -> Self; }", ], ); @@ -1829,11 +1864,6 @@ mod test { "trait MissingBody", "trait WrongDelimiter { fn foo() -> u8, fn bar() -> u8 }", "trait WhereClauseWithoutGenerics where A: SomeTrait { }", - // TODO: when implemnt generics in traits the following 3 should pass - "trait GenericTrait { fn elem(&mut self, index: Field) -> T; }", - "trait GenericTraitWithConstraints where T: SomeTrait { fn elem(self, index: Field) -> T; }", - "trait TraitWithMultipleGenericParams where A: SomeTrait, B: AnotherTrait { comptime Size: Field; fn zero() -> Self; }", - ], ); } diff --git a/crates/wasm/README.md b/crates/wasm/README.md index e2b625c739b..c5e8d54a836 100644 --- a/crates/wasm/README.md +++ b/crates/wasm/README.md @@ -2,7 +2,7 @@ This JavaScript package enables users to compile a Noir program, i.e. generating its artifacts. -The package also handles dependency management like how Nargo (Noir's CLI tool) opreates, but the package is used just for compilation, not proving, verifying and simulating functions. +The package also handles dependency management like how Nargo (Noir's CLI tool) operates, but the package is used just for compilation, not proving, verifying and simulating functions. ## Building from source diff --git a/crates/wasm/build.sh b/crates/wasm/build.sh index 22814b74936..54add2b8ed3 100755 --- a/crates/wasm/build.sh +++ b/crates/wasm/build.sh @@ -30,7 +30,7 @@ require_command wasm-opt self_path=$(dirname "$(readlink -f "$0")") export pname=$(toml2json < ${self_path}/Cargo.toml | jq -r .package.name) -export CARGO_TARGET_DIR=$self_path/target +export CARGO_TARGET_DIR=$self_path/../../target rm -rf $self_path/outputs >/dev/null 2>&1 rm -rf $self_path/result >/dev/null 2>&1 @@ -42,7 +42,7 @@ else echo "Will install package to $out" fi -run_or_fail ${self_path}/buildPhaseCargoCommand.sh +run_or_fail ${self_path}/buildPhaseCargoCommand.sh release run_or_fail ${self_path}/installPhase.sh ln -s $out $self_path/result diff --git a/crates/wasm/buildPhaseCargoCommand.sh b/crates/wasm/buildPhaseCargoCommand.sh index 5fff9eff808..f7828d7cd35 100755 --- a/crates/wasm/buildPhaseCargoCommand.sh +++ b/crates/wasm/buildPhaseCargoCommand.sh @@ -23,9 +23,26 @@ if [ -d ${self_path}/pkg/ ]; then rm -rf ${self_path}/pkg/ fi -# TODO: Handle the wasm target being built in release mode +# Check that the user passed in debug or release mode +# and set the BUILD_FLAG and BUILD_MODE appropriately. +if [[ -z "$1" ]]; then + echo "Build script requires either "debug" or "release" as an argument." + exit 1 +fi + +BUILD_MODE=$1 +BUILD_FLAG="" # Defaults to debug mode which is an empty string + +if [[ "$1" == "release" ]]; then + BUILD_MODE=release + BUILD_FLAG="--release" +elif [[ "$1" != "debug" ]]; then + echo "Invalid BUILD_MODE. Accepted values are 'debug' or 'release'." + exit 1 +fi + TARGET=wasm32-unknown-unknown -WASM_BINARY=${self_path}/../../target/${TARGET}/release/${pname}.wasm +WASM_BINARY=${CARGO_TARGET_DIR}/${TARGET}/${BUILD_MODE}/${pname}.wasm NODE_DIR=${self_path}/pkg/nodejs/ BROWSER_DIR=${self_path}/pkg/web/ @@ -33,7 +50,7 @@ NODE_WASM=${NODE_DIR}/${pname}_bg.wasm BROWSER_WASM=${BROWSER_DIR}/${pname}_bg.wasm # Build the new wasm package -run_or_fail cargo build --lib --release --package noir_wasm --target wasm32-unknown-unknown +run_or_fail cargo build --lib $BUILD_FLAG --package noir_wasm --target wasm32-unknown-unknown 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 diff --git a/crates/wasm/package.json b/crates/wasm/package.json index a44e0c1a3f4..73b0284e89c 100644 --- a/crates/wasm/package.json +++ b/crates/wasm/package.json @@ -3,7 +3,7 @@ "collaborators": [ "The Noir Team " ], - "version": "0.9.0", + "version": "0.10.1", "license": "(MIT OR Apache-2.0)", "main": "./nodejs/noir_wasm.js", "types": "./web/noir_wasm.d.ts", diff --git a/crates/wasm/test/browser/index.test.ts b/crates/wasm/test/browser/index.test.ts index 6c86f0d3f94..9cc49069bfd 100644 --- a/crates/wasm/test/browser/index.test.ts +++ b/crates/wasm/test/browser/index.test.ts @@ -1,5 +1,5 @@ import { expect } from "@esm-bundle/chai"; -import initNoirWasm from "../../dist"; +import initNoirWasm from "../../result"; import { compileNoirSource, nargoArtifactPath, noirSourcePath } from "../shared"; beforeEach(async () => { diff --git a/crates/wasm/test/shared.ts b/crates/wasm/test/shared.ts index c21ce2e0de4..ee9c57d24f6 100644 --- a/crates/wasm/test/shared.ts +++ b/crates/wasm/test/shared.ts @@ -1,5 +1,5 @@ import { initialiseResolver } from "@noir-lang/noir-source-resolver"; -import { compile } from "../dist/"; +import { compile } from "../result/"; export const noirSourcePath = "../../noir-script/src/main.nr"; export const nargoArtifactPath = "../../noir-script/target/noir_wasm_testing.json"; diff --git a/cspell.json b/cspell.json index 8da1d65fb56..9953b6f0cd0 100644 --- a/cspell.json +++ b/cspell.json @@ -21,7 +21,9 @@ "defunctionalized", "defunctionalization", "desugared", + "eddsa", "endianness", + "Flamegraph", "forall", "foralls", "Guillaume", @@ -49,7 +51,10 @@ "nand", "pedersen", "peekable", + "pprof", "preprocess", + "prettytable", + "printstd", "pseudocode", "schnorr", "sdiv", diff --git a/flake.nix b/flake.nix index ea677501147..9edae168a7f 100644 --- a/flake.nix +++ b/flake.nix @@ -88,7 +88,7 @@ BARRETENBERG_BIN_DIR = "${pkgs.barretenberg-wasm}/bin"; }; - testEnvironment = sharedEnvironment // {}; + testEnvironment = sharedEnvironment // { }; # The `self.rev` property is only available when the working tree is not dirty GIT_COMMIT = if (self ? rev) then self.rev else "unknown"; @@ -117,7 +117,7 @@ sharedArgs = { # x-release-please-start-version - version = "0.9.0"; + version = "0.10.1"; # x-release-please-end src = pkgs.lib.cleanSourceWith { @@ -295,6 +295,7 @@ COMMIT_SHORT = builtins.substring 0 7 GIT_COMMIT; VERSION_APPENDIX = if GIT_DIRTY == "true" then "-dirty" else ""; PKG_PATH = "./pkg"; + CARGO_TARGET_DIR = "./target"; nativeBuildInputs = with pkgs; [ which @@ -307,7 +308,7 @@ ]; buildPhaseCargoCommand = '' - bash crates/wasm/buildPhaseCargoCommand.sh + bash crates/wasm/buildPhaseCargoCommand.sh release ''; installPhase = '' diff --git a/noir_stdlib/Nargo.toml b/noir_stdlib/Nargo.toml index 29392dcc38a..9b7a79ad2a6 100644 --- a/noir_stdlib/Nargo.toml +++ b/noir_stdlib/Nargo.toml @@ -1,5 +1,6 @@ [package] name = "std" +type = "lib" authors = [""] compiler_version = "0.1" diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index a6a719ec9b9..684b2636744 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -15,6 +15,7 @@ mod unsafe; mod collections; mod compat; mod option; +mod string; // Oracle calls are required to be wrapped in an unconstrained function // Thus, the only argument to the `println` oracle is expected to always be an ident @@ -30,3 +31,8 @@ fn verify_proof(_verification_key : [Field], _proof : [Field], _public_inputs #[builtin(assert_eq)] fn assert_eq(_lhs: T, _rhs: T) {} + +// Asserts that the given value is known at compile-time. +// Useful for debugging for-loop bounds. +#[builtin(assert_constant)] +fn assert_constant(_x: T) {} diff --git a/noir_stdlib/src/string.nr b/noir_stdlib/src/string.nr new file mode 100644 index 00000000000..e24bb567681 --- /dev/null +++ b/noir_stdlib/src/string.nr @@ -0,0 +1,11 @@ +use crate::collections::vec::Vec; +impl str { + /// Converts the given string into a byte array + #[builtin(str_as_bytes)] + fn as_bytes(_self: Self) -> [u8; N] { } + + /// return a byte vector of the str content + fn as_bytes_vec(self: Self) -> Vec { + Vec::from_slice(self.as_bytes().as_slice()) + } +} \ No newline at end of file diff --git a/release-please-config.json b/release-please-config.json index 1dbec7e5d26..a84fc0de82c 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -20,5 +20,8 @@ } ] } - } + }, + "plugins": [ + "sentence-case" + ] }