diff --git a/Cargo.lock b/Cargo.lock index f7d2e63662f..6b81b673b42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -129,15 +129,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - [[package]] name = "aho-corasick" version = "1.0.2" @@ -337,9 +328,9 @@ dependencies = [ [[package]] name = "assert_cmd" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d6b683edf8d1119fe420a94f8a7e389239666aa72e65495d91c00462510151" +checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" dependencies = [ "anstyle", "bstr", @@ -393,7 +384,7 @@ checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.26", ] [[package]] @@ -677,9 +668,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.11" +version = "4.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" +checksum = "74bb1b4028935821b2d6b439bba2e970bdcf740832732437ead910c632e30d7d" dependencies = [ "clap_builder", "clap_derive", @@ -688,9 +679,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.11" +version = "4.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" +checksum = "5ae467cbb0111869b765e13882a1dbbd6cb52f58203d8b80c44f667d4dd19843" dependencies = [ "anstream", "anstyle", @@ -700,14 +691,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.3.2" +version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.26", ] [[package]] @@ -793,9 +784,9 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6340df57935414636969091153f35f68d9f00bbc8fb4a9c6054706c213e6c6bc" +checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747" [[package]] name = "const_format" @@ -980,9 +971,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.1" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ "darling_core", "darling_macro", @@ -990,26 +981,26 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.1" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.26", ] [[package]] name = "darling_macro" -version = "0.20.1" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.25", + "syn 2.0.26", ] [[package]] @@ -1161,14 +1152,14 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.26", ] [[package]] name = "equivalent" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" @@ -1328,7 +1319,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.26", ] [[package]] @@ -1418,11 +1409,11 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +checksum = "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df" dependencies = [ - "aho-corasick 0.7.20", + "aho-corasick", "bstr", "fnv", "log", @@ -1442,9 +1433,9 @@ dependencies = [ [[package]] name = "gloo-utils" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8e8fc851e9c7b9852508bc6e3f690f452f474417e8545ec9857b7f7377036b5" +checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" dependencies = [ "js-sys", "serde", @@ -1745,7 +1736,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", - "rustix 0.38.3", + "rustix 0.38.4", "windows-sys 0.48.0", ] @@ -1764,9 +1755,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" @@ -1998,6 +1989,7 @@ dependencies = [ "noirc_errors", "rustc_version", "serde", + "serde_json", "thiserror", "toml", ] @@ -2146,6 +2138,7 @@ dependencies = [ "noirc_errors", "rustc-hash", "serde", + "serde_json", "small-ord-set", "smol_str", "strum", @@ -2255,9 +2248,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b27ab7be369122c218afc2079489cdcb4b517c0a3fc386ff11e1fedfcc2b35" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "peeking_take_while" @@ -2373,9 +2366,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.64" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -2411,9 +2404,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.29" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" dependencies = [ "proc-macro2", ] @@ -2568,7 +2561,7 @@ version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ - "aho-corasick 1.0.2", + "aho-corasick", "memchr", "regex-automata", "regex-syntax", @@ -2576,20 +2569,20 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" dependencies = [ - "aho-corasick 1.0.2", + "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "region" @@ -2757,7 +2750,7 @@ dependencies = [ "quote", "rust-embed-utils", "shellexpand", - "syn 2.0.25", + "syn 2.0.26", "walkdir", ] @@ -2809,9 +2802,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.3" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" dependencies = [ "bitflags 2.3.3", "errno", @@ -2822,9 +2815,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.3" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b19faa85ecb5197342b54f987b142fb3e30d0c90da40f80ef4fa9a726e6676ed" +checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36" dependencies = [ "log", "ring", @@ -2853,15 +2846,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc31bd9b61a32c31f9650d18add92aa83a49ba979c143eefd27fe7177b05bd5f" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "safe-lock" @@ -2927,9 +2920,9 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" @@ -2963,9 +2956,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" @@ -2987,9 +2980,9 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.11" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a16be4fe5320ade08736447e3198294a5ea9a6d44dde6f35f0a5e06859c427a" +checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" dependencies = [ "serde", ] @@ -3002,14 +2995,14 @@ checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.26", ] [[package]] name = "serde_json" -version = "1.0.100" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" +checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" dependencies = [ "itoa", "ryu", @@ -3024,7 +3017,7 @@ checksum = "1d89a8107374290037607734c0b73a85db7ed80cae314b3c5791f192a496e731" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.26", ] [[package]] @@ -3241,9 +3234,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.25" +version = "2.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970" dependencies = [ "proc-macro2", "quote", @@ -3258,9 +3251,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.8" +version = "0.12.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1c7f239eb94671427157bd93b3694320f3668d4e1eff08c7285366fd777fac" +checksum = "1d2faeef5759ab89935255b1a4cd98e0baf99d1085e37d36599c625dac49ae8e" [[package]] name = "tempdir" @@ -3318,7 +3311,7 @@ checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.26", ] [[package]] @@ -3382,7 +3375,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.26", ] [[package]] @@ -3432,9 +3425,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.12" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ "indexmap 2.0.0", "serde", @@ -3487,7 +3480,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.26", ] [[package]] @@ -3541,9 +3534,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -3592,9 +3585,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" [[package]] name = "valuable" @@ -3671,7 +3664,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.26", "wasm-bindgen-shared", ] @@ -3705,7 +3698,7 @@ checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.26", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3718,9 +3711,9 @@ checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" [[package]] name = "wasm-encoder" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18c41dbd92eaebf3612a39be316540b8377c871cb9bde6b064af962984912881" +checksum = "06a3d1b4a575ffb873679402b2aedb3117555eb65c27b1b86c8a91e574bc2a2a" dependencies = [ "leb128", ] @@ -3975,9 +3968,9 @@ checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" [[package]] name = "wast" -version = "60.0.0" +version = "62.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd06cc744b536e30387e72a48fdd492105b9c938bb4f415c39c616a7a0a697ad" +checksum = "c7f7ee878019d69436895f019b65f62c33da63595d8e857cbdc87c13ecb29a32" dependencies = [ "leb128", "memchr", @@ -3987,9 +3980,9 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.66" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5abe520f0ab205366e9ac7d3e6b2fc71de44e32a2b58f2ec871b6b575bdcea3b" +checksum = "295572bf24aa5b685a971a83ad3e8b6e684aaad8a9be24bc7bf59bed84cc1c08" dependencies = [ "wast", ] @@ -4185,9 +4178,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.9" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81a2094c43cc94775293eaa0e499fbc30048a6d824ac82c0351a8c0bf9112529" +checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" dependencies = [ "memchr", ] @@ -4227,5 +4220,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.26", ] diff --git a/Cargo.toml b/Cargo.toml index 2dde6ff67c4..e272989b2aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,6 @@ noirc_errors = { path = "crates/noirc_errors" } noirc_evaluator = { path = "crates/noirc_evaluator" } noirc_frontend = { path = "crates/noirc_frontend" } noir_wasm = { path = "crates/wasm" } - cfg-if = "1.0.0" clap = { version = "4.1.4", features = ["derive"] } codespan = { version = "0.11.1", features = ["serialization"] } @@ -58,4 +57,4 @@ wasm-bindgen-test = "0.3.33" base64 = "0.21.2" [patch.crates-io] -async-lsp = { git = "https://github.com/oxalica/async-lsp", rev = "09dbcc11046f7a188a80137f8d36484d86c78c78" } +async-lsp = { git = "https://github.com/oxalica/async-lsp", rev = "09dbcc11046f7a188a80137f8d36484d86c78c78" } \ No newline at end of file diff --git a/crates/nargo/Cargo.toml b/crates/nargo/Cargo.toml index dce7b098aaa..6c053cba931 100644 --- a/crates/nargo/Cargo.toml +++ b/crates/nargo/Cargo.toml @@ -17,6 +17,7 @@ noirc_driver.workspace = true iter-extended.workspace = true toml.workspace = true serde.workspace = true +serde_json.workspace = true thiserror.workspace = true noirc_errors.workspace = true base64.workspace = true \ No newline at end of file diff --git a/crates/nargo/src/errors.rs b/crates/nargo/src/errors.rs index e9561aa15a9..2878d9f2db7 100644 --- a/crates/nargo/src/errors.rs +++ b/crates/nargo/src/errors.rs @@ -1,4 +1,5 @@ use acvm::pwg::OpcodeResolutionError; +use noirc_abi::errors::{AbiError, InputParserError}; use thiserror::Error; #[derive(Debug, Error)] @@ -10,4 +11,21 @@ pub enum NargoError { /// ACIR circuit solving error #[error(transparent)] SolvingError(#[from] OpcodeResolutionError), + + #[error(transparent)] + ForeignCallError(#[from] ForeignCallError), +} + +#[derive(Debug, Error)] +pub enum ForeignCallError { + #[error("Foreign call inputs needed for execution are missing")] + MissingForeignCallInputs, + + /// ABI encoding/decoding error + #[error(transparent)] + AbiError(#[from] AbiError), + + /// Input parsing error + #[error(transparent)] + InputParserError(#[from] InputParserError), } diff --git a/crates/nargo/src/ops/execute.rs b/crates/nargo/src/ops/execute.rs index f5b2cd7a3b6..dd68c30af4c 100644 --- a/crates/nargo/src/ops/execute.rs +++ b/crates/nargo/src/ops/execute.rs @@ -1,11 +1,11 @@ -use acvm::acir::brillig::{ForeignCallResult, Value}; -use acvm::pwg::{ACVMStatus, ForeignCallWaitInfo, ACVM}; +use acvm::pwg::{ACVMStatus, ACVM}; use acvm::BlackBoxFunctionSolver; use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; -use iter_extended::vecmap; use crate::NargoError; +use super::foreign_calls::ForeignCall; + pub fn execute_circuit( _backend: &B, circuit: Circuit, @@ -24,7 +24,7 @@ pub fn execute_circuit( ACVMStatus::Failure(error) => return Err(error.into()), ACVMStatus::RequiresForeignCall => { while let Some(foreign_call) = acvm.get_pending_foreign_call() { - let foreign_call_result = execute_foreign_call(foreign_call); + let foreign_call_result = ForeignCall::execute(foreign_call)?; acvm.resolve_pending_foreign_call(foreign_call_result); } } @@ -34,41 +34,3 @@ pub fn execute_circuit( let solved_witness = acvm.finalize(); Ok(solved_witness) } - -fn execute_foreign_call(foreign_call: &ForeignCallWaitInfo) -> ForeignCallResult { - // TODO(#1615): Nargo only supports "oracle_print_**_impl" functions that print a singular value or an array and nothing else - // This should be expanded in a general logging refactor - match foreign_call.function.as_str() { - // TODO(#1910): Move to an enum and don't match directly on these strings - "oracle_print_impl" => { - let values = &foreign_call.inputs[0]; - println!("{:?}", values[0].to_field().to_hex()); - values[0].into() - } - "oracle_print_array_impl" => { - let mut outputs_hex = Vec::new(); - for values in &foreign_call.inputs { - for value in values { - outputs_hex.push(value.to_field().to_hex()); - } - } - // Join all of the hex strings using a comma - let comma_separated_elements = outputs_hex.join(", "); - let output_witnesses_string = "[".to_owned() + &comma_separated_elements + "]"; - println!("{output_witnesses_string}"); - - foreign_call.inputs[0][0].into() - } - "get_number_sequence" => { - let sequence_length: u128 = foreign_call.inputs[0][0].to_field().to_u128(); - - vecmap(0..sequence_length, Value::from).into() - } - "get_reverse_number_sequence" => { - let sequence_length: u128 = foreign_call.inputs[0][0].to_field().to_u128(); - - vecmap((0..sequence_length).rev(), Value::from).into() - } - _ => panic!("unexpected foreign call type"), - } -} diff --git a/crates/nargo/src/ops/foreign_calls.rs b/crates/nargo/src/ops/foreign_calls.rs new file mode 100644 index 00000000000..ea7f9be21b4 --- /dev/null +++ b/crates/nargo/src/ops/foreign_calls.rs @@ -0,0 +1,93 @@ +use acvm::{ + acir::brillig::{ForeignCallResult, Value}, + pwg::ForeignCallWaitInfo, +}; +use iter_extended::vecmap; +use noirc_abi::{decode_string_value, decode_value, input_parser::json::JsonTypes, AbiType}; + +use crate::errors::ForeignCallError; + +/// This enumeration represents the Brillig foreign calls that are natively supported by nargo. +/// After resolution of a foreign call, nargo will restart execution of the ACVM +pub(crate) enum ForeignCall { + Println, + Sequence, + ReverseSequence, +} + +impl std::fmt::Display for ForeignCall { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name()) + } +} + +impl ForeignCall { + pub(crate) fn name(&self) -> &'static str { + match self { + ForeignCall::Println => "println", + ForeignCall::Sequence => "get_number_sequence", + ForeignCall::ReverseSequence => "get_reverse_number_sequence", + } + } + + pub(crate) fn lookup(op_name: &str) -> Option { + match op_name { + "println" => Some(ForeignCall::Println), + "get_number_sequence" => Some(ForeignCall::Sequence), + "get_reverse_number_sequence" => Some(ForeignCall::ReverseSequence), + _ => None, + } + } + + pub(crate) fn execute( + foreign_call: &ForeignCallWaitInfo, + ) -> Result { + let foreign_call_name = foreign_call.function.as_str(); + match Self::lookup(foreign_call_name) { + Some(ForeignCall::Println) => { + Self::execute_println(&foreign_call.inputs)?; + Ok(ForeignCallResult { values: vec![] }) + } + Some(ForeignCall::Sequence) => { + let sequence_length: u128 = foreign_call.inputs[0][0].to_field().to_u128(); + + Ok(vecmap(0..sequence_length, Value::from).into()) + } + Some(ForeignCall::ReverseSequence) => { + let sequence_length: u128 = foreign_call.inputs[0][0].to_field().to_u128(); + + Ok(vecmap((0..sequence_length).rev(), Value::from).into()) + } + None => panic!("unexpected foreign call {:?}", foreign_call_name), + } + } + + fn execute_println(foreign_call_inputs: &[Vec]) -> Result<(), ForeignCallError> { + let (abi_type, input_values) = fetch_abi_type(foreign_call_inputs)?; + + // We must use a flat map here as each value in a struct will be in a separate input value + let mut input_values_as_fields = + input_values.iter().flat_map(|values| values.iter().map(|value| value.to_field())); + let decoded_value = decode_value(&mut input_values_as_fields, &abi_type)?; + + let json_value = JsonTypes::try_from_input_value(&decoded_value, &abi_type)?; + + println!("{json_value}"); + Ok(()) + } +} + +/// Fetch the abi type from the foreign call input +/// The remaining input values should hold the values to be printed +fn fetch_abi_type( + foreign_call_inputs: &[Vec], +) -> Result<(AbiType, &[Vec]), ForeignCallError> { + let (abi_type_as_values, input_values) = + foreign_call_inputs.split_last().ok_or(ForeignCallError::MissingForeignCallInputs)?; + let abi_type_as_fields = vecmap(abi_type_as_values, |value| value.to_field()); + let abi_type_as_string = decode_string_value(&abi_type_as_fields); + let abi_type: AbiType = serde_json::from_str(&abi_type_as_string) + .map_err(|err| ForeignCallError::InputParserError(err.into()))?; + + Ok((abi_type, input_values)) +} diff --git a/crates/nargo/src/ops/mod.rs b/crates/nargo/src/ops/mod.rs index 8a0cce4b8c5..6d55e5b0dad 100644 --- a/crates/nargo/src/ops/mod.rs +++ b/crates/nargo/src/ops/mod.rs @@ -6,6 +6,7 @@ pub use self::verify::verify_proof; mod codegen_verifier; mod execute; +mod foreign_calls; mod preprocess; mod prove; mod verify; diff --git a/crates/nargo_cli/src/cli/check_cmd.rs b/crates/nargo_cli/src/cli/check_cmd.rs index 9d071731207..61a4f79593c 100644 --- a/crates/nargo_cli/src/cli/check_cmd.rs +++ b/crates/nargo_cli/src/cli/check_cmd.rs @@ -220,9 +220,9 @@ pub(crate) fn check_crate_and_report_errors( context: &mut Context, crate_id: CrateId, deny_warnings: bool, - enable_slices: bool, + experimental_ssa: bool, ) -> Result<(), ReportedErrors> { - let result = - check_crate(context, crate_id, deny_warnings, enable_slices).map(|warnings| ((), warnings)); + let result = check_crate(context, crate_id, deny_warnings, experimental_ssa) + .map(|warnings| ((), warnings)); super::compile_cmd::report_errors(result, context, deny_warnings) } diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/brillig_oracle/src/main.nr b/crates/nargo_cli/tests/test_data_ssa_refactor/brillig_oracle/src/main.nr index 53356c80ab9..84dcb1a0915 100644 --- a/crates/nargo_cli/tests/test_data_ssa_refactor/brillig_oracle/src/main.nr +++ b/crates/nargo_cli/tests/test_data_ssa_refactor/brillig_oracle/src/main.nr @@ -2,31 +2,9 @@ use dep::std::slice; // Tests oracle usage in brillig/unconstrained functions fn main(x: Field) { - // call through a brillig wrapper - oracle_print_array_wrapper([x, x]); - - // TODO(#1615) Nargo currently only supports resolving one foreign call - // oracle_print_wrapper(x); - get_number_sequence_wrapper(20); } -// TODO(#1911) -#[oracle(oracle_print_impl)] -unconstrained fn oracle_print(_x : Field) -> Field {} - -unconstrained fn oracle_print_wrapper(x: Field) { - oracle_print(x); -} - -// TODO(#1911) -#[oracle(oracle_print_array_impl)] -unconstrained fn oracle_print_array(_arr : [Field; 2]) -> Field {} - -unconstrained fn oracle_print_array_wrapper(arr: [Field; 2]) { - oracle_print_array(arr); -} - // TODO(#1911): This function does not need to be an oracle but acts // as a useful test while we finalize code generation for slices in Brillig #[oracle(get_number_sequence)] diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/debug_logs/Nargo.toml b/crates/nargo_cli/tests/test_data_ssa_refactor/debug_logs/Nargo.toml new file mode 100644 index 00000000000..dc0c2f8917c --- /dev/null +++ b/crates/nargo_cli/tests/test_data_ssa_refactor/debug_logs/Nargo.toml @@ -0,0 +1,5 @@ +[package] +authors = [""] +compiler_version = "0.8.0" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/debug_logs/Prover.toml b/crates/nargo_cli/tests/test_data_ssa_refactor/debug_logs/Prover.toml new file mode 100644 index 00000000000..f28f2f8cc48 --- /dev/null +++ b/crates/nargo_cli/tests/test_data_ssa_refactor/debug_logs/Prover.toml @@ -0,0 +1,2 @@ +x = "5" +y = "10" diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/debug_logs/src/main.nr b/crates/nargo_cli/tests/test_data_ssa_refactor/debug_logs/src/main.nr new file mode 100644 index 00000000000..29386feb98c --- /dev/null +++ b/crates/nargo_cli/tests/test_data_ssa_refactor/debug_logs/src/main.nr @@ -0,0 +1,22 @@ +use dep::std; + +fn main(x : Field, y : pub Field) { + + std::println("*** println ***"); + std::println(x); + std::println([x, y]); + + let s = myStruct { y: x, x: y }; + let foo = fooStruct { my_struct: s, foo: 15 }; + std::println(foo); +} + +struct myStruct { + y: Field, + x: Field, +} + +struct fooStruct { + my_struct: myStruct, + foo: Field, +} diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/regression/Nargo.toml b/crates/nargo_cli/tests/test_data_ssa_refactor/regression/Nargo.toml new file mode 100644 index 00000000000..e0b467ce5da --- /dev/null +++ b/crates/nargo_cli/tests/test_data_ssa_refactor/regression/Nargo.toml @@ -0,0 +1,5 @@ +[package] +authors = [""] +compiler_version = "0.1" + +[dependencies] \ No newline at end of file diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/regression/Prover.toml b/crates/nargo_cli/tests/test_data_ssa_refactor/regression/Prover.toml new file mode 100644 index 00000000000..2875190982f --- /dev/null +++ b/crates/nargo_cli/tests/test_data_ssa_refactor/regression/Prover.toml @@ -0,0 +1,2 @@ +x = [0x3f, 0x1c, 0xb8, 0x99, 0xab] +z = 3 diff --git a/crates/nargo_cli/tests/test_data_ssa_refactor/regression/src/main.nr b/crates/nargo_cli/tests/test_data_ssa_refactor/regression/src/main.nr new file mode 100644 index 00000000000..e01888dea37 --- /dev/null +++ b/crates/nargo_cli/tests/test_data_ssa_refactor/regression/src/main.nr @@ -0,0 +1,90 @@ +global NIBBLE_LENGTH: comptime Field = 16; + +fn compact_decode(input: [u8; N], length: Field) -> ([u4; NIBBLE_LENGTH], Field) +{ + assert(2*input.len() as u64 <= NIBBLE_LENGTH as u64); + assert(length as u64 <= input.len() as u64); + + let mut nibble = [0 as u4; NIBBLE_LENGTH]; + + let first_nibble = (input[0] >> 4) as u4; + let parity = first_nibble as u1; + + if parity == 1 + { + nibble[0] = (input[0] & 0x0f) as u4; + for i in 1..input.len() + { + if i as u64 < length as u64 + { + let x = input[i]; + nibble[2*i - 1] = (x >> 4) as u4; + nibble[2*i] = (x & 0x0f) as u4; + } + } + } + else + { + for i in 0..2 + { + if (i as u64) < length as u64 - 1 + { + let x = input[i + 1]; + nibble[2*i] = (x >> 4) as u4; + nibble[2*i + 1] = (x & 0x0f) as u4; + } + } + } + + let out = (nibble, 2*length + (parity as Field) - 2); + + out +} + +fn enc(value: [u8; N], value_length: Field) -> ([u8; 32], Field) +{ + assert(value.len() as u8 >= value_length as u8); + let mut out_value = [0; 32]; + if value_length == 0 + { + let out = (out_value, value_length); + out + } + else { if value_length as u8 < 31 + { + out_value[0] = 0x80 + value_length as u8; + + for i in 1..value.len() + { + out_value[i] = value[i-1]; + } + + let out = (out_value, value_length + 1); + + out + } + else + { + let out = (out_value, 32); + out + } + } +} + +fn main(x: [u8; 5], z: Field) +{ + //Issue 1144 + let (nib, len) = compact_decode(x,z); + assert(len == 5); + assert([nib[0], nib[1], nib[2], nib[3], nib[4]] == [15, 1, 12, 11, 8]); + + // Issue 1169 + let val1 = [0xb8,0x8f,0x61,0xe6,0xfb,0xda,0x83,0xfb,0xff,0xfa,0xbe,0x36,0x41,0x12,0x13,0x74,0x80,0x39,0x80,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]; + let val1_length = 20; + + let enc_val1 = enc(val1,val1_length); + + assert(enc_val1.0 == [0x94,0xb8,0x8f,0x61,0xe6,0xfb,0xda,0x83,0xfb,0xff,0xfa,0xbe,0x36,0x41,0x12,0x13,0x74,0x80,0x39,0x80,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]); + assert(enc_val1.1 == 21); + +} \ No newline at end of file diff --git a/crates/noirc_abi/src/input_parser/json.rs b/crates/noirc_abi/src/input_parser/json.rs index 748c0d09e04..7a0cd76698d 100644 --- a/crates/noirc_abi/src/input_parser/json.rs +++ b/crates/noirc_abi/src/input_parser/json.rs @@ -59,7 +59,7 @@ pub(crate) fn serialize_to_json( #[derive(Debug, Deserialize, Serialize, Clone)] #[serde(untagged)] -enum JsonTypes { +pub enum JsonTypes { // This is most likely going to be a hex string // But it is possible to support UTF-8 String(String), @@ -78,14 +78,13 @@ enum JsonTypes { } impl JsonTypes { - fn try_from_input_value( + pub fn try_from_input_value( value: &InputValue, abi_type: &AbiType, ) -> Result { let json_value = match (value, abi_type) { (InputValue::Field(f), AbiType::Field | AbiType::Integer { .. }) => { - let f_str = format!("0x{}", f.to_hex()); - JsonTypes::String(f_str) + JsonTypes::String(Self::format_field_string(*f)) } (InputValue::Field(f), AbiType::Boolean) => JsonTypes::Bool(f.is_one()), @@ -109,6 +108,21 @@ impl JsonTypes { }; Ok(json_value) } + + /// This trims any leading zeroes. + /// A singular '0' will be prepended as well if the trimmed string has an odd length. + /// A hex string's length needs to be even to decode into bytes, as two digits correspond to + /// one byte. + fn format_field_string(field: FieldElement) -> String { + if field.is_zero() { + return "0x00".to_owned(); + } + let mut trimmed_field = field.to_hex().trim_start_matches('0').to_owned(); + if trimmed_field.len() % 2 != 0 { + trimmed_field = "0".to_owned() + &trimmed_field; + } + "0x".to_owned() + &trimmed_field + } } impl InputValue { @@ -161,3 +175,13 @@ impl InputValue { Ok(input_value) } } + +impl std::fmt::Display for JsonTypes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + // From the docs: https://doc.rust-lang.org/std/fmt/struct.Error.html + // This type does not support transmission of an error other than that an error + // occurred. Any extra information must be arranged to be transmitted through + // some other means. + write!(f, "{}", serde_json::to_string(&self).map_err(|_| std::fmt::Error)?) + } +} diff --git a/crates/noirc_abi/src/input_parser/mod.rs b/crates/noirc_abi/src/input_parser/mod.rs index c5b8a2fff09..6818f40786c 100644 --- a/crates/noirc_abi/src/input_parser/mod.rs +++ b/crates/noirc_abi/src/input_parser/mod.rs @@ -1,4 +1,4 @@ -mod json; +pub mod json; mod toml; use std::collections::BTreeMap; diff --git a/crates/noirc_abi/src/lib.rs b/crates/noirc_abi/src/lib.rs index 856889b3860..86f9edc73bd 100644 --- a/crates/noirc_abi/src/lib.rs +++ b/crates/noirc_abi/src/lib.rs @@ -117,15 +117,6 @@ pub enum Sign { } impl AbiType { - pub fn num_elements(&self) -> usize { - match self { - AbiType::Field | AbiType::Integer { .. } | AbiType::Boolean => 1, - AbiType::Array { length, typ: _ } => *length as usize, - AbiType::Struct { fields, .. } => fields.len(), - AbiType::String { length } => *length as usize, - } - } - /// Returns the number of field elements required to represent the type once encoded. pub fn field_count(&self) -> u32 { match self { @@ -345,7 +336,7 @@ impl Abi { .copied() })?; - Self::decode_value(&mut param_witness_values.into_iter(), &typ) + decode_value(&mut param_witness_values.into_iter(), &typ) .map(|input_value| (name.clone(), input_value)) })?; @@ -362,7 +353,7 @@ impl Abi { .copied() }) { - Some(Self::decode_value(&mut return_witness_values.into_iter(), return_type)?) + Some(decode_value(&mut return_witness_values.into_iter(), return_type)?) } else { // Unlike for the circuit inputs, we tolerate not being able to find the witness values for the return value. // This is because the user may be decoding a partial witness map for which is hasn't been calculated yet. @@ -375,49 +366,48 @@ impl Abi { Ok((public_inputs_map, return_value)) } +} - fn decode_value( - field_iterator: &mut impl Iterator, - value_type: &AbiType, - ) -> Result { - // This function assumes that `field_iterator` contains enough `FieldElement`s in order to decode a `value_type` - // `Abi.decode` enforces that the encoded inputs matches the expected length defined by the ABI so this is safe. - let value = match value_type { - AbiType::Field | AbiType::Integer { .. } | AbiType::Boolean => { - let field_element = field_iterator.next().unwrap(); - - InputValue::Field(field_element) - } - AbiType::Array { length, typ } => { - let length = *length as usize; - let mut array_elements = Vec::with_capacity(length); - for _ in 0..length { - array_elements.push(Self::decode_value(field_iterator, typ)?); - } - - InputValue::Vec(array_elements) +pub fn decode_value( + field_iterator: &mut impl Iterator, + value_type: &AbiType, +) -> Result { + // This function assumes that `field_iterator` contains enough `FieldElement`s in order to decode a `value_type` + // `Abi.decode` enforces that the encoded inputs matches the expected length defined by the ABI so this is safe. + let value = match value_type { + AbiType::Field | AbiType::Integer { .. } | AbiType::Boolean => { + let field_element = field_iterator.next().unwrap(); + + InputValue::Field(field_element) + } + AbiType::Array { length, typ } => { + let length = *length as usize; + let mut array_elements = Vec::with_capacity(length); + for _ in 0..length { + array_elements.push(decode_value(field_iterator, typ)?); } - AbiType::String { length } => { - let field_elements: Vec = - field_iterator.take(*length as usize).collect(); - InputValue::String(decode_string_value(&field_elements)) - } - AbiType::Struct { fields, .. } => { - let mut struct_map = BTreeMap::new(); + InputValue::Vec(array_elements) + } + AbiType::String { length } => { + let field_elements: Vec = field_iterator.take(*length as usize).collect(); - for (field_key, param_type) in fields { - let field_value = Self::decode_value(field_iterator, param_type)?; + InputValue::String(decode_string_value(&field_elements)) + } + AbiType::Struct { fields, .. } => { + let mut struct_map = BTreeMap::new(); - struct_map.insert(field_key.to_owned(), field_value); - } + for (field_key, param_type) in fields { + let field_value = decode_value(field_iterator, param_type)?; - InputValue::Struct(struct_map) + struct_map.insert(field_key.to_owned(), field_value); } - }; - Ok(value) - } + InputValue::Struct(struct_map) + } + }; + + Ok(value) } pub fn decode_string_value(field_elements: &[FieldElement]) -> String { diff --git a/crates/noirc_driver/src/lib.rs b/crates/noirc_driver/src/lib.rs index 05e4cfd28e3..3a1de5989ce 100644 --- a/crates/noirc_driver/src/lib.rs +++ b/crates/noirc_driver/src/lib.rs @@ -144,7 +144,7 @@ pub fn check_crate( context: &mut Context, crate_id: CrateId, deny_warnings: bool, - enable_slices: bool, + experimental_ssa: bool, ) -> Result { // Add the stdlib before we check the crate // TODO: This should actually be done when constructing the driver and then propagated to each dependency when added; @@ -159,7 +159,7 @@ pub fn check_crate( let std_crate = context.crate_graph.add_stdlib(CrateType::Library, root_file_id); propagate_dep(context, std_crate, &CrateName::new(std_crate_name).unwrap()); - context.def_interner.enable_slices = enable_slices; + context.def_interner.experimental_ssa = experimental_ssa; let mut errors = vec![]; match context.crate_graph.crate_type(crate_id) { diff --git a/crates/noirc_evaluator/src/ssa_refactor/acir_gen/acir_ir/generated_acir.rs b/crates/noirc_evaluator/src/ssa_refactor/acir_gen/acir_ir/generated_acir.rs index af01f39593d..70cd3b5a3fc 100644 --- a/crates/noirc_evaluator/src/ssa_refactor/acir_gen/acir_ir/generated_acir.rs +++ b/crates/noirc_evaluator/src/ssa_refactor/acir_gen/acir_ir/generated_acir.rs @@ -735,7 +735,7 @@ impl GeneratedAcir { let (q_witness, r_witness) = self.quotient_directive( comparison_evaluation.clone(), two_max_bits.into(), - Some(predicate), + Some(predicate.clone()), q_max_bits, r_max_bits, )?; @@ -760,10 +760,17 @@ impl GeneratedAcir { // - 2^{max_bits} - k == q * 2^{max_bits} + r // - This is only the case when q == 0 and r == 2^{max_bits} - k // + // case: predicate is zero + // The values for q and r will be zero for a honest prover and + // can be garbage for a dishonest prover. The below constraint will + // will be switched off. let mut expr = Expression::default(); expr.push_addition_term(two_max_bits, q_witness); expr.push_addition_term(FieldElement::one(), r_witness); - self.push_opcode(AcirOpcode::Arithmetic(&comparison_evaluation - &expr)); + + let equation = &comparison_evaluation - &expr; + let predicated_equation = self.mul_with_witness(&equation, &predicate); + self.push_opcode(AcirOpcode::Arithmetic(predicated_equation)); Ok(q_witness) } diff --git a/crates/noirc_frontend/Cargo.toml b/crates/noirc_frontend/Cargo.toml index f3fc1c83758..a9a62673af6 100644 --- a/crates/noirc_frontend/Cargo.toml +++ b/crates/noirc_frontend/Cargo.toml @@ -17,6 +17,7 @@ chumsky.workspace = true thiserror.workspace = true smol_str.workspace = true serde.workspace = true +serde_json.workspace = true rustc-hash = "1.1.0" small-ord-set = "0.1.3" diff --git a/crates/noirc_frontend/src/graph/mod.rs b/crates/noirc_frontend/src/graph/mod.rs index 7a95ff324de..e6dd226997f 100644 --- a/crates/noirc_frontend/src/graph/mod.rs +++ b/crates/noirc_frontend/src/graph/mod.rs @@ -51,16 +51,6 @@ pub struct CrateGraph { arena: FxHashMap, } -impl CrateGraph { - pub fn is_last_crate(&self, crate_id: CrateId) -> bool { - match crate_id { - CrateId::Crate(crate_id) | CrateId::Stdlib(crate_id) => { - (self.arena.len() - 1) == crate_id - } - } - } -} - /// List of characters that are not allowed in a crate name /// For example, Hyphen(-) is disallowed as it is similar to underscore(_) /// and we do not want names that differ by a hyphen diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index 3997903a43e..8bb88abd1e9 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -80,21 +80,31 @@ impl CrateDefMap { let mut ast = parse_file(&mut context.file_manager, root_file_id, errors); // TODO(#1850): This check should be removed once we fully move over to the new SSA pass - // Compiling with the old SSA pass will lead to duplicate method definitions between + // There are some features that use the new SSA pass that also affect the stdlib. + // 1. Compiling with the old SSA pass will lead to duplicate method definitions between // the `slice` and `array` modules of the stdlib. + // 2. The `println` method is a builtin with the old SSA but is a normal function that calls + // an oracle in the new SSA. // - // The last crate represents the stdlib crate. - // After resolving the manifest of the local crate the stdlib is added to the manifest and propagated to all crates - // thus being the last crate. - if !context.def_interner.enable_slices && context.crate_graph.is_last_crate(crate_id) { + if crate_id.is_stdlib() { let path_as_str = context .file_manager .path(root_file_id) .to_str() .expect("expected std path to be convertible to str"); assert_eq!(path_as_str, "std/lib"); - ast.module_decls - .retain(|ident| ident.0.contents != "slice" && ident.0.contents != "collections"); + // There are 2 printlns in the stdlib. If we are using the experimental SSA, we want to keep + // only the unconstrained one. Otherwise we want to keep only the constrained one. + ast.functions.retain(|func| { + func.def.name.0.contents.as_str() != "println" + || func.def.is_unconstrained == context.def_interner.experimental_ssa + }); + + if !context.def_interner.experimental_ssa { + ast.module_decls.retain(|ident| { + ident.0.contents != "slice" && ident.0.contents != "collections" + }); + } } // Allocate a default Module for the root, giving it a ModuleId diff --git a/crates/noirc_frontend/src/hir/resolution/resolver.rs b/crates/noirc_frontend/src/hir/resolution/resolver.rs index 506844824c8..f314cd63e44 100644 --- a/crates/noirc_frontend/src/hir/resolution/resolver.rs +++ b/crates/noirc_frontend/src/hir/resolution/resolver.rs @@ -329,7 +329,7 @@ impl<'a> Resolver<'a> { UnresolvedType::FieldElement(comp_time) => Type::FieldElement(comp_time), UnresolvedType::Array(size, elem) => { let elem = Box::new(self.resolve_type_inner(*elem, new_variables)); - if self.interner.enable_slices && size.is_none() { + if self.interner.experimental_ssa && size.is_none() { return Type::Slice(elem); } let resolved_size = self.resolve_array_size(size, new_variables); diff --git a/crates/noirc_frontend/src/monomorphization/mod.rs b/crates/noirc_frontend/src/monomorphization/mod.rs index c3b3ae44bd2..a3188eaa14a 100644 --- a/crates/noirc_frontend/src/monomorphization/mod.rs +++ b/crates/noirc_frontend/src/monomorphization/mod.rs @@ -188,7 +188,7 @@ impl<'interner> Monomorphizer<'interner> { let meta = self.interner.function_meta(&f); let name = self.interner.function_name(&f).to_owned(); - let return_type = Self::convert_type(meta.return_type()); + let return_type = self.convert_type(meta.return_type()); let parameters = self.parameters(meta.parameters); let body = self.expr(*self.interner.function(&f).as_expr()); let unconstrained = meta.is_unconstrained; @@ -223,7 +223,7 @@ impl<'interner> Monomorphizer<'interner> { let new_id = self.next_local_id(); let definition = self.interner.definition(ident.id); let name = definition.name.clone(); - new_params.push((new_id, definition.mutable, name, Self::convert_type(typ))); + new_params.push((new_id, definition.mutable, name, self.convert_type(typ))); self.define_local(ident.id, new_id); } HirPattern::Mutable(pattern, _) => self.parameter(*pattern, typ, new_params), @@ -262,7 +262,7 @@ impl<'interner> Monomorphizer<'interner> { HirExpression::Literal(HirLiteral::Str(contents)) => Literal(Str(contents)), HirExpression::Literal(HirLiteral::Bool(value)) => Literal(Bool(value)), HirExpression::Literal(HirLiteral::Integer(value)) => { - let typ = Self::convert_type(&self.interner.id_type(expr)); + let typ = self.convert_type(&self.interner.id_type(expr)); Literal(Integer(value, typ)) } HirExpression::Literal(HirLiteral::Array(array)) => match array { @@ -277,7 +277,7 @@ impl<'interner> Monomorphizer<'interner> { HirExpression::Prefix(prefix) => ast::Expression::Unary(ast::Unary { operator: prefix.operator, rhs: Box::new(self.expr(prefix.rhs)), - result_type: Self::convert_type(&self.interner.id_type(expr)), + result_type: self.convert_type(&self.interner.id_type(expr)), }), HirExpression::Infix(infix) => { @@ -300,7 +300,7 @@ impl<'interner> Monomorphizer<'interner> { HirExpression::Cast(cast) => ast::Expression::Cast(ast::Cast { lhs: Box::new(self.expr(cast.lhs)), - r#type: Self::convert_type(&cast.r#type), + r#type: self.convert_type(&cast.r#type), }), HirExpression::For(for_expr) => { @@ -314,7 +314,7 @@ impl<'interner> Monomorphizer<'interner> { ast::Expression::For(ast::For { index_variable, index_name: self.interner.definition_name(for_expr.identifier.id).to_owned(), - index_type: Self::convert_type(&self.interner.id_type(for_expr.start_range)), + index_type: self.convert_type(&self.interner.id_type(for_expr.start_range)), start_range: Box::new(start), end_range: Box::new(end), block, @@ -329,7 +329,7 @@ impl<'interner> Monomorphizer<'interner> { condition: Box::new(cond), consequence: Box::new(then), alternative: else_, - typ: Self::convert_type(&self.interner.id_type(expr)), + typ: self.convert_type(&self.interner.id_type(expr)), }) } @@ -354,9 +354,9 @@ impl<'interner> Monomorphizer<'interner> { array_elements: Vec, ) -> ast::Expression { let element_type = - Self::convert_type(&unwrap_array_element_type(&self.interner.id_type(array))); + self.convert_type(&unwrap_array_element_type(&self.interner.id_type(array))); let contents = vecmap(array_elements, |id| self.expr(id)); - Self::aos_to_soa(contents, element_type) + self.aos_to_soa(contents, element_type) } fn repeated_array( @@ -364,14 +364,14 @@ impl<'interner> Monomorphizer<'interner> { repeated_element: node_interner::ExprId, length: HirType, ) -> ast::Expression { - let element_type = Self::convert_type(&self.interner.id_type(repeated_element)); + let element_type = self.convert_type(&self.interner.id_type(repeated_element)); let contents = self.expr(repeated_element); let length = length .evaluate_to_u64() .expect("Length of array is unknown when evaluating numeric generic"); let contents = vec![contents; length as usize]; - Self::aos_to_soa(contents, element_type) + self.aos_to_soa(contents, element_type) } /// Convert an array in (potentially) array of structs form into struct of arrays form. @@ -380,9 +380,16 @@ impl<'interner> Monomorphizer<'interner> { /// /// TODO Remove side effects from clones fn aos_to_soa( + &self, array_contents: Vec, element_type: ast::Type, ) -> ast::Expression { + if self.interner.experimental_ssa { + return ast::Expression::Literal(ast::Literal::Array(ast::ArrayLiteral { + contents: array_contents, + element_type, + })); + } match element_type { ast::Type::Field | ast::Type::Integer(_, _) @@ -403,7 +410,7 @@ impl<'interner> Monomorphizer<'interner> { ast::Expression::ExtractTupleField(Box::new(element.clone()), i) }); - Self::aos_to_soa(contents, element_type) + self.aos_to_soa(contents, element_type) }, )), @@ -414,22 +421,31 @@ impl<'interner> Monomorphizer<'interner> { } fn index(&mut self, id: node_interner::ExprId, index: HirIndexExpression) -> ast::Expression { - let element_type = Self::convert_type(&self.interner.id_type(id)); + let element_type = self.convert_type(&self.interner.id_type(id)); let collection = Box::new(self.expr(index.collection)); let index = Box::new(self.expr(index.index)); let location = self.interner.expr_location(&id); - Self::aos_to_soa_index(collection, index, element_type, location) + self.aos_to_soa_index(collection, index, element_type, location) } /// Unpack an array index into an array of structs into a struct of arrays index if needed. /// E.g. transforms my_pair_array[i] into (my_pair1_array[i], my_pair2_array[i]) fn aos_to_soa_index( + &self, collection: Box, index: Box, element_type: ast::Type, location: Location, ) -> ast::Expression { + if self.interner.experimental_ssa { + return ast::Expression::Index(ast::Index { + collection, + index, + element_type, + location, + }); + } match element_type { ast::Type::Field | ast::Type::Integer(_, _) @@ -447,7 +463,7 @@ impl<'interner> Monomorphizer<'interner> { let collection = Box::new(ast::Expression::ExtractTupleField(collection.clone(), i)); - Self::aos_to_soa_index(collection, index.clone(), element_type, location) + self.aos_to_soa_index(collection, index.clone(), element_type, location) })) } @@ -496,7 +512,7 @@ impl<'interner> Monomorphizer<'interner> { for (field_name, expr_id) in constructor.fields { let new_id = self.next_local_id(); let field_type = field_type_map.get(&field_name.0.contents).unwrap(); - let typ = Self::convert_type(field_type); + let typ = self.convert_type(field_type); field_vars.insert(field_name.0.contents.clone(), (new_id, typ)); let expression = Box::new(self.expr(expr_id)); @@ -592,7 +608,7 @@ impl<'interner> Monomorphizer<'interner> { let mutable = false; let definition = Definition::Local(fresh_id); let name = i.to_string(); - let typ = Self::convert_type(&field_type); + let typ = self.convert_type(&field_type); let new_rhs = ast::Expression::Ident(ast::Ident { location, mutable, definition, name, typ }); @@ -612,7 +628,7 @@ impl<'interner> Monomorphizer<'interner> { let mutable = definition.mutable; let definition = self.lookup_local(ident.id)?; - let typ = Self::convert_type(&self.interner.id_type(ident.id)); + let typ = self.convert_type(&self.interner.id_type(ident.id)); Some(ast::Ident { location: Some(ident.location), mutable, definition, name, typ }) } @@ -627,7 +643,7 @@ impl<'interner> Monomorphizer<'interner> { let typ = self.interner.id_type(expr_id); let definition = self.lookup_function(*func_id, expr_id, &typ); - let typ = Self::convert_type(&typ); + let typ = self.convert_type(&typ); let ident = ast::Ident { location, mutable, definition, name, typ }; ast::Expression::Ident(ident) } @@ -653,7 +669,7 @@ impl<'interner> Monomorphizer<'interner> { } /// Convert a non-tuple/struct type to a monomorphized type - fn convert_type(typ: &HirType) -> ast::Type { + fn convert_type(&self, typ: &HirType) -> ast::Type { match typ { HirType::FieldElement(_) => ast::Type::Field, HirType::Integer(_, sign, bits) => ast::Type::Integer(*sign, *bits), @@ -663,12 +679,15 @@ impl<'interner> Monomorphizer<'interner> { HirType::Array(length, element) => { let length = length.evaluate_to_u64().unwrap_or(0); - let element = Self::convert_type(element.as_ref()); - Self::aos_to_soa_type(length, element) + let element = self.convert_type(element.as_ref()); + if self.interner.experimental_ssa { + return ast::Type::Array(length, Box::new(element)); + } + self.aos_to_soa_type(length, element) } HirType::Slice(element) => { - let element = Self::convert_type(element.as_ref()); + let element = self.convert_type(element.as_ref()); ast::Type::Slice(Box::new(element)) } @@ -676,7 +695,7 @@ impl<'interner> Monomorphizer<'interner> { | HirType::TypeVariable(binding) | HirType::NamedGeneric(binding, _) => { if let TypeBinding::Bound(binding) = &*binding.borrow() { - return Self::convert_type(binding); + return self.convert_type(binding); } // Default any remaining unbound type variables to Field. @@ -693,23 +712,23 @@ impl<'interner> Monomorphizer<'interner> { HirType::Struct(def, args) => { let fields = def.borrow().get_fields(args); - let fields = vecmap(fields, |(_, field)| Self::convert_type(&field)); + let fields = vecmap(fields, |(_, field)| self.convert_type(&field)); ast::Type::Tuple(fields) } HirType::Tuple(fields) => { - let fields = vecmap(fields, Self::convert_type); + let fields = vecmap(fields, |typ| self.convert_type(typ)); ast::Type::Tuple(fields) } HirType::Function(args, ret) => { - let args = vecmap(args, Self::convert_type); - let ret = Box::new(Self::convert_type(ret)); + let args = vecmap(args, |typ| self.convert_type(typ)); + let ret = Box::new(self.convert_type(ret)); ast::Type::Function(args, ret) } HirType::MutableReference(element) => { - let element = Self::convert_type(element); + let element = self.convert_type(element); ast::Type::MutableReference(Box::new(element)) } @@ -721,7 +740,10 @@ impl<'interner> Monomorphizer<'interner> { /// Converts arrays of structs (AOS) into structs of arrays (SOA). /// This is required since our SSA pass does not support arrays of structs. - fn aos_to_soa_type(length: u64, element: ast::Type) -> ast::Type { + fn aos_to_soa_type(&self, length: u64, element: ast::Type) -> ast::Type { + if self.interner.experimental_ssa { + return ast::Type::Array(length, Box::new(element)); + } match element { ast::Type::Field | ast::Type::Integer(_, _) @@ -731,7 +753,7 @@ impl<'interner> Monomorphizer<'interner> { | ast::Type::MutableReference(_) => ast::Type::Array(length, Box::new(element)), ast::Type::Tuple(elements) => { - ast::Type::Tuple(vecmap(elements, |typ| Self::aos_to_soa_type(length, typ))) + ast::Type::Tuple(vecmap(elements, |typ| self.aos_to_soa_type(length, typ))) } ast::Type::Array(_, _) | ast::Type::String(_) | ast::Type::Slice(_) => { @@ -746,15 +768,53 @@ impl<'interner> Monomorphizer<'interner> { id: node_interner::ExprId, ) -> ast::Expression { let func = Box::new(self.expr(call.func)); - let arguments = vecmap(&call.arguments, |id| self.expr(*id)); + let mut arguments = vecmap(&call.arguments, |id| self.expr(*id)); + let hir_arguments = vecmap(&call.arguments, |id| self.interner.expression(id)); let return_type = self.interner.id_type(id); - let return_type = Self::convert_type(&return_type); + let return_type = self.convert_type(&return_type); let location = call.location; + if let ast::Expression::Ident(ident) = func.as_ref() { + if let Definition::Oracle(name) = &ident.definition { + if name.as_str() == "println" { + // 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 + self.append_abi_arg(&hir_arguments[0], &mut arguments); + } + } + } + self.try_evaluate_call(&func, &call.arguments, &return_type) .unwrap_or(ast::Expression::Call(ast::Call { func, arguments, return_type, location })) } + /// Adds a function argument that contains type metadata that is required to tell + /// a caller (such as nargo) how to convert values passed to an foreign call + /// back to a human-readable string. + /// The values passed to an foreign call will be a simple list of field elements, + /// thus requiring extra metadata to correctly decode this list of elements. + /// + /// The Noir compiler has an `AbiType` that handles encoding/decoding a list + /// of field elements to/from JSON. The type metadata attached in this method + /// is the serialized `AbiType` for the argument passed to the function. + /// The caller that is running a Noir program should then deserialize the `AbiType`, + /// and accurately decode the list of field elements passed to the foreign call. + fn append_abi_arg(&self, hir_argument: &HirExpression, arguments: &mut Vec) { + match hir_argument { + HirExpression::Ident(ident) => { + let typ = self.interner.id_type(ident.id); + let typ = typ.follow_bindings(); + + let abi_type = typ.as_abi_type(); + let abi_as_string = + serde_json::to_string(&abi_type).expect("ICE: expected Abi type to serialize"); + + arguments.push(ast::Expression::Literal(ast::Literal::Str(abi_as_string))); + } + _ => unreachable!("logging expr {:?} is not supported", arguments[0]), + } + } + /// Try to evaluate certain builtin functions (currently only 'array_len' and field modulus methods) /// at their call site. /// NOTE: Evaluating at the call site means we cannot track aliased functions. @@ -864,7 +924,7 @@ impl<'interner> Monomorphizer<'interner> { match index_lvalue { Some((index, element_type, location)) => { - Self::aos_to_soa_assign(expression, Box::new(lvalue), index, element_type, location) + self.aos_to_soa_assign(expression, Box::new(lvalue), index, element_type, location) } None => ast::Expression::Assign(ast::Assign { expression, lvalue }), } @@ -899,13 +959,13 @@ impl<'interner> Monomorphizer<'interner> { ); let index = Box::new(self.expr(index)); - let element_type = Self::convert_type(&typ); + let element_type = self.convert_type(&typ); (array, Some((index, element_type, location))) } HirLValue::Dereference { lvalue, element_type } => { let (reference, index) = self.lvalue(*lvalue); let reference = Box::new(reference); - let element_type = Self::convert_type(&element_type); + let element_type = self.convert_type(&element_type); let lvalue = ast::LValue::Dereference { reference, element_type }; (lvalue, index) } @@ -913,12 +973,17 @@ impl<'interner> Monomorphizer<'interner> { } fn aos_to_soa_assign( + &self, expression: Box, lvalue: Box, index: Box, typ: ast::Type, location: Location, ) -> ast::Expression { + if self.interner.experimental_ssa { + let lvalue = ast::LValue::Index { array: lvalue, index, element_type: typ, location }; + return ast::Expression::Assign(ast::Assign { lvalue, expression }); + } match typ { ast::Type::Tuple(fields) => { let fields = fields.into_iter().enumerate(); @@ -926,7 +991,7 @@ impl<'interner> Monomorphizer<'interner> { let expression = ast::Expression::ExtractTupleField(expression.clone(), i); let lvalue = ast::LValue::MemberAccess { object: lvalue.clone(), field_index: i }; - Self::aos_to_soa_assign( + self.aos_to_soa_assign( Box::new(expression), Box::new(lvalue), index.clone(), @@ -945,9 +1010,9 @@ impl<'interner> Monomorphizer<'interner> { } fn lambda(&mut self, lambda: HirLambda) -> ast::Expression { - let ret_type = Self::convert_type(&lambda.return_type); + let ret_type = self.convert_type(&lambda.return_type); let lambda_name = "lambda"; - let parameter_types = vecmap(&lambda.parameters, |(_, typ)| Self::convert_type(typ)); + let parameter_types = vecmap(&lambda.parameters, |(_, typ)| self.convert_type(typ)); // Manually convert to Parameters type so we can reuse the self.parameters method let parameters = Parameters(vecmap(lambda.parameters, |(pattern, typ)| { diff --git a/crates/noirc_frontend/src/node_interner.rs b/crates/noirc_frontend/src/node_interner.rs index ce12cf5cfc3..69b313fd538 100644 --- a/crates/noirc_frontend/src/node_interner.rs +++ b/crates/noirc_frontend/src/node_interner.rs @@ -72,8 +72,9 @@ pub struct NodeInterner { primitive_methods: HashMap<(TypeMethodKey, String), FuncId>, /// TODO(#1850): This is technical debt that should be removed once we fully move over - /// to the new SSA pass which does have slices enabled - pub enable_slices: bool, + /// to the new SSA pass which has certain frontend features enabled + /// such as slices and the removal of aos_to_soa + pub experimental_ssa: bool, } #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] @@ -253,7 +254,7 @@ impl Default for NodeInterner { globals: HashMap::new(), struct_methods: HashMap::new(), primitive_methods: HashMap::new(), - enable_slices: false, + experimental_ssa: false, }; // An empty block expression is used often, we add this into the `node` on startup diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index 7629634ac0a..80bf6e8bae5 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -15,8 +15,18 @@ mod unsafe; mod collections; mod compat; +// TODO(#1850): Remove this builtin to use the foreign call based println #[builtin(println)] fn println(_input : T) {} +// 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 +#[oracle(println)] +unconstrained fn println_oracle(_input: T) {} + +unconstrained fn println(input: T) { + println_oracle(input); +} + #[foreign(recursive_aggregation)] fn verify_proof(_verification_key : [Field], _proof : [Field], _public_inputs : [Field], _key_hash : Field, _input_aggregation_object : [Field]) -> [Field] {}