diff --git a/Cargo.lock b/Cargo.lock index 88b38ba1b95..5b9e5d37c90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1910,6 +1910,7 @@ dependencies = [ "leo-disassembler", "leo-errors", "leo-parser", + "leo-passes", "leo-span", "leo-test-framework", "rand", @@ -3212,8 +3213,7 @@ dependencies = [ [[package]] name = "snarkvm" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2650900efb0df263e678b7f0f423e99277da3c6c62c63062a1639242bb73644" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "anyhow", "dotenvy", @@ -3233,8 +3233,7 @@ dependencies = [ [[package]] name = "snarkvm-algorithms" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc064d3ad09ca7a22e9659212d4fddf28e4bbc9c96d4aa2288aa17933498117" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "aleo-std", "anyhow", @@ -3261,8 +3260,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13621a445caf8ee1958e835e14a0f8974bd0b757ebd4de4fe22b9063e45aad40" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-circuit-account", "snarkvm-circuit-algorithms", @@ -3276,8 +3274,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-account" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df5d6cb0579ec9e9f473d85ee985cb6cf021d35541262dc770438164f60a328" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-circuit-network", "snarkvm-circuit-types", @@ -3287,8 +3284,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-algorithms" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75f80d1dd05e4f5daaee601240c0bec20fafdafa5246cc2c48382940ce08b6d" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-circuit-types", "snarkvm-console-algorithms", @@ -3298,8 +3294,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-collections" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c70bfd65cf988f1546b5f7b3751b62977a1937ffebad090850a784ebb0a96a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-circuit-algorithms", "snarkvm-circuit-types", @@ -3309,8 +3304,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-environment" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "901cc5c67082b0df337ee26f2bfb3b70a2ca51d64e6f200cf11f7a1708bfc1d9" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "indexmap", "itertools 0.14.0", @@ -3328,14 +3322,12 @@ dependencies = [ [[package]] name = "snarkvm-circuit-environment-witness" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884964c0c167823e6ee76fc1ae1e647880f07b4a04415371c81de0f0cb5a2e6d" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" [[package]] name = "snarkvm-circuit-network" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38eab9d33e9eb389d7187a67327ba48482e64ab66924dd934367f4cd4775f6b0" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-circuit-algorithms", "snarkvm-circuit-collections", @@ -3346,8 +3338,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-program" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b137592dd746c7eb4f56f63ecfa9947e62257f9b3b14e335aa8afacd5354217" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-circuit-account", "snarkvm-circuit-algorithms", @@ -3361,8 +3352,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-types" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc98725fbd1fc34be94198f8271f534d1c9049c822a26eaa26432a7edcf6e81a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-circuit-environment", "snarkvm-circuit-types-address", @@ -3377,8 +3367,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-types-address" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "550c5271f41cb15316f7a09a5d1faa0411fcef3031a4a23ac51751d9dd9e0b6c" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-circuit-environment", "snarkvm-circuit-types-boolean", @@ -3391,8 +3380,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-types-boolean" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bfbc7c607653fd523ef25fccec2fa7084d869468ff49ee58c6069a628786fe" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-circuit-environment", "snarkvm-console-types-boolean", @@ -3401,8 +3389,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-types-field" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8db5a8888734da5ee5598fe80a70ec251a679fad2c72aaccb039ec8c09836fa" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-circuit-environment", "snarkvm-circuit-types-boolean", @@ -3412,8 +3399,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-types-group" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f76791b4f4c960b5c8a89c6caa2953315eb70fe8ca0e7734c6354b8206089cce" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-circuit-environment", "snarkvm-circuit-types-boolean", @@ -3425,8 +3411,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-types-integers" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3607d81e417bf23dde768575ae16072537e66ed19f8bbd1b96066c66b207f8bb" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-circuit-environment", "snarkvm-circuit-types-boolean", @@ -3438,8 +3423,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-types-scalar" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe08c39cb7ce71d947da1fd36c0a677ed9d361edadb42c14f83b9652cf34d6a5" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-circuit-environment", "snarkvm-circuit-types-boolean", @@ -3450,8 +3434,7 @@ dependencies = [ [[package]] name = "snarkvm-circuit-types-string" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdf54a2995c69c4e0f770bbd5352204151736f35128190f6361a58b762ae5210" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-circuit-environment", "snarkvm-circuit-types-boolean", @@ -3463,8 +3446,7 @@ dependencies = [ [[package]] name = "snarkvm-console" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78da46f40feec9078410c2415d36489256b282abf3c1dec85945874115e8803" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-console-account", "snarkvm-console-algorithms", @@ -3477,8 +3459,7 @@ dependencies = [ [[package]] name = "snarkvm-console-account" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd345486bab3fbe54a5800a1dfee37e05b0dee06710b4f389fea98645e8f58b6" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "bs58", "snarkvm-console-network", @@ -3489,8 +3470,7 @@ dependencies = [ [[package]] name = "snarkvm-console-algorithms" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ba6389587cd3bd7431ff3280619ae8a5761a46722ae53edcd98172ce027ae3" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "blake2s_simd", "hex", @@ -3505,8 +3485,7 @@ dependencies = [ [[package]] name = "snarkvm-console-collections" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a72b0ca52f5e4059b4159758aac8395155c081953abfddd2c196d43ba9743b5" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "aleo-std", "rayon", @@ -3517,8 +3496,7 @@ dependencies = [ [[package]] name = "snarkvm-console-network" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c92880c09b37d5bdff2ced22e0911cce75e3e425f51783628481d478aa2a80d" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "anyhow", "enum-iterator", @@ -3538,8 +3516,7 @@ dependencies = [ [[package]] name = "snarkvm-console-network-environment" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee1aeabebbda7b45b66196cdd9db456d154625b6b3233c477536b82e46024688" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "anyhow", "bech32", @@ -3557,8 +3534,7 @@ dependencies = [ [[package]] name = "snarkvm-console-program" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff7a0ff48530b8392268dec61f9231b2eb290624d6f58246ada83eaca7bcde99" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "enum-iterator", "enum_index", @@ -3579,8 +3555,7 @@ dependencies = [ [[package]] name = "snarkvm-console-types" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047f4712e7de64699e401c08d80aa11aefbda4bb5a3b97cfe0181b31cb482c8" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-console-network-environment", "snarkvm-console-types-address", @@ -3595,8 +3570,7 @@ dependencies = [ [[package]] name = "snarkvm-console-types-address" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc581d795614ffa23dbf737c44413caf7b25a62845f38649ba00a61be6f124ec" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-console-network-environment", "snarkvm-console-types-boolean", @@ -3607,8 +3581,7 @@ dependencies = [ [[package]] name = "snarkvm-console-types-boolean" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740afa0e3018eb8048f1d2351b92c163214a6d16b73513f9f4fe0de53d8d977c" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-console-network-environment", ] @@ -3616,8 +3589,7 @@ dependencies = [ [[package]] name = "snarkvm-console-types-field" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d92c4e9dddcdf25942903959d0402cad5baba3d56f0d09d22f4ee95fc24cb5" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-console-network-environment", "snarkvm-console-types-boolean", @@ -3627,8 +3599,7 @@ dependencies = [ [[package]] name = "snarkvm-console-types-group" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10ea02928ed85ea863cac8eb429ba92232d8e0aba7f111473b42665eedcc794f" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-console-network-environment", "snarkvm-console-types-boolean", @@ -3639,8 +3610,7 @@ dependencies = [ [[package]] name = "snarkvm-console-types-integers" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca7cfb62960c945521065a04cabf5b25cb9925027455c4f428d7613afc22a77d" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-console-network-environment", "snarkvm-console-types-boolean", @@ -3651,8 +3621,7 @@ dependencies = [ [[package]] name = "snarkvm-console-types-scalar" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "593c61497ce4658e43d0829b6dd7ee57a01fe5069333f36bd14bcfa1cdeef680" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-console-network-environment", "snarkvm-console-types-boolean", @@ -3663,8 +3632,7 @@ dependencies = [ [[package]] name = "snarkvm-console-types-string" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8eb1d2a1275202adb50f1abae4d6afc1059950435f01bb884da435205bff96b" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-console-network-environment", "snarkvm-console-types-boolean", @@ -3675,8 +3643,7 @@ dependencies = [ [[package]] name = "snarkvm-curves" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b8f4f8477fdd9fbab7bde40b5fd104993b6199227cd219af968384a8e8d657" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "rand", "rayon", @@ -3690,8 +3657,7 @@ dependencies = [ [[package]] name = "snarkvm-fields" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78912e74b5de1807326eb175e75331a9f5c195944746c362e9092dda6b54db8d" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "aleo-std", "anyhow", @@ -3708,8 +3674,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7316e32fd40e028b214a9c3699a8c662e8202269d8378ab9d73f79388af110ce" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "aleo-std", "anyhow", @@ -3729,6 +3694,7 @@ dependencies = [ "snarkvm-ledger-store", "snarkvm-synthesizer", "snarkvm-utilities", + "thiserror 2.0.17", "time", "tracing", ] @@ -3736,8 +3702,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-authority" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a20386597ffd5550e536265c0a24d823db995df8d555ab59d55b335d823cd2" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "anyhow", "rand", @@ -3749,8 +3714,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-block" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39d4687d54811d18d6922e167e432c2f78be2fc31ff849098d90b5a08f56a741" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "anyhow", "indexmap", @@ -3772,8 +3736,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-committee" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2678d6e0bc202ea821638836c36d129df1f4aec86c19681edffbf0d7ce14bb04" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "indexmap", "rayon", @@ -3785,8 +3748,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-narwhal" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00182f1ef4f1d804d884784a3de91246fe3f4b53231434b02f15f35b2146638" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-ledger-narwhal-batch-certificate", "snarkvm-ledger-narwhal-batch-header", @@ -3799,8 +3761,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-narwhal-batch-certificate" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a87acbd36d5851fc746550fc190d37ce56660a48be9659bcc4c0de08ba4ea7f" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "indexmap", "rayon", @@ -3813,8 +3774,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-narwhal-batch-header" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c94988348c23db830c3431528af45a6c876a2bf1142fcd15f42d128a496762b" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "indexmap", "rayon", @@ -3826,8 +3786,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-narwhal-data" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c37c79d6a7f934ed19a7f32391ea1c77567991f3a29b1dfe138b4b8ef27b41" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "bytes", "serde_json", @@ -3838,8 +3797,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-narwhal-subdag" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98fe2242ed6a26b4fe3d52888b392b4560e3ec466ea97e7d06d4083068d9113f" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "indexmap", "rayon", @@ -3854,8 +3812,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-narwhal-transmission" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5d7f2d5e27d90ff886c9ce773d85283297027da314bf6ae76bcce09fc76dbf" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "bytes", "serde_json", @@ -3868,8 +3825,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-narwhal-transmission-id" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "850f880f898929b3ad24b6e403c6edeec8c35f40040c5f89c1ae2ef539bf0293" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "snarkvm-console", "snarkvm-ledger-puzzle", @@ -3878,8 +3834,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-puzzle" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94e53be712268f4d0994899fa2d5143606da62e1946265607abe58fa7a6723c" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "aleo-std", "anyhow", @@ -3898,8 +3853,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-puzzle-epoch" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c2b866424dba58368d47e85e4529e4dbae68cf346ab1f9b7e2dd08631c5697" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "aleo-std", "anyhow", @@ -3921,8 +3875,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-query" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b9a6594198d17477adc72a8fbffa495e0c0193dd8fc36e632f787d5b98c694" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "anyhow", "async-trait", @@ -3939,8 +3892,7 @@ dependencies = [ [[package]] name = "snarkvm-ledger-store" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c125333fa216ae69b3f90563a2d06f47511d553bc0f951771709177cfaa1638" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "aleo-std-storage", "anyhow", @@ -3959,13 +3911,13 @@ dependencies = [ "snarkvm-synthesizer-program", "snarkvm-synthesizer-snark", "snarkvm-utilities", + "tracing", ] [[package]] name = "snarkvm-parameters" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1f5f415330dbb5c5921ef6d05962b5d18f239c12d4f1a12795a592ecde6038" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "aleo-std", "anyhow", @@ -3987,8 +3939,7 @@ dependencies = [ [[package]] name = "snarkvm-synthesizer" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0092bc65216609a180c01e8a03b2a527d6b527e8776ce71785b932f901893e" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "aleo-std", "anyhow", @@ -4013,14 +3964,14 @@ dependencies = [ "snarkvm-synthesizer-program", "snarkvm-synthesizer-snark", "snarkvm-utilities", + "tokio", "tracing", ] [[package]] name = "snarkvm-synthesizer-process" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28ceadf64062805b4a48b298c206a9f5a4bb12032a9a60465ef1e0a5766cdf77" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "aleo-std", "colored 3.0.0", @@ -4044,8 +3995,7 @@ dependencies = [ [[package]] name = "snarkvm-synthesizer-program" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdc32136ef3ac69aaeab040e11e4a0bdc8c00dbae778c0d3b65120d179e44eb9" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "enum-iterator", "indexmap", @@ -4064,8 +4014,7 @@ dependencies = [ [[package]] name = "snarkvm-synthesizer-snark" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175dbc6a6d7cbf188397b81c545323bed27dab554a40e3a6261988f0b1fdca0a" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "bincode", "serde_json", @@ -4078,12 +4027,12 @@ dependencies = [ [[package]] name = "snarkvm-utilities" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b062dc2351b888db6ea293e2ee294b4069c5b9eda242f8d336f08f2ea95150d" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "aleo-std", "anyhow", "bincode", + "colored 3.0.0", "num-bigint", "num_cpus", "rand", @@ -4101,8 +4050,7 @@ dependencies = [ [[package]] name = "snarkvm-utilities-derives" version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a60e21b9d249446399f4c67bf2ca12e1e10bb2539a1812df3202d235213d5b3" +source = "git+https://github.com/ProvableHQ/snarkVM.git?branch=struct-namespace-squashed#93e4e4807f6e2c04fa1d2e93f6c1f42f352dc28d" dependencies = [ "proc-macro2", "quote 1.0.42", diff --git a/Cargo.toml b/Cargo.toml index 93471530e47..0c16761d050 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,7 +144,9 @@ version = "3.1.1" version = "0.10.9" [workspace.dependencies.snarkvm] -version = "4.4.0" +#path = "./../snarkvm" +git = "https://github.com/ProvableHQ/snarkVM.git" +branch = "struct-namespace-squashed" features = [ "test_consensus_heights" ] [workspace.dependencies.tempfile] diff --git a/compiler/ast/src/common/graph/mod.rs b/compiler/ast/src/common/graph/mod.rs index 918d052dba5..f98ee7492e0 100644 --- a/compiler/ast/src/common/graph/mod.rs +++ b/compiler/ast/src/common/graph/mod.rs @@ -20,9 +20,8 @@ use leo_span::Symbol; use indexmap::{IndexMap, IndexSet}; use std::{fmt::Debug, hash::Hash, rc::Rc}; -/// A composite dependency graph. -/// The `Vec` is to the absolute path to each composite -pub type CompositeGraph = DiGraph>; +/// A struct dependency graph. +pub type CompositeGraph = DiGraph; /// A call graph. pub type CallGraph = DiGraph; diff --git a/compiler/ast/src/expressions/composite_init.rs b/compiler/ast/src/expressions/composite_init.rs index d938c4a662b..a215a908374 100644 --- a/compiler/ast/src/expressions/composite_init.rs +++ b/compiler/ast/src/expressions/composite_init.rs @@ -58,6 +58,8 @@ pub struct CompositeExpression { /// N.B. Any functions or member constants in the composite definition /// are excluded from this list. pub members: Vec, + /// The external program that this composite is defined in. + pub program: Option, /// A span from `name` to `}`. pub span: Span, /// The ID of the node. @@ -66,7 +68,12 @@ pub struct CompositeExpression { impl fmt::Display for CompositeExpression { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.path)?; + if let Some(program) = self.program.as_ref() { + write!(f, "{}.aleo/{}", program, self.path)?; + } else { + write!(f, "{}", self.path)?; + } + if !self.const_arguments.is_empty() { write!(f, "::[{}]", self.const_arguments.iter().format(", "))?; } diff --git a/compiler/ast/src/expressions/mod.rs b/compiler/ast/src/expressions/mod.rs index 7dc277a7287..57fce16c8c9 100644 --- a/compiler/ast/src/expressions/mod.rs +++ b/compiler/ast/src/expressions/mod.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::{Identifier, IntegerType, Intrinsic, Node, NodeBuilder, NodeID, Path, Type}; +use crate::{Identifier, IntegerType, Intrinsic, Location, Node, NodeBuilder, NodeID, Path, Type}; use leo_span::{Span, Symbol}; use serde::{Deserialize, Serialize}; @@ -388,7 +388,8 @@ impl Expression { ty: &Type, span: Span, node_builder: &NodeBuilder, - composite_lookup: &dyn Fn(&[Symbol]) -> Vec<(Symbol, Type)>, + current_program: Symbol, + composite_lookup: &dyn Fn(&Location) -> Vec<(Symbol, Type)>, ) -> Option { let id = node_builder.next_id(); @@ -444,13 +445,17 @@ impl Expression { // Composite types Type::Composite(composite_type) => { let path = &composite_type.path; - let members = composite_lookup(&path.absolute_path()); + let members = composite_lookup(&Location::new( + composite_type.program.unwrap_or(current_program), + path.absolute_path(), + )); let composite_members = members .into_iter() .map(|(symbol, member_type)| { let member_id = node_builder.next_id(); - let zero_expr = Self::zero(&member_type, span, node_builder, composite_lookup)?; + let zero_expr = + Self::zero(&member_type, span, node_builder, current_program, composite_lookup)?; Some(CompositeFieldInitializer { span, @@ -465,6 +470,7 @@ impl Expression { span, id, path: path.clone(), + program: composite_type.program, const_arguments: composite_type.const_arguments.clone(), members: composite_members, })) @@ -474,7 +480,7 @@ impl Expression { Type::Array(array_type) => { let element_ty = &array_type.element_type; - let element_expr = Self::zero(element_ty, span, node_builder, composite_lookup)?; + let element_expr = Self::zero(element_ty, span, node_builder, current_program, composite_lookup)?; Some(Expression::Repeat( RepeatExpression { span, id, expr: element_expr, count: *array_type.length.clone() }.into(), diff --git a/compiler/ast/src/interpreter_value/intrinsic.rs b/compiler/ast/src/interpreter_value/intrinsic.rs index 5e46c752247..be5d022f8e0 100644 --- a/compiler/ast/src/interpreter_value/intrinsic.rs +++ b/compiler/ast/src/interpreter_value/intrinsic.rs @@ -163,11 +163,13 @@ pub fn evaluate_intrinsic( _ => crate::halt_no_span2!("expected array for deserialization"), }; let get_struct_fail = |_: &SvmIdentifier| anyhow::bail!("structs are not supported"); + let get_external_struct_fail = |_: &SvmLocator| anyhow::bail!("structs are not supported"); let value = snarkvm::synthesizer::program::evaluate_deserialize( variant, &bits, &type_.to_snarkvm()?, &get_struct_fail, + &get_external_struct_fail, )?; Ok(value.into()) }; diff --git a/compiler/ast/src/interpreter_value/value.rs b/compiler/ast/src/interpreter_value/value.rs index 72e91d3409e..ab5698dc840 100644 --- a/compiler/ast/src/interpreter_value/value.rs +++ b/compiler/ast/src/interpreter_value/value.rs @@ -43,6 +43,7 @@ use snarkvm::prelude::{ pub(crate) use snarkvm::prelude::{ Identifier as SvmIdentifierParam, Literal as SvmLiteralParam, + Locator as SvmLocatorParam, Plaintext, Signature as SvmSignature, TestnetV0, @@ -61,6 +62,7 @@ pub(crate) type ProgramID = ProgramIDParam; pub(crate) type SvmPlaintext = Plaintext; pub(crate) type SvmLiteral = SvmLiteralParam; pub(crate) type SvmIdentifier = SvmIdentifierParam; +pub(crate) type SvmLocator = SvmLocatorParam; pub(crate) type Group = SvmGroup; pub(crate) type Field = SvmField; pub(crate) type Scalar = SvmScalar; @@ -827,8 +829,9 @@ impl Value { &self, span: Span, node_builder: &NodeBuilder, + current_program: Symbol, ty: &Type, - struct_lookup: &dyn Fn(&[Symbol]) -> Vec<(Symbol, Type)>, + struct_lookup: &dyn Fn(&Location) -> Vec<(Symbol, Type)>, ) -> Option { use crate::{Literal, TupleExpression, UnitExpression}; @@ -850,7 +853,7 @@ impl Value { elements: vec .iter() .zip(tuple_type.elements()) - .map(|(val, ty)| val.to_expression(span, node_builder, ty, struct_lookup)) + .map(|(val, ty)| val.to_expression(span, node_builder, current_program, ty, struct_lookup)) .collect::>>()?, } .into() @@ -858,7 +861,7 @@ impl Value { ValueVariants::Unsuffixed(s) => Literal::unsuffixed(s.clone(), span, id).into(), ValueVariants::Svm(value) => match value { SvmValueParam::Plaintext(plaintext) => { - plaintext_to_expression(plaintext, span, node_builder, ty, &struct_lookup)? + plaintext_to_expression(plaintext, span, node_builder, current_program, ty, &struct_lookup)? } SvmValueParam::Record(..) => return None, SvmValueParam::Future(..) => return None, @@ -876,8 +879,9 @@ fn plaintext_to_expression( plaintext: &SvmPlaintext, span: Span, node_builder: &NodeBuilder, + current_program: Symbol, ty: &Type, - struct_lookup: &dyn Fn(&[Symbol]) -> Vec<(Symbol, Type)>, + struct_lookup: &dyn Fn(&Location) -> Vec<(Symbol, Type)>, ) -> Option { use crate::{ArrayExpression, CompositeExpression, CompositeFieldInitializer, Identifier, IntegerType, Literal}; @@ -925,7 +929,8 @@ fn plaintext_to_expression( return None; }; let symbols = composite_type.path.as_symbols(); - let iter_members = struct_lookup(&symbols); + let iter_members = + struct_lookup(&Location::new(composite_type.program.unwrap_or(current_program), symbols)); CompositeExpression { span, id, @@ -933,6 +938,7 @@ fn plaintext_to_expression( // If we were able to construct a Value, the const arguments must have already been resolved // and inserted appropriately. const_arguments: Vec::new(), + program: None, // not an external struct. members: iter_members .into_iter() .map(|(sym, ty)| { @@ -946,6 +952,7 @@ fn plaintext_to_expression( index_map.get(&svm_identifier)?, span, node_builder, + current_program, &ty, &struct_lookup, )?), @@ -964,7 +971,16 @@ fn plaintext_to_expression( id, elements: vec .iter() - .map(|pt| plaintext_to_expression(pt, span, node_builder, &array_ty.element_type, &struct_lookup)) + .map(|pt| { + plaintext_to_expression( + pt, + span, + node_builder, + current_program, + &array_ty.element_type, + &struct_lookup, + ) + }) .collect::>>()?, } .into() diff --git a/compiler/ast/src/lib.rs b/compiler/ast/src/lib.rs index 576e36ec0aa..f2ac899ed50 100644 --- a/compiler/ast/src/lib.rs +++ b/compiler/ast/src/lib.rs @@ -156,6 +156,18 @@ impl AsRef for Ast { } } +/// Produced set of bytecodes +pub struct CompiledPrograms { + pub primary_bytecode: String, + pub import_bytecodes: Vec, +} + +/// Bytecode for a single program. +pub struct Bytecode { + pub program_name: String, + pub bytecode: String, +} + /// Helper function to recursively filter keys from AST JSON pub fn remove_key_from_json(value: serde_json::Value, key: &str) -> serde_json::Value { match value { diff --git a/compiler/ast/src/passes/consumer.rs b/compiler/ast/src/passes/consumer.rs index c28e5d61662..1531b1f25d7 100644 --- a/compiler/ast/src/passes/consumer.rs +++ b/compiler/ast/src/passes/consumer.rs @@ -173,6 +173,12 @@ pub trait ProgramConsumer { fn consume_program(&mut self, input: Program) -> Self::Output; } +/// A Consumer trait for a stub in the the AST. +pub trait StubConsumer { + type Output; + fn consume_stub(&mut self, input: Stub) -> Self::Output; +} + /// A Consumer trait for modules in the AST. pub trait ModuleConsumer { type Output; diff --git a/compiler/ast/src/passes/reconstructor.rs b/compiler/ast/src/passes/reconstructor.rs index f4925a4883e..fbdc8172e60 100644 --- a/compiler/ast/src/passes/reconstructor.rs +++ b/compiler/ast/src/passes/reconstructor.rs @@ -560,18 +560,16 @@ pub trait AstReconstructor { /// A Reconstructor trait for the program represented by the AST. pub trait ProgramReconstructor: AstReconstructor { fn reconstruct_program(&mut self, input: Program) -> Program { + let stubs = input.stubs.into_iter().map(|(id, stub)| (id, self.reconstruct_stub(stub))).collect(); let program_scopes = input.program_scopes.into_iter().map(|(id, scope)| (id, self.reconstruct_program_scope(scope))).collect(); - Program { - imports: input.imports, - stubs: input.stubs.into_iter().map(|(id, stub)| (id, self.reconstruct_stub(stub))).collect(), - modules: input.modules.into_iter().map(|(id, module)| (id, self.reconstruct_module(module))).collect(), - program_scopes, - } + let modules = input.modules.into_iter().map(|(id, module)| (id, self.reconstruct_module(module))).collect(); + + Program { modules, imports: input.imports, stubs, program_scopes } } - fn reconstruct_stub(&mut self, input: Stub) -> Stub { - Stub { + fn reconstruct_aleo_program(&mut self, input: AleoProgram) -> AleoProgram { + AleoProgram { imports: input.imports, stub_id: input.stub_id, consts: input.consts, @@ -582,6 +580,15 @@ pub trait ProgramReconstructor: AstReconstructor { } } + fn reconstruct_stub(&mut self, input: Stub) -> Stub { + match input { + Stub::FromLeo { program, parents } => Stub::FromLeo { program: self.reconstruct_program(program), parents }, + Stub::FromAleo { program, parents } => { + Stub::FromAleo { program: self.reconstruct_aleo_program(program), parents } + } + } + } + fn reconstruct_program_scope(&mut self, input: ProgramScope) -> ProgramScope { ProgramScope { program_id: input.program_id, @@ -679,10 +686,6 @@ pub trait ProgramReconstructor: AstReconstructor { } } - fn reconstruct_import(&mut self, input: Program) -> Program { - self.reconstruct_program(input) - } - fn reconstruct_mapping(&mut self, input: Mapping) -> Mapping { Mapping { key_type: self.reconstruct_type(input.key_type).0, diff --git a/compiler/ast/src/passes/visitor.rs b/compiler/ast/src/passes/visitor.rs index 3fc21b6715e..366090ed16a 100644 --- a/compiler/ast/src/passes/visitor.rs +++ b/compiler/ast/src/passes/visitor.rs @@ -326,6 +326,15 @@ pub trait ProgramVisitor: AstVisitor { input.stubs.values().for_each(|stub| self.visit_stub(stub)); } + fn visit_aleo_program(&mut self, _input: &AleoProgram) {} + + fn visit_stub(&mut self, input: &Stub) { + match input { + Stub::FromLeo { program, .. } => self.visit_program(program), + Stub::FromAleo { program, .. } => self.visit_aleo_program(program), + } + } + fn visit_program_scope(&mut self, input: &ProgramScope) { input.consts.iter().for_each(|(_, c)| self.visit_const(c)); input.composites.iter().for_each(|(_, c)| self.visit_composite(c)); @@ -343,12 +352,6 @@ pub trait ProgramVisitor: AstVisitor { input.functions.iter().for_each(|(_, c)| self.visit_function(c)); } - fn visit_stub(&mut self, _input: &Stub) {} - - fn visit_import(&mut self, input: &Program) { - self.visit_program(input) - } - fn visit_composite(&mut self, input: &Composite) { input.const_parameters.iter().for_each(|input| self.visit_type(&input.type_)); input.members.iter().for_each(|member| self.visit_type(&member.type_)); diff --git a/compiler/ast/src/program/program_scope.rs b/compiler/ast/src/program/program_scope.rs index d0f90d56f61..d2ab144f86c 100644 --- a/compiler/ast/src/program/program_scope.rs +++ b/compiler/ast/src/program/program_scope.rs @@ -16,7 +16,7 @@ //! A Leo program scope consists of const, composite, function, and mapping definitions. -use crate::{Composite, ConstDeclaration, Constructor, Function, Indent, Mapping, ProgramId, StorageVariable, Stub}; +use crate::{Composite, ConstDeclaration, Constructor, Function, Indent, Mapping, ProgramId, StorageVariable}; use leo_span::{Span, Symbol}; use serde::{Deserialize, Serialize}; @@ -43,26 +43,6 @@ pub struct ProgramScope { pub span: Span, } -impl From for ProgramScope { - fn from(stub: Stub) -> Self { - Self { - program_id: stub.stub_id, - consts: stub.consts, - composites: stub.composites, - mappings: stub.mappings, - storage_variables: Vec::new(), // stubs don't have storage variables - functions: stub - .functions - .into_iter() - .map(|(symbol, function)| (symbol, Function::from(function))) - .collect(), - // A program scope constructed from a stub does not need a constructor, since they are not externally callable. - constructor: None, - span: stub.span, - } - } -} - impl fmt::Display for ProgramScope { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "program {} {{", self.program_id)?; diff --git a/compiler/ast/src/stub/mod.rs b/compiler/ast/src/stub/mod.rs index ce27d07d0b1..685c607f38b 100644 --- a/compiler/ast/src/stub/mod.rs +++ b/compiler/ast/src/stub/mod.rs @@ -19,14 +19,70 @@ pub mod function_stub; pub use function_stub::*; -use crate::{Composite, ConstDeclaration, Identifier, Indent, Mapping, NodeID, ProgramId}; +use crate::{Composite, ConstDeclaration, Identifier, Indent, Mapping, NodeID, Program, ProgramId}; +use indexmap::IndexSet; use leo_span::{Span, Symbol}; use serde::{Deserialize, Serialize}; use std::fmt; +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +pub enum Stub { + /// A dependency that is a Leo program parsed into an AST. + FromLeo { + program: Program, + parents: IndexSet, // These are the names of all program that import this dependency. + }, + /// A dependency that is an Aleo program. + FromAleo { + program: AleoProgram, + parents: IndexSet, // These are the names of all program that import this dependency. + }, +} + +impl Stub { + /// Returns the programs that this stub imports. + pub fn imports(&self) -> Box + '_> { + match self { + Stub::FromLeo { program, .. } => Box::new(program.imports.keys()), + Stub::FromAleo { program, .. } => Box::new(program.imports.iter().map(|id| &id.name.name)), + } + } + + /// Inserts the given program name as a parent for this stub, implying that the stub is + /// imported by this parent. + pub fn add_parent(&mut self, parent: Symbol) { + match self { + Stub::FromLeo { parents, .. } | Stub::FromAleo { parents, .. } => { + parents.insert(parent); + } + } + } +} + +impl fmt::Display for Stub { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Stub::FromLeo { program, .. } => write!(f, "{program}"), + Stub::FromAleo { program, .. } => write!(f, "{program}"), + } + } +} + +impl From for Stub { + fn from(program: Program) -> Self { + Stub::FromLeo { program, parents: IndexSet::new() } + } +} + +impl From for Stub { + fn from(program: AleoProgram) -> Self { + Stub::FromAleo { program, parents: IndexSet::new() } + } +} + /// Stores the Leo stub abstract syntax tree. #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -pub struct Stub { +pub struct AleoProgram { /// A vector of imported programs. pub imports: Vec, /// The stub id @@ -43,7 +99,7 @@ pub struct Stub { pub span: Span, } -impl Default for Stub { +impl Default for AleoProgram { /// Constructs an empty program stub fn default() -> Self { Self { @@ -61,7 +117,7 @@ impl Default for Stub { } } -impl fmt::Display for Stub { +impl fmt::Display for AleoProgram { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "stub {} {{", self.stub_id)?; for import in self.imports.iter() { diff --git a/compiler/ast/src/types/composite_type.rs b/compiler/ast/src/types/composite.rs similarity index 100% rename from compiler/ast/src/types/composite_type.rs rename to compiler/ast/src/types/composite.rs diff --git a/compiler/ast/src/types/mod.rs b/compiler/ast/src/types/mod.rs index 45d676f29a3..ff947ab8105 100644 --- a/compiler/ast/src/types/mod.rs +++ b/compiler/ast/src/types/mod.rs @@ -17,8 +17,8 @@ mod array; pub use array::*; -mod composite_type; -pub use composite_type::*; +mod composite; +pub use composite::*; mod future; pub use future::*; diff --git a/compiler/ast/src/types/type_.rs b/compiler/ast/src/types/type_.rs index 948abca111f..dba8223c861 100644 --- a/compiler/ast/src/types/type_.rs +++ b/compiler/ast/src/types/type_.rs @@ -34,7 +34,7 @@ use snarkvm::prelude::{ LiteralType, Network, PlaintextType, - PlaintextType::{Array, Literal, Struct}, + PlaintextType::{Array, ExternalStruct, Literal, Struct}, }; use std::fmt; @@ -93,10 +93,13 @@ impl Type { /// if their element types match. This allows const propagation to potentially resolve the length before type /// checking is performed again. /// - /// Composite types are considered equal if their names and resolved program names match. If either side still has - /// const generic arguments, they are treated as equal unconditionally since monomorphization and other passes of - /// type-checking will handle mismatches later. - pub fn eq_user(&self, other: &Type) -> bool { + /// Composite types are considered equal if + /// - They are records and their absolute paths and resolved program names match. + /// - They are structs and their absolute paths match (program names are ignored for now). + /// + /// If either side still has const generic arguments, they are treated as equal unconditionally since + /// monomorphization and other passes of type-checking will handle mismatches later. + pub fn eq_user(&self, other: &Type, current_program: Symbol) -> bool { match (self, other) { (Type::Err, _) | (_, Type::Err) @@ -116,40 +119,50 @@ impl Type { // equal to other arrays because their lengths _may_ eventually be proven equal. true } - }) && left.element_type().eq_user(right.element_type()) + }) && left.element_type().eq_user(right.element_type(), current_program) } (Type::Identifier(left), Type::Identifier(right)) => left.name == right.name, (Type::Integer(left), Type::Integer(right)) => left == right, (Type::Mapping(left), Type::Mapping(right)) => { - left.key.eq_user(&right.key) && left.value.eq_user(&right.value) + left.key.eq_user(&right.key, current_program) && left.value.eq_user(&right.value, current_program) } - (Type::Optional(left), Type::Optional(right)) => left.inner.eq_user(&right.inner), + (Type::Optional(left), Type::Optional(right)) => left.inner.eq_user(&right.inner, current_program), (Type::Tuple(left), Type::Tuple(right)) if left.length() == right.length() => left .elements() .iter() .zip_eq(right.elements().iter()) - .all(|(left_type, right_type)| left_type.eq_user(right_type)), - (Type::Vector(left), Type::Vector(right)) => left.element_type.eq_user(&right.element_type), + .all(|(left_type, right_type)| left_type.eq_user(right_type, current_program)), + (Type::Vector(left), Type::Vector(right)) => { + left.element_type.eq_user(&right.element_type, current_program) + } + (Type::Composite(left), Type::Composite(right)) => { // If either composite still has const generic arguments, treat them as equal. - // Type checking will run again after monomorphization. if !left.const_arguments.is_empty() || !right.const_arguments.is_empty() { return true; } - // Two composite types are the same if their programs and their _absolute_ paths match. - (left.program == right.program) - && match (&left.path.try_absolute_path(), &right.path.try_absolute_path()) { - (Some(l), Some(r)) => l == r, - _ => false, - } + // Get absolute paths, return false immediately if any is None. + let Some(left_path) = left.path.try_absolute_path() else { + return false; + }; + let Some(right_path) = right.path.try_absolute_path() else { + return false; + }; + + // Use current_program if program is `None`. + let left_program = left.program.unwrap_or(current_program); + let right_program = right.program.unwrap_or(current_program); + + left_program == right_program && left_path == right_path } + (Type::Future(left), Type::Future(right)) if !left.is_explicit || !right.is_explicit => true, (Type::Future(left), Type::Future(right)) if left.inputs.len() == right.inputs.len() => left .inputs() .iter() .zip_eq(right.inputs().iter()) - .all(|(left_type, right_type)| left_type.eq_user(right_type)), + .all(|(left_type, right_type)| left_type.eq_user(right_type, current_program)), _ => false, } } @@ -234,6 +247,14 @@ impl Type { const_arguments: Vec::new(), program, }), + ExternalStruct(l) => Type::Composite(CompositeType { + path: { + let ident = Identifier::from(l.name()); + Path::from(ident).with_absolute_path(Some(vec![ident.name])) + }, + const_arguments: Vec::new(), + program: Some(Identifier::from(l.program_id().name()).name), + }), Array(array) => Type::Array(ArrayType::from_snarkvm(array, program)), } } @@ -265,13 +286,19 @@ impl Type { } // A helper function to get the size in bits of the input type. - pub fn size_in_bits(&self, is_raw: bool, get_composite: F) -> anyhow::Result + pub fn size_in_bits( + &self, + is_raw: bool, + get_structs: F0, + get_external_structs: F1, + ) -> anyhow::Result where - F: Fn(&snarkvm::prelude::Identifier) -> anyhow::Result>, + F0: Fn(&snarkvm::prelude::Identifier) -> anyhow::Result>, + F1: Fn(&snarkvm::prelude::Locator) -> anyhow::Result>, { match is_raw { - false => self.to_snarkvm::()?.size_in_bits(&get_composite), - true => self.to_snarkvm::()?.size_in_bits_raw(&get_composite), + false => self.to_snarkvm::()?.size_in_bits(&get_structs, &get_external_structs), + true => self.to_snarkvm::()?.size_in_bits_raw(&get_structs, &get_external_structs), } } @@ -290,15 +317,17 @@ impl Type { /// /// # Returns /// `true` if coercion is allowed; `false` otherwise. - pub fn can_coerce_to(&self, expected: &Type) -> bool { + pub fn can_coerce_to(&self, expected: &Type, current_program: Symbol) -> bool { use Type::*; match (self, expected) { // Allow Optional → Optional - (Optional(actual_opt), Optional(expected_opt)) => actual_opt.inner.can_coerce_to(&expected_opt.inner), + (Optional(actual_opt), Optional(expected_opt)) => { + actual_opt.inner.can_coerce_to(&expected_opt.inner, current_program) + } // Allow T → Optional - (a, Optional(opt)) => a.can_coerce_to(&opt.inner), + (a, Optional(opt)) => a.can_coerce_to(&opt.inner, current_program), // Allow [T; N] → [Optional; N] (Array(a_arr), Array(e_arr)) => { @@ -307,11 +336,11 @@ impl Type { _ => true, }; - lengths_equal && a_arr.element_type().can_coerce_to(e_arr.element_type()) + lengths_equal && a_arr.element_type().can_coerce_to(e_arr.element_type(), current_program) } // Fallback: check for exact match - _ => self.eq_user(expected), + _ => self.eq_user(expected, current_program), } } diff --git a/compiler/compiler/src/compiler.rs b/compiler/compiler/src/compiler.rs index 43445f035d5..33a21a4bd38 100644 --- a/compiler/compiler/src/compiler.rs +++ b/compiler/compiler/src/compiler.rs @@ -20,8 +20,8 @@ use crate::{AstSnapshots, CompilerOptions}; -pub use leo_ast::Ast; -use leo_ast::{NetworkName, Stub}; +pub use leo_ast::{Ast, CompiledPrograms, Program}; +use leo_ast::{NetworkName, NodeBuilder, Stub}; use leo_errors::{CompilerError, Handler, Result}; use leo_passes::*; use leo_span::{Symbol, source_map::FileName, with_session_globals}; @@ -30,6 +30,7 @@ use std::{ ffi::OsStr, fs, path::{Path, PathBuf}, + rc::Rc, }; use indexmap::{IndexMap, IndexSet}; @@ -103,19 +104,39 @@ impl Compiler { Ok(()) } + /// Simple wrapper around `parse` that also returns the AST. + pub fn parse_and_return_ast( + &mut self, + source: &str, + filename: FileName, + modules: &[(&str, FileName)], + ) -> Result { + // Parse the program. + self.parse(source, filename, modules)?; + + Ok(self.state.ast.ast.clone()) + } + /// Returns a new Leo compiler. #[allow(clippy::too_many_arguments)] pub fn new( expected_program_name: Option, is_test: bool, handler: Handler, + node_builder: Rc, output_directory: PathBuf, compiler_options: Option, import_stubs: IndexMap, network: NetworkName, ) -> Self { Self { - state: CompilerState { handler, is_test, network, ..Default::default() }, + state: CompilerState { + handler, + node_builder: Rc::clone(&node_builder), + is_test, + network, + ..Default::default() + }, output_directory, program_name: expected_program_name, compiler_options: compiler_options.unwrap_or_default(), @@ -211,7 +232,12 @@ impl Compiler { /// /// * `Ok(String)` containing the generated bytecode if compilation succeeds. /// * `Err(CompilerError)` if any stage of the pipeline fails. - pub fn compile(&mut self, source: &str, filename: FileName, modules: &Vec<(&str, FileName)>) -> Result { + pub fn compile( + &mut self, + source: &str, + filename: FileName, + modules: &Vec<(&str, FileName)>, + ) -> Result { // Parse the program. self.parse(source, filename, modules)?; // Merge the stubs into the AST. @@ -219,35 +245,28 @@ impl Compiler { // Run the intermediate compiler stages. self.intermediate_passes()?; // Run code generation. - let bytecode = CodeGenerating::do_pass((), &mut self.state)?; - Ok(bytecode.to_string()) + CodeGenerating::do_pass((), &mut self.state) } - /// Compiles a program from a source file and its associated module files in the same directory tree. + /// Reads the main source file and all module files in the same directory tree. /// - /// This method reads the main source file and collects all other source files under the same - /// root directory (excluding the main file itself). It assumes a modular structure where additional - /// source files are compiled as modules, with deeper files (submodules) compiled first. + /// This helper walks all `.leo` files under `source_directory` (excluding the main file itself), + /// reads their contents, and returns: + /// - The main file’s source as a `String`. + /// - A vector of module tuples `(String, FileName)` suitable for compilation or parsing. /// /// # Arguments /// - /// * `source_file_path` - A path to the main source file to compile. It must have a parent directory, - /// which is used as the root for discovering additional module files. - /// - /// # Returns - /// - /// * `Ok(String)` containing the compiled output if successful. - /// * `Err(CompilerError)` if reading the main file fails or a compilation error occurs. + /// * `entry_file_path` - The main source file. + /// * `source_directory` - The directory root for discovering `.leo` module files. /// - /// # Panics + /// # Errors /// - /// * If the provided source file has no parent directory. - /// * If any discovered module file cannot be read (marked as a TODO). - pub fn compile_from_directory( - &mut self, + /// Returns `Err(CompilerError)` if reading any file fails. + fn read_sources_and_modules( entry_file_path: impl AsRef, source_directory: impl AsRef, - ) -> Result { + ) -> Result<(String, Vec<(String, FileName)>)> { // Read the contents of the main source file. let source = fs::read_to_string(&entry_file_path) .map_err(|e| CompilerError::file_read_error(entry_file_path.as_ref().display().to_string(), e))?; @@ -263,24 +282,48 @@ impl Compiler { }) .collect::>(); - let mut module_sources = Vec::new(); // Keep Strings alive for valid borrowing - let mut modules = Vec::new(); // Parsed (source, filename) tuples for compilation - - // Read all module files and store their contents + // Read all module files and pair with FileName immediately + let mut modules = Vec::new(); for file in &files { - let source = fs::read_to_string(file.path()) + let module_source = fs::read_to_string(file.path()) .map_err(|e| CompilerError::file_read_error(file.path().display().to_string(), e))?; - module_sources.push(source); // Keep the String alive + modules.push((module_source, FileName::Real(file.path().into()))); } - // Create tuples of (&str, FileName) for the compiler - for (i, file) in files.iter().enumerate() { - let source = &module_sources[i]; // Borrow from the alive String - modules.push((&source[..], FileName::Real(file.path().into()))); - } + Ok((source, modules)) + } + + /// Compiles a program from a source file and its associated module files in the same directory tree. + pub fn compile_from_directory( + &mut self, + entry_file_path: impl AsRef, + source_directory: impl AsRef, + ) -> Result { + let (source, modules_owned) = Self::read_sources_and_modules(&entry_file_path, &source_directory)?; - // Compile the main source along with all collected modules - self.compile(&source, FileName::Real(entry_file_path.as_ref().into()), &modules) + // Convert owned module sources into temporary (&str, FileName) tuples. + let module_refs: Vec<(&str, FileName)> = + modules_owned.iter().map(|(src, fname)| (src.as_str(), fname.clone())).collect(); + + // Compile the main source along with all collected modules. + self.compile(&source, FileName::Real(entry_file_path.as_ref().into()), &module_refs) + } + + /// Parses a program from a source file and its associated module files in the same directory tree. + pub fn parse_from_directory( + &mut self, + entry_file_path: impl AsRef, + source_directory: impl AsRef, + ) -> Result { + let (source, modules_owned) = Self::read_sources_and_modules(&entry_file_path, &source_directory)?; + + // Convert owned module sources into temporary (&str, FileName) tuples. + let module_refs: Vec<(&str, FileName)> = + modules_owned.iter().map(|(src, fname)| (src.as_str(), fname.clone())).collect(); + + // Parse the main source along with all collected modules. + self.parse(&source, FileName::Real(entry_file_path.as_ref().into()), &module_refs)?; + Ok(self.state.ast.ast.clone()) } /// Writes the AST to a JSON file. @@ -310,27 +353,62 @@ impl Compiler { Ok(()) } - /// Merge the imported stubs which are dependencies of the current program into the AST - /// in topological order. + /// Resolves and registers all import stubs for the current program. + /// + /// This method performs a graph traversal over the program’s import relationships to: + /// 1. Establish parent–child relationships between stubs based on imports. + /// 2. Collect all reachable stubs in traversal order. + /// 3. Store the explored stubs back into `self.state.ast.ast.stubs`. + /// + /// The traversal starts from the imports of the main program and recursively follows + /// their transitive dependencies. Any missing stub during traversal results in an error. + /// + /// # Returns + /// + /// * `Ok(())` if all imports are successfully resolved and stubs are collected. + /// * `Err(CompilerError)` if any imported program cannot be found. pub fn add_import_stubs(&mut self) -> Result<()> { + // Track which programs we've already processed. let mut explored = IndexSet::::new(); + + // Initialize the exploration queue with the main program’s direct imports. let mut to_explore: Vec = self.state.ast.ast.imports.keys().cloned().collect(); - while let Some(import) = to_explore.pop() { - explored.insert(import); - if let Some(stub) = self.import_stubs.get(&import) { - for new_import_id in stub.imports.iter() { - if !explored.contains(&new_import_id.name.name) { - to_explore.push(new_import_id.name.name); - } + // If this is a named program, set the main program as the parent of its direct imports. + if let Some(main_program_name) = self.program_name.clone() { + let main_symbol = Symbol::intern(&main_program_name); + for import in self.state.ast.ast.imports.keys() { + if let Some(child_stub) = self.import_stubs.get_mut(import) { + child_stub.add_parent(main_symbol); } - } else { + } + } + + // Traverse the import graph breadth-first, collecting dependencies. + while let Some(import_symbol) = to_explore.pop() { + // Mark this import as explored. + explored.insert(import_symbol); + + // Look up the corresponding stub. + let Some(stub) = self.import_stubs.get(&import_symbol) else { return Err(CompilerError::imported_program_not_found( self.program_name.as_ref().unwrap(), - import, - self.state.ast.ast.imports[&import], + import_symbol, + self.state.ast.ast.imports[&import_symbol], ) .into()); + }; + + for child_symbol in stub.imports().cloned().collect::>() { + // Record parent relationship. + if let Some(child_stub) = self.import_stubs.get_mut(&child_symbol) { + child_stub.add_parent(import_symbol); + } + + // Schedule child for exploration if not yet visited. + if explored.insert(child_symbol) { + to_explore.push(child_symbol); + } } } diff --git a/compiler/compiler/src/test_compiler.rs b/compiler/compiler/src/test_compiler.rs index 3ad4444188d..04fdb1b5742 100644 --- a/compiler/compiler/src/test_compiler.rs +++ b/compiler/compiler/src/test_compiler.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use leo_disassembler::disassemble_from_str; +use leo_ast::{Bytecode, NodeBuilder}; use leo_errors::{BufferEmitter, Handler, LeoError}; use leo_span::{Symbol, create_session_if_not_set_then}; @@ -26,50 +26,68 @@ use snarkvm::{ use indexmap::IndexMap; use itertools::Itertools as _; use serial_test::serial; -use std::str::FromStr; +use std::{rc::Rc, str::FromStr}; -fn run_test(test: &str, handler: &Handler) -> Result { - // Initialize a `Process`. This should always succeed. +fn run_test(test: &str, handler: &Handler, node_builder: &Rc) -> Result { let mut process = Process::::load().unwrap(); let mut import_stubs = IndexMap::new(); let mut bytecodes = Vec::::new(); - // Compile each source file separately. - for source in test.split(super::test_utils::PROGRAM_DELIMITER) { - let (bytecode, program_name) = - handler.extend_if_error(super::test_utils::whole_compile(source, handler, import_stubs.clone()))?; + let sources: Vec<&str> = test.split(super::test_utils::PROGRAM_DELIMITER).collect(); - // Parse the bytecode as an Aleo program. - // Note that this function checks that the bytecode is well-formed. - let aleo_program = handler.extend_if_error(ProgramCore::from_str(&bytecode).map_err(LeoError::Anyhow))?; + // Helper: parse and register Aleo program into `process`. + // Note that this performs an additional validity check on the bytecode. + let mut add_aleo_program = |code: &str| -> Result<(), ()> { + let program = handler.extend_if_error(ProgramCore::from_str(code).map_err(LeoError::Anyhow))?; + handler.extend_if_error(process.add_program(&program).map_err(LeoError::Anyhow))?; + Ok(()) + }; - // Add the program to the process. - // Note that this function performs an additional validity check on the bytecode. - handler.extend_if_error(process.add_program(&aleo_program).map_err(LeoError::Anyhow))?; + let (last, rest) = sources.split_last().expect("non-empty sources"); - // Add the bytecode to the import stubs. - let stub = handler - .extend_if_error(disassemble_from_str::(&program_name, &bytecode).map_err(|err| err.into()))?; - import_stubs.insert(Symbol::intern(&program_name), stub); + // Parse-only stage for intermediate programs. + for source in rest { + let (program, program_name) = + handler.extend_if_error(super::test_utils::parse(source, handler, node_builder, import_stubs.clone()))?; + + import_stubs.insert(Symbol::intern(&program_name), program.into()); - // Only error out if there are errors. Warnings are okay but we still want to print them later. if handler.err_count() != 0 { return Err(()); } + } + + // Full compile for final program. + let (compiled_programs, _program_name) = + handler.extend_if_error(super::test_utils::whole_compile(last, handler, node_builder, import_stubs.clone()))?; - bytecodes.push(bytecode); + // Only error out if there are errors. Warnings are okay but we still want to print them later. + if handler.err_count() != 0 { + return Err(()); } + // Add imports. + for Bytecode { bytecode, .. } in compiled_programs.import_bytecodes { + add_aleo_program(&bytecode)?; + bytecodes.push(bytecode.clone()); + } + + // Add main program. + let primary_bytecode = compiled_programs.primary_bytecode.clone(); + add_aleo_program(&primary_bytecode)?; + bytecodes.push(primary_bytecode); + Ok(bytecodes.iter().format(&format!("{}\n", super::test_utils::PROGRAM_DELIMITER)).to_string()) } fn runner(source: &str) -> String { let buf = BufferEmitter::new(); let handler = Handler::new(buf.clone()); + let node_builder = Rc::new(NodeBuilder::default()); - create_session_if_not_set_then(|_| match run_test(source, &handler) { + create_session_if_not_set_then(|_| match run_test(source, &handler, &node_builder) { Ok(x) => format!("{}{}", buf.extract_warnings(), x), Err(()) => format!("{}{}", buf.extract_errs(), buf.extract_warnings()), }) diff --git a/compiler/compiler/src/test_execution.rs b/compiler/compiler/src/test_execution.rs index c1eb782ce9c..c27b819e8a6 100644 --- a/compiler/compiler/src/test_execution.rs +++ b/compiler/compiler/src/test_execution.rs @@ -16,18 +16,14 @@ use crate::run; -use leo_disassembler::disassemble_from_str; +use leo_ast::{Bytecode, NodeBuilder}; use leo_errors::{BufferEmitter, Handler, Result}; use leo_span::{Symbol, create_session_if_not_set_then}; -use snarkvm::prelude::TestnetV0; - use indexmap::IndexMap; use itertools::Itertools as _; use serial_test::serial; -use std::fmt::Write as _; - -type CurrentNetwork = TestnetV0; +use std::{fmt::Write as _, rc::Rc}; // Execution test configuration. #[derive(Debug)] @@ -43,24 +39,42 @@ impl Default for Config { } } -fn execution_run_test(config: &Config, cases: &[run::Case], handler: &Handler) -> Result { +fn execution_run_test( + config: &Config, + cases: &[run::Case], + handler: &Handler, + node_builder: &Rc, +) -> Result { let mut import_stubs = IndexMap::new(); let mut ledger_config = run::Config { seed: config.seed, start_height: config.start_height, programs: Vec::new() }; - let mut requires_ledger = false; + // We assume config.sources is non-empty. + let (last, rest) = config.sources.split_last().expect("non-empty sources"); - // Compile each source file. - for source in &config.sources { - let (bytecode, name) = super::test_utils::whole_compile(source, handler, import_stubs.clone())?; - requires_ledger = bytecode.contains("async"); + // Parse-only for intermediate programs. + for source in rest { + let (program, program_name) = super::test_utils::parse(source, handler, node_builder, import_stubs.clone())?; - let stub = disassemble_from_str::(&name, &bytecode)?; - import_stubs.insert(Symbol::intern(&name), stub); + import_stubs.insert(Symbol::intern(&program_name), program.into()); + } - ledger_config.programs.push(run::Program { bytecode, name }); + // Full compile for the final program. + let (compiled_programs, program_name) = + super::test_utils::whole_compile(last, handler, node_builder, import_stubs.clone())?; + + // Add imports. + let mut requires_ledger = false; + for Bytecode { program_name, bytecode } in compiled_programs.import_bytecodes { + requires_ledger |= bytecode.contains("async"); + ledger_config.programs.push(run::Program { bytecode, name: program_name }); } + // Add main program. + let primary_bytecode = compiled_programs.primary_bytecode.clone(); + requires_ledger |= primary_bytecode.contains("async"); + ledger_config.programs.push(run::Program { bytecode: primary_bytecode, name: program_name }); + let mut result = ledger_config .programs .clone() @@ -95,6 +109,7 @@ fn execution_run_test(config: &Config, cases: &[run::Case], handler: &Handler) - fn execution_runner(source: &str) -> String { let buf = BufferEmitter::new(); let handler = Handler::new(buf.clone()); + let node_builder = Rc::new(NodeBuilder::default()); let mut config = Config::default(); let mut cases = Vec::::new(); @@ -124,7 +139,7 @@ fn execution_runner(source: &str) -> String { // Split the sources and add them to the config. config.sources = source.split(super::test_utils::PROGRAM_DELIMITER).map(|s| s.trim().to_string()).collect(); - create_session_if_not_set_then(|_| match execution_run_test(&config, &cases, &handler) { + create_session_if_not_set_then(|_| match execution_run_test(&config, &cases, &handler, &node_builder) { Ok(s) => s, Err(e) => { format!("Error while running execution tests:\n{e}\n\nErrors:\n{}", buf.extract_errs()) diff --git a/compiler/compiler/src/test_utils.rs b/compiler/compiler/src/test_utils.rs index b9c9d5d4918..8fbce714782 100644 --- a/compiler/compiler/src/test_utils.rs +++ b/compiler/compiler/src/test_utils.rs @@ -16,43 +16,104 @@ use crate::Compiler; -use leo_ast::{NetworkName, Stub}; +use leo_ast::{CompiledPrograms, NetworkName, NodeBuilder, Program, Stub}; use leo_errors::{Handler, LeoError}; use leo_span::{Symbol, source_map::FileName}; -use std::path::PathBuf; +use std::{path::PathBuf, rc::Rc}; use indexmap::IndexMap; pub const PROGRAM_DELIMITER: &str = "// --- Next Program --- //"; pub const MODULE_DELIMITER: &str = "// --- Next Module:"; -/// Compiles a complete program from a single source string that may contain -/// embedded modules marked by a delimiter. +/// Fully compiles a Leo source string into bytecode. /// -/// The source string is expected to contain sections separated by the `MODULE_DELIMITER`, -/// each representing either the main source or a named module. The compiler parses each -/// section and compiles the full program, including any modules. +/// This performs the entire compilation pipeline: +/// - splits embedded modules, +/// - initializes a compiler with the given `handler`, `node_builder`, and `import_stubs`, +/// - compiles the main program and its modules, +/// - returns: +/// * `(main_bytecode, imported_bytecodes)` and +/// * the compiled program's name. +/// +/// Used when compiling the final (top-level) program in a test. +#[allow(clippy::type_complexity)] pub fn whole_compile( source: &str, handler: &Handler, + node_builder: &Rc, + import_stubs: IndexMap, +) -> Result<(CompiledPrograms, String), LeoError> { + let (main_source, modules) = split_modules(source); + + let mut compiler = Compiler::new( + None, + /* is_test */ false, + handler.clone(), + node_builder.clone(), + "/fakedirectory-wont-use".into(), + None, + import_stubs, + NetworkName::TestnetV0, + ); + + // Prepare module references + let module_refs: Vec<(&str, FileName)> = + modules.iter().map(|(src, path)| (src.as_str(), FileName::Custom(path.to_string_lossy().into()))).collect(); + + let filename = FileName::Custom("compiler-test".into()); + let bytecodes = compiler.compile(&main_source, filename, &module_refs)?; + + Ok((bytecodes, compiler.program_name.unwrap())) +} + +/// Parses a Leo source string into an AST `Program` without generating bytecode. +/// +/// This runs only the front-end portion of the pipeline: +/// - splits embedded modules, +/// - initializes a compiler with the given `handler`, `node_builder`, and `import_stubs`, +/// - parses the main program and its modules into an AST, +/// - returns the parsed `Program` and the program's name. +/// +/// Used for intermediate programs that are imported by the final one. +pub fn parse( + source: &str, + handler: &Handler, + node_builder: &Rc, import_stubs: IndexMap, -) -> Result<(String, String), LeoError> { +) -> Result<(Program, String), LeoError> { + let (main_source, modules) = split_modules(source); + let mut compiler = Compiler::new( None, /* is_test */ false, handler.clone(), + node_builder.clone(), "/fakedirectory-wont-use".into(), None, import_stubs, NetworkName::TestnetV0, ); + // Prepare module references + let module_refs: Vec<(&str, FileName)> = + modules.iter().map(|(src, path)| (src.as_str(), FileName::Custom(path.to_string_lossy().into()))).collect(); + + let filename = FileName::Custom("compiler-test".into()); + let program = compiler.parse_and_return_ast(&main_source, filename, &module_refs)?; + + Ok((program, compiler.program_name.unwrap())) +} + +/// Splits a single source string into a main source and a list of module +/// `(source, path)` pairs using the MODULE_DELIMITER protocol. +/// +/// Shared by both `whole_compile` and `parse`. +fn split_modules(source: &str) -> (String, Vec<(String, PathBuf)>) { + // Fast path — no modules at all if !source.contains(MODULE_DELIMITER) { - // Fast path: no modules - let filename = FileName::Custom("compiler-test".into()); - let bytecode = compiler.compile(source, filename.clone(), &Vec::new())?; - return Ok((bytecode, compiler.program_name.unwrap())); + return (source.to_string(), Vec::new()); } let mut main_source = String::new(); @@ -88,12 +149,5 @@ pub fn whole_compile( main_source = current_module_source; } - // Prepare module references for compiler - let module_refs: Vec<(&str, FileName)> = - modules.iter().map(|(src, path)| (src.as_str(), FileName::Custom(path.to_string_lossy().into()))).collect(); - - let filename = FileName::Custom("compiler-test".into()); - let bytecode = compiler.compile(&main_source, filename, &module_refs)?; - - Ok((bytecode, compiler.program_name.unwrap())) + (main_source, modules) } diff --git a/compiler/parser-lossless/src/grammar.lalrpop b/compiler/parser-lossless/src/grammar.lalrpop index 342b77d273d..5f4b1c6e57b 100644 --- a/compiler/parser-lossless/src/grammar.lalrpop +++ b/compiler/parser-lossless/src/grammar.lalrpop @@ -195,9 +195,9 @@ ExprStruct: SyntaxNode<'a> = { // Struct initializer. > > => { // For now the initializer name can't be external. - if name.text.contains(".aleo") { + /*if name.text.contains(".aleo") { handler.emit_err(ParserError::cannot_define_external_record(name.span)); - } + }*/ SyntaxNode::new(ExpressionKind::Struct, iter::once(name).chain(c).chain([l]).chain(inits).chain([r])) }, } diff --git a/compiler/parser/src/conversions.rs b/compiler/parser/src/conversions.rs index ac0cfd70645..539a87bd0b7 100644 --- a/compiler/parser/src/conversions.rs +++ b/compiler/parser/src/conversions.rs @@ -985,11 +985,36 @@ impl<'a> ConversionContext<'a> { } } - let mut identifiers = self.path_to_parts(name); - let identifier = identifiers.pop().unwrap(); - let path = leo_ast::Path::new(identifiers, identifier, false, None, name.span, self.builder.next_id()); + if let Some((program, name_str)) = name.text.split_once(".aleo/") { + // This is a locator. + let name_id = leo_ast::Identifier { + name: Symbol::intern(name_str), + span: leo_span::Span { + lo: name.span.lo + program.len() as u32 + 5, + hi: name.span.lo + name.text.len() as u32, + }, + id: self.builder.next_id(), + }; - leo_ast::CompositeExpression { path, const_arguments, members, span, id }.into() + let path = + leo_ast::Path::new(Vec::new(), name_id, false, None, name_id.span, self.builder.next_id()); + leo_ast::CompositeExpression { + path, + const_arguments, + members, + program: Some(Symbol::intern(program)), + span, + id, + } + .into() + } else { + let mut identifiers = self.path_to_parts(name); + let identifier = identifiers.pop().unwrap(); + let path = + leo_ast::Path::new(identifiers, identifier, false, None, name.span, self.builder.next_id()); + + leo_ast::CompositeExpression { path, const_arguments, members, program: None, span, id }.into() + } } ExpressionKind::Ternary => { let [cond, _q, if_, _c, then] = &node.children[..] else { diff --git a/compiler/passes/src/code_generation/expression.rs b/compiler/passes/src/code_generation/expression.rs index 1d93a32743a..83708e7e36c 100644 --- a/compiler/passes/src/code_generation/expression.rs +++ b/compiler/passes/src/code_generation/expression.rs @@ -242,7 +242,7 @@ impl CodeGeneratingVisitor<'_> { // Construct the destination register. let dest_reg = self.next_register(); - let cast_instruction = AleoStmt::Cast(operand, dest_reg.clone(), Self::visit_type(&input.type_)); + let cast_instruction = AleoStmt::Cast(operand, dest_reg.clone(), self.visit_type(&input.type_)); // Concatenate the instructions. instructions.push(cast_instruction); @@ -269,7 +269,7 @@ impl CodeGeneratingVisitor<'_> { let Some(array_type @ Type::Array(..)) = self.state.type_table.get(&input.id) else { panic!("All types should be known at this phase of compilation"); }; - let array_type: AleoType = Self::visit_type(&array_type); + let array_type: AleoType = self.visit_type(&array_type); let array_instruction = AleoStmt::Cast(AleoExpr::Tuple(operands), destination_register.clone(), array_type); @@ -328,7 +328,10 @@ impl CodeGeneratingVisitor<'_> { fn visit_composite_init(&mut self, input: &CompositeExpression) -> (AleoExpr, Vec) { // Lookup struct or record. - let composite_type = if let Some(is_record) = self.composite_mapping.get(&input.path.absolute_path()) { + let this_program_name = self.program_id.unwrap().name.name; + let program = input.program.unwrap_or(this_program_name); + let composite_type = if let Some(is_record) = self.composite_mapping.get(&(program, input.path.absolute_path())) + { if *is_record { // record.private; let [record_name] = &input.path.absolute_path()[..] else { @@ -337,9 +340,12 @@ impl CodeGeneratingVisitor<'_> { AleoType::Record { name: record_name.to_string(), program: None } } else { // foo; // no visibility for structs - AleoType::Ident { - name: Self::legalize_path(&input.path.absolute_path()) - .expect("path format cannot be legalized at this point"), + let struct_name = Self::legalize_path(&input.path.absolute_path()) + .expect("path format cannot be legalized at this point"); + if program == this_program_name { + AleoType::Ident { name: struct_name.to_string() } + } else { + AleoType::Location { program: program.to_string(), name: struct_name.to_string() } } } } else { @@ -418,7 +424,7 @@ impl CodeGeneratingVisitor<'_> { let Some(array_type @ Type::Array(..)) = self.state.type_table.get(&input.id) else { panic!("All types should be known at this phase of compilation"); }; - let array_type = Self::visit_type(&array_type); + let array_type = self.visit_type(&array_type); let array_instruction = AleoStmt::Cast(AleoExpr::Tuple(expression_operands), dest_reg.clone(), array_type); @@ -460,7 +466,7 @@ impl CodeGeneratingVisitor<'_> { let func_symbol = self .state .symbol_table - .lookup_function(&Location::new(callee_program, input.function.absolute_path().to_vec())) + .lookup_function(caller_program, &Location::new(callee_program, input.function.absolute_path().to_vec())) .expect("Type checking guarantees functions exist"); let mut instructions = vec![]; @@ -687,20 +693,31 @@ impl CodeGeneratingVisitor<'_> { // Get the instruction variant. let is_raw = matches!(variant, SerializeVariant::ToBitsRaw); // Get the size in bits of the input type. - let size_in_bits = - match self.state.network { - NetworkName::TestnetV0 => input_type - .size_in_bits::(is_raw, |_| bail!("composites are not supported")), - NetworkName::MainnetV0 => input_type - .size_in_bits::(is_raw, |_| bail!("composites are not supported")), - NetworkName::CanaryV0 => input_type - .size_in_bits::(is_raw, |_| bail!("composites are not supported")), - } - .expect("TYC guarantees that all types have a valid size in bits"); + fn struct_not_supported(_: &T) -> anyhow::Result { + bail!("structs are not supported") + } + let size_in_bits = match self.state.network { + NetworkName::TestnetV0 => input_type.size_in_bits::( + is_raw, + &struct_not_supported, + &struct_not_supported, + ), + NetworkName::MainnetV0 => input_type.size_in_bits::( + is_raw, + &struct_not_supported, + &struct_not_supported, + ), + NetworkName::CanaryV0 => input_type.size_in_bits::( + is_raw, + &struct_not_supported, + &struct_not_supported, + ), + } + .expect("TYC guarantees that all types have a valid size in bits"); let dest_reg = self.next_register(); let output_type = AleoType::Array { inner: Box::new(AleoType::Boolean), len: size_in_bits as u32 }; - let input_type = Self::visit_type(&input_type); + let input_type = self.visit_type(&input_type); let instruction = AleoStmt::Serialize(variant, args[0].clone(), input_type, dest_reg.clone(), output_type); @@ -713,8 +730,8 @@ impl CodeGeneratingVisitor<'_> { }; let dest_reg = self.next_register(); - let input_type = Self::visit_type(&input_type); - let output_type = Self::visit_type(&output_type); + let input_type = self.visit_type(&input_type); + let output_type = self.visit_type(&output_type); let instruction = AleoStmt::Deserialize(variant, args[0].clone(), input_type, dest_reg.clone(), output_type); @@ -789,19 +806,24 @@ impl CodeGeneratingVisitor<'_> { .map(|i| AleoExpr::ArrayAccess(Box::new(register.clone()), Box::new(AleoExpr::U32(i)))) .collect::>(); - let ins = AleoStmt::Cast(AleoExpr::Tuple(elems), new_reg.clone(), Self::visit_type(typ)); + let ins = AleoStmt::Cast(AleoExpr::Tuple(elems), new_reg.clone(), self.visit_type(typ)); ((AleoExpr::Reg(new_reg)), vec![ins]) } Type::Composite(comp_ty) => { + let current_program = self.program_id.unwrap().name.name; // We need to cast the old struct or record's members into the new one. - let program = comp_ty.program.unwrap_or(self.program_id.unwrap().name.name); + let program = comp_ty.program.unwrap_or(current_program); let location = Location::new(program, comp_ty.path.absolute_path().to_vec()); let comp = self .state .symbol_table - .lookup_record(&location) - .or_else(|| self.state.symbol_table.lookup_struct(&comp_ty.path.absolute_path())) + .lookup_record(current_program, &location) + .or_else(|| { + self.state + .symbol_table + .lookup_struct(current_program, &Location::new(program, comp_ty.path.absolute_path())) + }) .unwrap(); let elems = comp .members diff --git a/compiler/passes/src/code_generation/mod.rs b/compiler/passes/src/code_generation/mod.rs index 625b80b74ab..264b6c288c8 100644 --- a/compiler/passes/src/code_generation/mod.rs +++ b/compiler/passes/src/code_generation/mod.rs @@ -14,14 +14,14 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use std::fmt::Display; - use crate::Pass; use itertools::Itertools; -use leo_ast::{Mode, ProgramId}; +use leo_ast::{CompiledPrograms, Mode, ProgramId}; use leo_errors::Result; +use std::fmt::Display; + mod expression; mod program; @@ -41,7 +41,7 @@ pub struct CodeGenerating; impl Pass for CodeGenerating { type Input = (); - type Output = AleoProgram; + type Output = CompiledPrograms; const NAME: &str = "CodeGenerating"; @@ -61,8 +61,8 @@ impl Pass for CodeGenerating { conditional_depth: 0, internal_record_inputs: Default::default(), }; - let code = visitor.visit_program(visitor.state.ast.as_repr()); - Ok(code) + + Ok(visitor.visit_package()) } } @@ -557,6 +557,7 @@ pub enum AleoType { Future { name: String, program: String }, Record { name: String, program: Option }, Ident { name: String }, + Location { program: String, name: String }, Array { inner: Box, len: u32 }, GroupX, GroupY, @@ -587,6 +588,7 @@ impl Display for AleoType { Self::GroupX => write!(f, "group.x"), Self::GroupY => write!(f, "group.y"), Self::Ident { name } => write!(f, "{name}"), + Self::Location { program, name } => write!(f, "{program}.aleo/{name}"), Self::Address => write!(f, "address"), Self::Boolean => write!(f, "boolean"), Self::Field => write!(f, "field"), @@ -613,6 +615,9 @@ impl From> for AleoType { match value { PlaintextType::Literal(lit) => lit.into(), PlaintextType::Struct(id) => Self::Ident { name: id.to_string() }, + PlaintextType::ExternalStruct(loc) => { + Self::Location { program: loc.program_id().to_string(), name: loc.name().to_string() } + } PlaintextType::Array(arr) => arr.into(), } } diff --git a/compiler/passes/src/code_generation/program.rs b/compiler/passes/src/code_generation/program.rs index 9ae5fd8d6a1..311a841466c 100644 --- a/compiler/passes/src/code_generation/program.rs +++ b/compiler/passes/src/code_generation/program.rs @@ -56,17 +56,21 @@ impl<'a> CodeGeneratingVisitor<'a> { let this_program = self.program_id.unwrap().name.name; - let lookup = |name: &[Symbol]| { - self.state - .symbol_table - .lookup_struct(name) - .or_else(|| self.state.symbol_table.lookup_record(&Location::new(this_program, name.to_vec()))) + let lookup = |loc: &Location| { + if loc.program == this_program { + self.state + .symbol_table + .lookup_struct(this_program, loc) + .or_else(|| self.state.symbol_table.lookup_record(this_program, loc)) + } else { + None + } }; // Add each `struct` or `record` in the post-ordering and produce an Aleo struct or record. let data_types = order .into_iter() - .filter_map(|name| lookup(&name).map(|composite| self.visit_struct_or_record(composite, &name))) + .filter_map(|loc| lookup(&loc).map(|composite| self.visit_struct_or_record(composite, &loc.path))) .collect(); // Visit each mapping in the Leo AST and produce an Aleo mapping declaration. @@ -90,10 +94,13 @@ impl<'a> CodeGeneratingVisitor<'a> { let finalize = &self .state .symbol_table - .lookup_function(&Location::new( - self.program_id.unwrap().name.name, - vec![function.identifier.name], // Guaranteed to live in program scope, not in any submodule - )) + .lookup_function( + this_program, + &Location::new( + this_program, + vec![function.identifier.name], // Guaranteed to live in program scope, not in any submodule + ), + ) .unwrap() .clone() .finalizer @@ -138,7 +145,7 @@ impl<'a> CodeGeneratingVisitor<'a> { fn visit_struct(&mut self, struct_: &'a Composite, absolute_path: &[Symbol]) -> AleoStruct { // Add private symbol to composite types. - self.composite_mapping.insert(absolute_path.to_vec(), false); // todo: private by default here. + self.composite_mapping.insert((self.program_id.unwrap().name.name, absolute_path.to_vec()), false); // todo: private by default here. // todo: check if this is safe from name conflicts. let name = Self::legalize_path(absolute_path).unwrap_or_else(|| { @@ -153,7 +160,7 @@ impl<'a> CodeGeneratingVisitor<'a> { if var.type_.is_empty() { None } else { - Some((var.identifier.to_string(), Self::visit_type(&var.type_))) + Some((var.identifier.to_string(), self.visit_type(&var.type_))) } }) .collect(); @@ -163,7 +170,7 @@ impl<'a> CodeGeneratingVisitor<'a> { fn visit_record(&mut self, record: &'a Composite, absolute_path: &[Symbol]) -> AleoRecord { // Add record symbol to composite types. - self.composite_mapping.insert(absolute_path.to_vec(), true); + self.composite_mapping.insert((self.program_id.unwrap().name.name, absolute_path.to_vec()), true); let name = record.identifier.to_string(); // todo: check if this is safe from name conflicts. @@ -185,7 +192,7 @@ impl<'a> CodeGeneratingVisitor<'a> { if var.type_.is_empty() { None } else { - Some((var.identifier.to_string(), Self::visit_type(&var.type_), match var.mode { + Some((var.identifier.to_string(), self.visit_type(&var.type_), match var.mode { Mode::Constant => AleoVisibility::Constant, Mode::Public => AleoVisibility::Public, Mode::None | Mode::Private => AleoVisibility::Private, @@ -236,12 +243,12 @@ impl<'a> CodeGeneratingVisitor<'a> { // Track all internal record inputs. if let Type::Composite(comp) = &input.type_ { - let program = comp.program.unwrap_or(self.program_id.unwrap().name.name); - if let Some(record) = self - .state - .symbol_table - .lookup_record(&Location::new(program, comp.path.absolute_path().to_vec())) - && (record.external.is_none() || record.external == self.program_id.map(|id| id.name.name)) + let current_program = self.program_id.unwrap().name.name; + let program = comp.program.unwrap_or(current_program); + + let path = Location::new(program, comp.path.absolute_path().to_vec()); + if program == current_program + && self.state.symbol_table.lookup_record(current_program, &path).is_some() { self.internal_record_inputs.insert(AleoExpr::Reg(register_num.clone())); } @@ -398,8 +405,11 @@ impl<'a> CodeGeneratingVisitor<'a> { Type::Mapping(_) | Type::Tuple(_) => panic!("Mappings cannot contain mappings or tuples."), Type::Identifier(identifier) => { // Lookup the type in the composite mapping. - // Note that this unwrap is safe since all structs and records have been added to the composite mapping. - let is_record = self.composite_mapping.get(&vec![identifier.name]).unwrap(); + // Note that this unwrap is safe since all struct and records have been added to the composite mapping. + let is_record = self + .composite_mapping + .get(&(self.program_id.unwrap().name.name, vec![identifier.name])) + .unwrap(); assert!(!is_record, "Type checking guarantees that mappings cannot contain records."); self.visit_type_with_visibility(type_, Some(AleoVisibility::Public)) } diff --git a/compiler/passes/src/code_generation/type_.rs b/compiler/passes/src/code_generation/type_.rs index f75ab3e4e26..22d67ff4a64 100644 --- a/compiler/passes/src/code_generation/type_.rs +++ b/compiler/passes/src/code_generation/type_.rs @@ -16,10 +16,10 @@ use super::*; -use leo_ast::{CompositeType, IntegerType, Location, Type}; +use leo_ast::{IntegerType, Location, Type}; impl CodeGeneratingVisitor<'_> { - pub fn visit_type(input: &Type) -> AleoType { + pub fn visit_type(&self, input: &Type) -> AleoType { match input { Type::Address => AleoType::Address, Type::Field => AleoType::Field, @@ -41,13 +41,32 @@ impl CodeGeneratingVisitor<'_> { IntegerType::I128 => AleoType::I128, }, Type::Identifier(id) => AleoType::Ident { name: id.to_string() }, - Type::Composite(CompositeType { path, .. }) => AleoType::Ident { - name: Self::legalize_path(&path.absolute_path()) - .expect("path format cannot be legalized at this point"), - }, + Type::Composite(composite) => { + let name = composite.path.absolute_path(); + let this_program_name = self.program_id.unwrap().name.name; + let program_name = composite.program.unwrap_or(this_program_name); + + if self + .state + .symbol_table + .lookup_struct(this_program_name, &Location::new(program_name, name.to_vec())) + .is_some() + { + let struct_name = + Self::legalize_path(&name).expect("path format cannot be legalized at this point"); + if program_name == this_program_name { + AleoType::Ident { name: struct_name.to_string() } + } else { + AleoType::Location { program: program_name.to_string(), name: struct_name.to_string() } + } + } else { + panic!("") + } + } + Type::Boolean => AleoType::Boolean, Type::Array(array_type) => AleoType::Array { - inner: Box::new(Self::visit_type(array_type.element_type())), + inner: Box::new(self.visit_type(array_type.element_type())), len: array_type.length.as_u32().expect("length should be known at this point"), }, Type::Future(..) => { @@ -81,7 +100,12 @@ impl CodeGeneratingVisitor<'_> { let name = composite.path.absolute_path(); let this_program_name = self.program_id.unwrap().name.name; let program_name = composite.program.unwrap_or(this_program_name); - if self.state.symbol_table.lookup_record(&Location::new(program_name, name.to_vec())).is_some() { + if self + .state + .symbol_table + .lookup_record(this_program_name, &Location::new(program_name, name.to_vec())) + .is_some() + { let [record_name] = &name[..] else { panic!("Absolute paths to records can only have a single segment at this stage.") }; @@ -96,6 +120,6 @@ impl CodeGeneratingVisitor<'_> { } } - (Self::visit_type(type_), visibility) + (self.visit_type(type_), visibility) } } diff --git a/compiler/passes/src/code_generation/visitor.rs b/compiler/passes/src/code_generation/visitor.rs index 5e7ae2b4a16..84c9ab52185 100644 --- a/compiler/passes/src/code_generation/visitor.rs +++ b/compiler/passes/src/code_generation/visitor.rs @@ -16,7 +16,7 @@ use crate::{AleoConstructor, AleoExpr, AleoReg, CompilerState}; -use leo_ast::{Function, Program, ProgramId, Variant}; +use leo_ast::{Bytecode, CompiledPrograms, Function, Program, ProgramId, Variant}; use leo_span::Symbol; use snarkvm::prelude::Network; @@ -34,9 +34,9 @@ pub struct CodeGeneratingVisitor<'a> { /// Mapping of local variables to registers. /// Because these are local, we can identify them using only a `Symbol` (i.e. a path is not necessary here). pub variable_mapping: IndexMap, - /// Mapping of composite names to a tuple containing metadata associated with the name. - /// The first element of the tuple indicate whether the composite is a record or not. - pub composite_mapping: IndexMap, bool>, + /// Mapping of composites to a Boolean indicate whether the composite is a record or not. + /// A composite here is identified by its program and its path within that program. + pub composite_mapping: IndexMap<(Symbol, Vec), bool>, /// Mapping of global identifiers to their associated names. /// Because we only allow mappings in the top level program scope at this stage, we can identify them using only a /// `Symbol` (i.e. a path is not necessary here currently). @@ -71,6 +71,53 @@ pub(crate) fn check_snarkvm_constructor(actual: &AleoConstructor) -> } impl CodeGeneratingVisitor<'_> { + pub(crate) fn visit_package(&mut self) -> CompiledPrograms { + let import_bytecodes = self + .state + .ast + .as_repr() + .stubs + .values() + .filter_map(|stub| { + if let leo_ast::Stub::FromLeo { program, .. } = stub { + let program_name = program + .program_scopes + .first() + .expect("programs must have a single program scope at this time.") + .0; + + // Get transitive imports for this program + let transitive_imports = self + .state + .symbol_table + .get_transitive_imports(program_name) + .into_iter() + .map(|sym| sym.to_string()) + .collect::>(); + + // Generate Aleo imports for those dependencies + let import_section = if transitive_imports.is_empty() { + String::new() + } else { + transitive_imports.iter().map(|name| format!("import {name}.aleo;\n")).collect::() + }; + + // Generate this stub’s Aleo program text + let mut bytecode = self.visit_program(program).to_string(); + bytecode = format!("{import_section}{bytecode}"); + + Some(Bytecode { program_name: program_name.to_string(), bytecode }) + } else { + None + } + }) + .collect(); + + let primary_bytecode = self.visit_program(self.state.ast.as_repr()).to_string(); + + CompiledPrograms { primary_bytecode, import_bytecodes } + } + pub(crate) fn next_register(&mut self) -> AleoReg { self.next_register += 1; AleoReg::R(self.next_register - 1) diff --git a/compiler/passes/src/common/block_to_function_rewriter/mod.rs b/compiler/passes/src/common/block_to_function_rewriter/mod.rs new file mode 100644 index 00000000000..3298562589d --- /dev/null +++ b/compiler/passes/src/common/block_to_function_rewriter/mod.rs @@ -0,0 +1,336 @@ +// Copyright (C) 2019-2025 Provable Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +/// Transforms a captured `Block` into a standalone async `Function` plus a +/// corresponding call expression. +/// +/// This pass analyzes symbol accesses inside the block, determines which +/// variables must become parameters, and synthesizes the necessary `Input`s +/// and call-site arguments. Tuple and tuple-field accesses are normalized so +/// that each accessed element becomes a unique parameter, with full-tuple +/// reconstruction when needed. +/// +/// The original block is then reconstructed with all symbol references +/// replaced by these synthesized parameters. The result is a function +/// that encapsulates the block's logic and a call expression that invokes it. +/// +/// # Example +/// ```leo +/// // Original block +/// let a: i32 = 1; +/// let b: i32 = 2; +/// let c: (i32, i32) = (3, 4); +/// { +/// let x = a + b; +/// let y = c.0 + c.1; +/// .. +/// } +/// +/// // Rewritten as a function + call expression (assume `variant` is `AsyncFunction` here) +/// async function generated_async(a: i32, b: i32, "c.0": i32, "c.1": i32) { +/// let x = a + b; +/// let y = "c.0" + "c.1"; +/// .. +/// } +/// +/// // Call +/// generated_async(a, b, c.0, c.1); +/// ``` +use crate::{CompilerState, Replacer, SymbolAccessCollector}; + +use leo_ast::{ + AstReconstructor, + AstVisitor, + Block, + CallExpression, + Expression, + Function, + Identifier, + Input, + Location, + Node, + Path, + TupleAccess, + TupleExpression, + TupleType, + Type, + Variant, +}; +use leo_span::{Span, Symbol}; + +use indexmap::IndexMap; + +pub struct BlockToFunctionRewriter<'a> { + state: &'a mut CompilerState, + current_program: Symbol, +} + +impl<'a> BlockToFunctionRewriter<'a> { + pub fn new(state: &'a mut CompilerState, current_program: Symbol) -> Self { + Self { state, current_program } + } +} + +impl BlockToFunctionRewriter<'_> { + pub fn rewrite_block( + &mut self, + input: &Block, + function_name: Symbol, + function_variant: Variant, + ) -> (Function, Expression) { + // Collect all symbol accesses in the block. + let mut access_collector = SymbolAccessCollector::new(self.state); + access_collector.visit_block(input); + + // Stores mapping from accessed symbol (and optional index) to the expression used in replacement. + let mut replacements: IndexMap<(Symbol, Option), Expression> = IndexMap::new(); + + // Helper to create a fresh `Identifier`. + let make_identifier = |slf: &mut Self, symbol: Symbol| Identifier { + name: symbol, + span: Span::default(), + id: slf.state.node_builder.next_id(), + }; + + // Generates a set of `Input`s and corresponding call-site `Expression`s for a given symbol access. + // + // This function handles both: + // - Direct variable accesses (e.g., `foo`) + // - Tuple element accesses (e.g., `foo.0`) + // + // For tuple accesses: + // - If a single element (e.g. `foo.0`) is accessed, it generates a synthetic input like `"foo.0"`. + // - If the whole tuple (e.g. `foo`) is accessed, it ensures all elements are covered by: + // - Reusing existing inputs from `replacements` if already generated via prior field access. + // - Creating new inputs and arguments for any missing elements. + // - The entire tuple is reconstructed in `replacements` using the individual elements as a `TupleExpression`. + // + // This function also ensures deduplication by consulting the `replacements` map: + // - If a given `(symbol, index)` has already been processed, no duplicate input or argument is generated. + // - This prevents repeated parameters for accesses like both `foo` and `foo.0`. + // + // # Parameters + // - `symbol`: The symbol being accessed. + // - `var_type`: The type of the symbol (may be a tuple or base type). + // - `index_opt`: `Some(index)` for a tuple field (e.g., `.0`), or `None` for full-variable access. + // + // # Returns + // A `Vec<(Input, Expression)>`, where: + // - `Input` is a parameter for the generated function. + // - `Expression` is the call-site argument expression used to invoke that parameter. + let mut make_inputs_and_arguments = + |slf: &mut Self, symbol: Symbol, var_type: &Type, index_opt: Option| -> Vec<(Input, Expression)> { + if replacements.contains_key(&(symbol, index_opt)) { + return vec![]; // No new input needed; argument already exists + } + + match index_opt { + Some(index) => { + let Type::Tuple(TupleType { elements }) = var_type else { + panic!("Expected tuple type when accessing tuple field: {symbol}.{index}"); + }; + + let synthetic_name = format!("\"{symbol}.{index}\""); + let synthetic_symbol = Symbol::intern(&synthetic_name); + let identifier = make_identifier(slf, synthetic_symbol); + + let input = Input { + identifier, + mode: leo_ast::Mode::None, + type_: elements[index].clone(), + span: Span::default(), + id: slf.state.node_builder.next_id(), + }; + + replacements.insert((symbol, Some(index)), Path::from(identifier).into_absolute().into()); + + vec![( + input, + TupleAccess { + tuple: Path::from(make_identifier(slf, symbol)).into_absolute().into(), + index: index.into(), + span: Span::default(), + id: slf.state.node_builder.next_id(), + } + .into(), + )] + } + + None => match var_type { + Type::Tuple(TupleType { elements }) => { + let mut inputs_and_arguments = Vec::with_capacity(elements.len()); + let mut tuple_elements = Vec::with_capacity(elements.len()); + + for (i, element_type) in elements.iter().enumerate() { + let key = (symbol, Some(i)); + + // Skip if this field is already handled + if let Some(existing_expr) = replacements.get(&key) { + tuple_elements.push(existing_expr.clone()); + continue; + } + + // Otherwise, synthesize identifier and input + let synthetic_name = format!("\"{symbol}.{i}\""); + let synthetic_symbol = Symbol::intern(&synthetic_name); + let identifier = make_identifier(slf, synthetic_symbol); + + let input = Input { + identifier, + mode: leo_ast::Mode::None, + type_: element_type.clone(), + span: Span::default(), + id: slf.state.node_builder.next_id(), + }; + + let expr: Expression = Path::from(identifier).into_absolute().into(); + + replacements.insert(key, expr.clone()); + tuple_elements.push(expr.clone()); + inputs_and_arguments.push(( + input, + TupleAccess { + tuple: Path::from(make_identifier(slf, symbol)).into_absolute().into(), + index: i.into(), + span: Span::default(), + id: slf.state.node_builder.next_id(), + } + .into(), + )); + } + + // Now insert the full tuple (even if all fields were already there). + replacements.insert( + (symbol, None), + Expression::Tuple(TupleExpression { + elements: tuple_elements, + span: Span::default(), + id: slf.state.node_builder.next_id(), + }), + ); + + inputs_and_arguments + } + + _ => { + let identifier = make_identifier(slf, symbol); + let input = Input { + identifier, + mode: leo_ast::Mode::None, + type_: var_type.clone(), + span: Span::default(), + id: slf.state.node_builder.next_id(), + }; + + replacements.insert((symbol, None), Path::from(identifier).into_absolute().into()); + + let argument = Path::from(make_identifier(slf, symbol)).into_absolute().into(); + vec![(input, argument)] + } + }, + } + }; + + // Resolve symbol accesses into inputs and call arguments. + let (inputs, arguments): (Vec<_>, Vec<_>) = access_collector + .symbol_accesses + .iter() + .filter_map(|(path, index)| { + // Skip globals and variables that are local to this block or to one of its children. + + // Skip globals. + if self + .state + .symbol_table + .lookup_global(self.current_program, &Location::new(self.current_program, path.to_vec())) + .is_some() + { + return None; + } + + // Skip variables that are local to this block or to one of its children. + let local_var_name = *path.last().expect("all paths must have at least one segment."); + if self.state.symbol_table.is_local_to_or_in_child_scope(input.id(), local_var_name) { + return None; + } + + // All other variables become parameters to the function being built. + let var = self.state.symbol_table.lookup_local(local_var_name)?; + Some(make_inputs_and_arguments(self, local_var_name, &var.type_, *index)) + }) + .flatten() + .unzip(); + + // Replacement logic used to patch the block. + let replace_expr = |expr: &Expression| -> Expression { + match expr { + Expression::Path(path) => { + replacements.get(&(path.identifier().name, None)).cloned().unwrap_or_else(|| expr.clone()) + } + + Expression::TupleAccess(ta) => { + if let Expression::Path(path) = &ta.tuple { + replacements + .get(&(path.identifier().name, Some(ta.index.value()))) + .cloned() + .unwrap_or_else(|| expr.clone()) + } else { + expr.clone() + } + } + + _ => expr.clone(), + } + }; + + // Reconstruct the block with replaced references. + let mut replacer = Replacer::new(replace_expr, true /* refresh IDs */, self.state); + let new_block = replacer.reconstruct_block(input.clone()).0; + + // Define the new function. + let function = Function { + annotations: vec![], + variant: function_variant, + identifier: make_identifier(self, function_name), + const_parameters: vec![], + input: inputs, + output: vec![], // No returns supported yet. + output_type: Type::Unit, // No returns supported yet. + block: new_block, + span: input.span, + id: self.state.node_builder.next_id(), + }; + + // Create the call expression to invoke the function. + let call_to_function = CallExpression { + function: Path::new( + vec![], + make_identifier(self, function_name), + true, + Some(vec![function_name]), // the new function lives in the top level program scope for now. + Span::default(), + self.state.node_builder.next_id(), + ), + const_arguments: vec![], + arguments, + program: Some(self.current_program), + span: input.span, + id: self.state.node_builder.next_id(), + }; + + (function, call_to_function.into()) + } +} diff --git a/compiler/passes/src/common/mod.rs b/compiler/passes/src/common/mod.rs index 9d23fee0da2..0bfa010b9b4 100644 --- a/compiler/passes/src/common/mod.rs +++ b/compiler/passes/src/common/mod.rs @@ -17,12 +17,18 @@ mod assigner; pub use assigner::*; +mod block_to_function_rewriter; +pub use block_to_function_rewriter::*; + mod rename_table; pub use rename_table::*; mod replacer; pub use replacer::*; +mod symbol_access_collector; +pub use symbol_access_collector::*; + mod symbol_table; pub use symbol_table::*; diff --git a/compiler/passes/src/common/symbol_access_collector/mod.rs b/compiler/passes/src/common/symbol_access_collector/mod.rs new file mode 100644 index 00000000000..26c2cd07566 --- /dev/null +++ b/compiler/passes/src/common/symbol_access_collector/mod.rs @@ -0,0 +1,64 @@ +// Copyright (C) 2019-2025 Provable Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use crate::CompilerState; + +use leo_ast::{AstVisitor, Expression, Node as _, Path, ProgramVisitor, TupleAccess, Type}; +use leo_span::Symbol; + +use indexmap::IndexSet; + +/// Collects all symbol accesses within an async block, +/// including both direct variable identifiers (`x`) and tuple field accesses (`x.0`, `x.1`, etc.). +/// Each access is recorded as a pair: (Symbol, Option). +/// - `None` means a direct variable access. +/// - `Some(index)` means a tuple field access. +pub struct SymbolAccessCollector<'a> { + state: &'a CompilerState, + pub symbol_accesses: IndexSet<(Vec, Option)>, +} + +impl<'a> SymbolAccessCollector<'a> { + pub fn new(state: &'a mut CompilerState) -> Self { + Self { state, symbol_accesses: IndexSet::new() } + } +} + +impl AstVisitor for SymbolAccessCollector<'_> { + type AdditionalInput = (); + type Output = (); + + fn visit_path(&mut self, input: &Path, _: &Self::AdditionalInput) -> Self::Output { + self.symbol_accesses.insert((input.absolute_path(), None)); + } + + fn visit_tuple_access(&mut self, input: &TupleAccess, _: &Self::AdditionalInput) -> Self::Output { + // Here we assume that we can't have nested tuples which is currently guaranteed by type + // checking. This may change in the future. + if let Expression::Path(path) = &input.tuple { + // Futures aren't accessed by field; treat the whole thing as a direct variable + if let Some(Type::Future(_)) = self.state.type_table.get(&input.tuple.id()) { + self.symbol_accesses.insert((path.absolute_path(), None)); + } else { + self.symbol_accesses.insert((path.absolute_path(), Some(input.index.value()))); + } + } else { + self.visit_expression(&input.tuple, &()); + } + } +} + +impl ProgramVisitor for SymbolAccessCollector<'_> {} diff --git a/compiler/passes/src/common/symbol_table/mod.rs b/compiler/passes/src/common/symbol_table/mod.rs index 2308bef5edd..6533c83f8f2 100644 --- a/compiler/passes/src/common/symbol_table/mod.rs +++ b/compiler/passes/src/common/symbol_table/mod.rs @@ -18,7 +18,7 @@ use leo_ast::{Composite, Expression, Function, Location, NodeBuilder, NodeID, Ty use leo_errors::{AstError, Color, Label, LeoError, Result}; use leo_span::{Span, Symbol}; -use indexmap::IndexMap; +use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; use std::{cell::RefCell, collections::HashMap, rc::Rc}; @@ -30,6 +30,9 @@ pub use symbols::*; /// Scopes are indexed by the NodeID of the function, block, or iteration. #[derive(Debug, Default)] pub struct SymbolTable { + /// Maps a program to the list if programs it imports + imports: IndexMap>, + /// Functions indexed by location. functions: IndexMap, @@ -37,7 +40,7 @@ pub struct SymbolTable { records: IndexMap, /// Structs indexed by a path. - structs: IndexMap, Composite>, + structs: IndexMap, /// Consts that have been successfully evaluated. global_consts: IndexMap, @@ -144,6 +147,56 @@ impl LocalTable { } impl SymbolTable { + /// Record that `importer` imports `imported`. + pub fn add_import(&mut self, importer: Symbol, imported: Symbol) { + self.imports.entry(importer).or_default().insert(imported); + } + + /// Record that multiple importers import the same `imported` program. + pub fn add_imported_by(&mut self, imported: Symbol, importers: &IndexSet) { + for importer in importers { + self.add_import(*importer, imported); + } + } + + /// Returns all programs imported by a given program. + pub fn get_imports(&self, program: &Symbol) -> Option<&IndexSet> { + self.imports.get(program) + } + + /// Returns a mutable reference to the set of imports for a given program. + pub fn get_imports_mut(&mut self, program: &Symbol) -> Option<&mut IndexSet> { + self.imports.get_mut(program) + } + + /// Returns an iterator over all import relationships. + pub fn iter_imports(&self) -> impl Iterator)> { + self.imports.iter() + } + + /// Check if `target` program is visible from `current` program. + fn is_visible(&self, current: Symbol, target: &Symbol) -> bool { + current == *target || self.imports.get(¤t).map(|imports| imports.contains(target)).unwrap_or(false) + } + + /// Returns the transitive closure of imports for a given program. + pub fn get_transitive_imports(&self, program: &Symbol) -> IndexSet { + let mut visited = IndexSet::new(); + self.collect_transitive_imports(program, &mut visited); + visited + } + + fn collect_transitive_imports(&self, program: &Symbol, visited: &mut IndexSet) { + if let Some(direct_imports) = self.imports.get(program) { + for imported in direct_imports { + if visited.insert(*imported) { + // Recurse only if this is a new import + self.collect_transitive_imports(imported, visited); + } + } + } + } + /// Reset everything except leave consts that have been evaluated. pub fn reset_but_consts(&mut self) { self.functions.clear(); @@ -165,7 +218,7 @@ impl SymbolTable { } /// Iterator over all the structs (not records) in this program. - pub fn iter_structs(&self) -> impl Iterator, &Composite)> { + pub fn iter_structs(&self) -> impl Iterator { self.structs.iter() } @@ -179,22 +232,22 @@ impl SymbolTable { self.functions.iter() } - /// Access the struct by this name if it exists. - pub fn lookup_struct(&self, path: &[Symbol]) -> Option<&Composite> { - self.structs.get(path) + /// Access the struct by this name if it exists and is accessible from program named `current_program`. + pub fn lookup_struct(&self, current_program: Symbol, loc: &Location) -> Option<&Composite> { + if self.is_visible(current_program, &loc.program) { self.structs.get(loc) } else { None } } - /// Access the record at this location if it exists. - pub fn lookup_record(&self, location: &Location) -> Option<&Composite> { - self.records.get(location) + /// Access the record at this location if it exists and is accessible from program named `current_program`. + pub fn lookup_record(&self, current_program: Symbol, location: &Location) -> Option<&Composite> { + if self.is_visible(current_program, &location.program) { self.records.get(location) } else { None } } - /// Access the function at this location if it exists. - pub fn lookup_function(&self, location: &Location) -> Option<&FunctionSymbol> { - self.functions.get(location) + /// Access the struct by this name if it exists and is accessible from program named `current_program`. + pub fn lookup_function(&self, current_program: Symbol, location: &Location) -> Option<&FunctionSymbol> { + if self.is_visible(current_program, &location.program) { self.functions.get(location) } else { None } } - /// Attempts to look up a variable by a path. + /// Attempts to look up a variable by a path from program named `current_program`. /// /// First, it tries to resolve the symbol as a global using the full path under the given program. /// If that fails and the path is non-empty, it falls back to resolving the last component @@ -208,8 +261,8 @@ impl SymbolTable { /// # Returns /// /// An `Option` containing the resolved symbol if found, otherwise `None`. - pub fn lookup_path(&self, program: Symbol, path: &[Symbol]) -> Option { - self.lookup_global(&Location::new(program, path.to_vec())) + pub fn lookup_path(&self, current_program: Symbol, program: Symbol, path: &[Symbol]) -> Option { + self.lookup_global(current_program, &Location::new(program, path.to_vec())) .cloned() .or_else(|| path.last().copied().and_then(|name| self.lookup_local(name))) } @@ -354,22 +407,11 @@ impl SymbolTable { self.global_consts.get(&Location::new(program, path.to_vec())).cloned() } - /// Insert a struct at this name. - /// - /// Since structs are indexed only by name, the program is used only to check shadowing. - pub fn insert_struct(&mut self, program: Symbol, path: &[Symbol], composite: Composite) -> Result<()> { - if let Some(old_composite) = self.structs.get(path) { - if eq_struct(&composite, old_composite) { - Ok(()) - } else { - Err(AstError::redefining_external_struct(path.iter().format("::"), old_composite.span).into()) - } - } else { - let location = Location::new(program, path.to_vec()); - self.check_shadow_global(&location, composite.identifier.span)?; - self.structs.insert(path.to_vec(), composite); - Ok(()) - } + /// Insert a struct at this location. + pub fn insert_struct(&mut self, location: Location, composite: Composite) -> Result<()> { + self.check_shadow_global(&location, composite.identifier.span)?; + self.structs.insert(location, composite); + Ok(()) } /// Insert a record at this location. @@ -393,9 +435,10 @@ impl SymbolTable { Ok(()) } - /// Access the global at this location if it exists. - pub fn lookup_global(&self, location: &Location) -> Option<&VariableSymbol> { - self.globals.get(location) + /// Access the global variable at this location if it exists and is visible + /// from the given `current_program`. + pub fn lookup_global(&self, current_program: Symbol, location: &Location) -> Option<&VariableSymbol> { + if self.is_visible(current_program, &location.program) { self.globals.get(location) } else { None } } pub fn emit_shadow_error(name: Symbol, span: Span, prev_span: Span) -> LeoError { @@ -414,7 +457,7 @@ impl SymbolTable { .get(location) .map(|f| f.function.identifier.span) .or_else(|| self.records.get(location).map(|r| r.identifier.span)) - .or_else(|| self.structs.get(&location.path).map(|s| s.identifier.span)) + .or_else(|| self.structs.get(location).map(|s| s.identifier.span)) .or_else(|| self.globals.get(location).map(|g| g.span)) .map_or_else(|| Ok(()), |prev_span| Err(Self::emit_shadow_error(*name, span, prev_span))) } @@ -468,14 +511,3 @@ impl SymbolTable { } } } - -fn eq_struct(new: &Composite, old: &Composite) -> bool { - if new.members.len() != old.members.len() { - return false; - } - - new.members - .iter() - .zip(old.members.iter()) - .all(|(member1, member2)| member1.name() == member2.name() && member1.type_.eq_flat_relaxed(&member2.type_)) -} diff --git a/compiler/passes/src/const_propagation/visitor.rs b/compiler/passes/src/const_propagation/visitor.rs index bdde0c4d4b7..1ce66f67045 100644 --- a/compiler/passes/src/const_propagation/visitor.rs +++ b/compiler/passes/src/const_propagation/visitor.rs @@ -16,7 +16,7 @@ use crate::CompilerState; -use leo_ast::{Expression, Node, NodeID, interpreter_value::Value}; +use leo_ast::{Expression, Location, Node, NodeID, interpreter_value::Value}; use leo_errors::StaticAnalyzerError; use leo_span::{Span, Symbol}; @@ -64,16 +64,16 @@ impl ConstPropagationVisitor<'_> { pub fn value_to_expression(&self, value: &Value, span: Span, id: NodeID) -> Option { let ty = self.state.type_table.get(&id)?; let symbol_table = &self.state.symbol_table; - let struct_lookup = |sym: &[Symbol]| { + let struct_lookup = |loc: &Location| { symbol_table - .lookup_struct(sym) + .lookup_struct(self.program, loc) .unwrap() .members .iter() .map(|mem| (mem.identifier.name, mem.type_.clone())) .collect() }; - value.to_expression(span, &self.state.node_builder, &ty, &struct_lookup) + value.to_expression(span, &self.state.node_builder, self.program, &ty, &struct_lookup) } pub fn value_to_expression_node(&self, value: &Value, previous: &impl Node) -> Option { diff --git a/compiler/passes/src/flattening/ast.rs b/compiler/passes/src/flattening/ast.rs index 360d5ba8b49..8c0ff4ffda7 100644 --- a/compiler/passes/src/flattening/ast.rs +++ b/compiler/passes/src/flattening/ast.rs @@ -102,14 +102,17 @@ impl AstReconstructor for FlatteningVisitor<'_> { ), Type::Composite(if_true_type) => { // Get the composite definitions. - let program = if_true_type.program.unwrap_or(self.program); + let if_true_type_program = if_true_type.program; + let program = if_true_type_program.unwrap_or(self.program); let composite_path = if_true_type.path.clone(); let if_true_type = self .state .symbol_table - .lookup_struct(&composite_path.absolute_path()) + .lookup_struct(self.program, &Location::new(program, composite_path.absolute_path())) .or_else(|| { - self.state.symbol_table.lookup_record(&Location::new(program, composite_path.absolute_path())) + self.state + .symbol_table + .lookup_record(self.program, &Location::new(program, composite_path.absolute_path())) }) .expect("This definition should exist") .clone(); @@ -117,6 +120,7 @@ impl AstReconstructor for FlatteningVisitor<'_> { self.ternary_composite( &composite_path, &if_true_type, + if_true_type_program, &input.condition, &as_identifier(input.if_true), &as_identifier(input.if_false), diff --git a/compiler/passes/src/flattening/visitor.rs b/compiler/passes/src/flattening/visitor.rs index 7adcd064cc7..0bf751053f7 100644 --- a/compiler/passes/src/flattening/visitor.rs +++ b/compiler/passes/src/flattening/visitor.rs @@ -473,6 +473,7 @@ impl FlatteningVisitor<'_> { &mut self, composite_path: &Path, composite: &Composite, + program: Option, condition: &Expression, first: &Identifier, second: &Identifier, @@ -516,6 +517,7 @@ impl FlatteningVisitor<'_> { path: composite_path.clone(), const_arguments: Vec::new(), // All const arguments should have been resolved by now members, + program, span: Default::default(), id: { // Create a new node ID for the comopsite expression. diff --git a/compiler/passes/src/loop_unrolling/program.rs b/compiler/passes/src/loop_unrolling/program.rs index e212aa6ae74..251f65e47ef 100644 --- a/compiler/passes/src/loop_unrolling/program.rs +++ b/compiler/passes/src/loop_unrolling/program.rs @@ -19,10 +19,10 @@ use leo_ast::*; use super::UnrollingVisitor; impl ProgramReconstructor for UnrollingVisitor<'_> { - fn reconstruct_stub(&mut self, input: Stub) -> Stub { + fn reconstruct_aleo_program(&mut self, input: AleoProgram) -> AleoProgram { // Set the current program. self.program = input.stub_id.name.name; - Stub { + AleoProgram { functions: input.functions.into_iter().map(|(i, f)| (i, self.reconstruct_function_stub(f))).collect(), ..input } diff --git a/compiler/passes/src/monomorphization/ast.rs b/compiler/passes/src/monomorphization/ast.rs index 08b1bde332c..7e28121df8e 100644 --- a/compiler/passes/src/monomorphization/ast.rs +++ b/compiler/passes/src/monomorphization/ast.rs @@ -25,6 +25,7 @@ use leo_ast::{ CompositeType, Expression, Identifier, + Location, Node as _, ProgramReconstructor, Type, @@ -77,7 +78,11 @@ impl AstReconstructor for MonomorphizationVisitor<'_> { self.changed = true; ( Type::Composite(CompositeType { - path: self.monomorphize_composite(&input.path, &evaluated_const_args), + path: self.monomorphize_composite( + input.program.unwrap_or(self.program), + &input.path, + &evaluated_const_args, + ), const_arguments: vec![], // remove const arguments program: input.program, }), @@ -123,6 +128,8 @@ impl AstReconstructor for MonomorphizationVisitor<'_> { input_call: CallExpression, _additional: &(), ) -> (Expression, Self::AdditionalOutput) { + let callee_program = input_call.program.unwrap_or(self.program); + // Skip calls to functions from other programs. if input_call.program.is_some_and(|prog| prog != self.program) { return (input_call.into(), Default::default()); @@ -145,7 +152,7 @@ impl AstReconstructor for MonomorphizationVisitor<'_> { // Look up the already reconstructed function by name. let callee_fn = self .reconstructed_functions - .get(&input_call.function.absolute_path()) + .get(&Location::new(callee_program, input_call.function.absolute_path())) .expect("Callee should already be reconstructed (post-order traversal)."); // Proceed only if the function variant is `inline`. @@ -166,7 +173,7 @@ impl AstReconstructor for MonomorphizationVisitor<'_> { // Check if the new callee name is not already present in `reconstructed_functions`. This ensures that we do not // add a duplicate definition for the same function. - if self.reconstructed_functions.get(&new_callee_path.absolute_path()).is_none() { + if self.reconstructed_functions.get(&Location::new(callee_program, new_callee_path.absolute_path())).is_none() { // Build mapping from const parameters to const argument values. let const_param_map: IndexMap<_, _> = callee_fn .const_parameters @@ -202,10 +209,11 @@ impl AstReconstructor for MonomorphizationVisitor<'_> { function.id = self.state.node_builder.next_id(); // Keep track of the new function in case other functions need it. - self.reconstructed_functions.insert(new_callee_path.absolute_path(), function); + self.reconstructed_functions + .insert(Location::new(callee_program, new_callee_path.absolute_path()), function); // Now keep track of the function we just monomorphized - self.monomorphized_functions.insert(input_call.function.absolute_path()); + self.monomorphized_functions.insert(Location::new(callee_program, input_call.function.absolute_path())); } // At this stage, we know that we're going to modify the program @@ -266,10 +274,11 @@ impl AstReconstructor for MonomorphizationVisitor<'_> { // Finally, construct the updated composite expression that points to a monomorphized version and return it. ( CompositeExpression { - path: self.monomorphize_composite(&input.path, &evaluated_const_args), + path: self.monomorphize_composite(self.program, &input.path, &evaluated_const_args), members, const_arguments: vec![], // remove const arguments - span: input.span, // Keep pointing to the original composite expression + program: Some(self.program), + span: input.span, // Keep pointing to the original composite expression id: input.id, } .into(), diff --git a/compiler/passes/src/monomorphization/program.rs b/compiler/passes/src/monomorphization/program.rs index 098f8818fc7..de94941578d 100644 --- a/compiler/passes/src/monomorphization/program.rs +++ b/compiler/passes/src/monomorphization/program.rs @@ -15,7 +15,7 @@ // along with the Leo library. If not, see . use super::MonomorphizationVisitor; -use leo_ast::{AstReconstructor, Module, Program, ProgramReconstructor, ProgramScope, Statement, Variant}; +use leo_ast::{AstReconstructor, Location, Module, Program, ProgramReconstructor, ProgramScope, Statement, Variant}; use leo_span::sym; impl ProgramReconstructor for MonomorphizationVisitor<'_> { @@ -28,12 +28,13 @@ impl ProgramReconstructor for MonomorphizationVisitor<'_> { let composite_order = self.state.composite_graph.post_order().unwrap(); // Reconstruct composites in post-order. - for composite_name in &composite_order { - if let Some(composite) = self.composite_map.swap_remove(composite_name) { + for loc in &composite_order { + if let Some(composite) = self.composite_map.swap_remove(&Location::new(self.program, loc.path.clone())) { // Perform monomorphization or other reconstruction logic. let reconstructed_composite = self.reconstruct_composite(composite); - // Store the reconstructed compoiste for inclusion in the output scope. - self.reconstructed_composites.insert(composite_name.clone(), reconstructed_composite); + // Store the reconstructed composite for inclusion in the output scope. + self.reconstructed_composites + .insert(Location::new(self.program, loc.path.clone()), reconstructed_composite); } } @@ -61,7 +62,7 @@ impl ProgramReconstructor for MonomorphizationVisitor<'_> { return true; } self.function_map - .get(&location.path) + .get(location) .map(|f| { matches!( f.variant, @@ -76,16 +77,17 @@ impl ProgramReconstructor for MonomorphizationVisitor<'_> { for function_name in &order { // Reconstruct functions in post-order. - if let Some(function) = self.function_map.swap_remove(&function_name.path) { + if let Some(function) = + self.function_map.swap_remove(&Location::new(self.program, function_name.path.clone())) + { // Reconstruct the function. let reconstructed_function = self.reconstruct_function(function); // Add the reconstructed function to the mapping. - self.reconstructed_functions.insert(function_name.path.clone(), reconstructed_function); + self.reconstructed_functions + .insert(Location::new(self.program, function_name.path.clone()), reconstructed_function); } } - // Get any - // Now reconstruct mappings and storage variables let mappings = input.mappings.into_iter().map(|(id, mapping)| (id, self.reconstruct_mapping(mapping))).collect(); @@ -109,9 +111,12 @@ impl ProgramReconstructor for MonomorphizationVisitor<'_> { let constructor = input.constructor.map(|c| self.reconstruct_constructor(c)); // Now retain only functions that are either not yet monomorphized or are still referenced by calls. - self.reconstructed_functions.retain(|f, _| { - let is_monomorphized = self.monomorphized_functions.contains(f); - let is_still_called = self.unresolved_calls.iter().any(|c| &c.function.absolute_path() == f); + self.reconstructed_functions.retain(|l, _| { + let is_monomorphized = self.monomorphized_functions.contains(l); + let is_still_called = self + .unresolved_calls + .iter() + .any(|c| c.function.absolute_path() == l.path && c.program.unwrap_or(self.program) == self.program); !is_monomorphized || is_still_called }); @@ -129,18 +134,24 @@ impl ProgramReconstructor for MonomorphizationVisitor<'_> { composites: self .reconstructed_composites .iter() - .filter_map(|(path, c)| { - // only consider composites defined at program scope. The rest will be added to their parent module. - path.split_last().filter(|(_, rest)| rest.is_empty()).map(|(last, _)| (*last, c.clone())) + .filter_map(|(loc, c)| { + // Only consider composites defined at program scope within the same program. + loc.path + .split_last() + .filter(|(_, rest)| rest.is_empty() && loc.program == self.program) + .map(|(last, _)| (*last, c.clone())) }) .collect(), mappings, storage_variables, functions: all_functions .iter() - .filter_map(|(path, f)| { - // only consider functions defined at program scope. The rest will be added to their parent module. - path.split_last().filter(|(_, rest)| rest.is_empty()).map(|(last, _)| (*last, f.clone())) + .filter_map(|(loc, f)| { + // Only consider functions defined at program scope within the same program. + loc.path + .split_last() + .filter(|(_, rest)| rest.is_empty() && loc.program == self.program) + .map(|(last, _)| (*last, f.clone())) }) .collect(), constructor, @@ -150,6 +161,16 @@ impl ProgramReconstructor for MonomorphizationVisitor<'_> { } fn reconstruct_program(&mut self, input: Program) -> Program { + let stubs = input.stubs.into_iter().map(|(id, stub)| (id, self.reconstruct_stub(stub))).collect(); + + // Set up for the next program. + // This is likely to change when we support cross-program monomorphization. + self.composite_map.clear(); + self.function_map.clear(); + + self.program = + *input.program_scopes.first().expect("a program must have a single program scope at this stage").0; + // Populate `self.function_map` using the functions in the program scopes and the modules input .modules @@ -166,7 +187,7 @@ impl ProgramReconstructor for MonomorphizationVisitor<'_> { .flat_map(|(_, scope)| scope.functions.iter().map(|(name, f)| (vec![*name], f.clone()))), ) .for_each(|(full_name, f)| { - self.function_map.insert(full_name, f); + self.function_map.insert(Location::new(self.program, full_name), f); }); // Populate `self.composite_map` using the composites in the program scopes and the modules @@ -185,12 +206,13 @@ impl ProgramReconstructor for MonomorphizationVisitor<'_> { .flat_map(|(_, scope)| scope.composites.iter().map(|(name, f)| (vec![*name], f.clone()))), ) .for_each(|(full_name, f)| { - self.composite_map.insert(full_name, f); + self.composite_map.insert(Location::new(self.program, full_name), f); }); // Reconstruct prrogram scopes first then reconstruct the modules after `self.reconstructed_composites` // and `self.reconstructed_functions` have been populated. Program { + stubs, program_scopes: input .program_scopes .into_iter() @@ -208,18 +230,25 @@ impl ProgramReconstructor for MonomorphizationVisitor<'_> { composites: self .reconstructed_composites .iter() - .filter_map(|(path, c)| path.split_last().map(|(last, rest)| (last, rest, c))) - .filter(|&(_, rest, _)| input.path == rest) - .map(|(last, _, c)| (*last, c.clone())) + .filter_map(|(loc, c)| { + loc.path + .split_last() + .filter(|(_, rest)| *rest == input.path && loc.program == self.program) + .map(|(last, _)| (*last, c.clone())) + }) .collect(), functions: self .reconstructed_functions .iter() - .filter_map(|(path, f)| path.split_last().map(|(last, rest)| (last, rest, f))) - .filter(|&(_, rest, _)| input.path == rest) - .map(|(last, _, f)| (*last, f.clone())) + .filter_map(|(loc, f)| { + loc.path + .split_last() + .filter(|(_, rest)| *rest == input.path && loc.program == self.program) + .map(|(last, _)| (*last, f.clone())) + }) .collect(), + ..input } } diff --git a/compiler/passes/src/monomorphization/visitor.rs b/compiler/passes/src/monomorphization/visitor.rs index 036c436a4ad..ae44bd21f61 100644 --- a/compiler/passes/src/monomorphization/visitor.rs +++ b/compiler/passes/src/monomorphization/visitor.rs @@ -25,6 +25,7 @@ use leo_ast::{ Expression, Function, Identifier, + Location, Path, ProgramReconstructor, }; @@ -35,19 +36,19 @@ pub struct MonomorphizationVisitor<'a> { /// The main program. pub program: Symbol, /// A map to provide faster lookup of functions. - pub function_map: IndexMap, Function>, + pub function_map: IndexMap, /// A map to provide faster lookup of composites. - pub composite_map: IndexMap, Composite>, + pub composite_map: IndexMap, /// A map of reconstructed functions in the current program scope. - pub reconstructed_functions: IndexMap, Function>, + pub reconstructed_functions: IndexMap, /// A set of all functions that have been monomorphized at least once. This keeps track of the _original_ names of /// the functions not the names of the monomorphized versions. - pub monomorphized_functions: IndexSet>, + pub monomorphized_functions: IndexSet, /// A map of reconstructed functions in the current program scope. - pub reconstructed_composites: IndexMap, Composite>, + pub reconstructed_composites: IndexMap, /// A set of all functions that have been monomorphized at least once. This keeps track of the _original_ names of /// the functions not the names of the monomorphized versions. - pub monomorphized_composites: IndexSet>, + pub monomorphized_composites: IndexSet, /// A vector of all the calls to const generic functions that have not been resolved. pub unresolved_calls: Vec, /// A vector of all the composite expressions of const generic composites that have not been resolved. @@ -71,7 +72,12 @@ impl MonomorphizationVisitor<'_> { /// * Returns a `Symbol` for the newly monomorphized composite. /// /// Note: this functions already assumes that all provided const arguments are literals. - pub(crate) fn monomorphize_composite(&mut self, path: &Path, const_arguments: &Vec) -> Path { + pub(crate) fn monomorphize_composite( + &mut self, + program: Symbol, + path: &Path, + const_arguments: &Vec, + ) -> Path { // Generate a unique name for the monomorphized composite based on const arguments. // // For `struct Foo::[x: u32, y: u32](..)`, the generated name would be `Foo::[1u32, 2u32]` for a composite @@ -87,12 +93,12 @@ impl MonomorphizationVisitor<'_> { // Check if the new composite name is not already present in `reconstructed_composites`. This ensures that we do not // add a duplicate definition for the same composite. - if self.reconstructed_composites.get(&new_composite_path.absolute_path()).is_none() { + if self.reconstructed_composites.get(&Location::new(program, new_composite_path.absolute_path())).is_none() { let full_name = path.absolute_path(); // Look up the already reconstructed composite by name. let composite = self .reconstructed_composites - .get(&full_name) + .get(&Location::new(program, full_name.clone())) .expect("Composite should already be reconstructed (post-order traversal)."); // Build mapping from const parameters to const argument values. @@ -128,10 +134,10 @@ impl MonomorphizationVisitor<'_> { composite.id = self.state.node_builder.next_id(); // Keep track of the new composite in case other composites need it. - self.reconstructed_composites.insert(new_composite_path.absolute_path(), composite); + self.reconstructed_composites.insert(Location::new(program, new_composite_path.absolute_path()), composite); // Now keep track of the composite we just monomorphized - self.monomorphized_composites.insert(full_name); + self.monomorphized_composites.insert(Location::new(program, full_name)); } new_composite_path diff --git a/compiler/passes/src/name_validation/program.rs b/compiler/passes/src/name_validation/program.rs index a1afb09bdb2..7d7082c31fe 100644 --- a/compiler/passes/src/name_validation/program.rs +++ b/compiler/passes/src/name_validation/program.rs @@ -39,7 +39,7 @@ impl ProgramVisitor for NameValidationVisitor<'_> { input.functions.iter().for_each(|(_, function)| self.visit_function(function)); } - fn visit_stub(&mut self, input: &Stub) { + fn visit_aleo_program(&mut self, input: &AleoProgram) { input.composites.iter().for_each(|(_, function)| self.visit_composite_stub(function)); input.functions.iter().for_each(|(_, function)| self.visit_function_stub(function)); } diff --git a/compiler/passes/src/option_lowering/ast.rs b/compiler/passes/src/option_lowering/ast.rs index cf6f7750786..f0b1a921a37 100644 --- a/compiler/passes/src/option_lowering/ast.rs +++ b/compiler/passes/src/option_lowering/ast.rs @@ -62,8 +62,8 @@ impl leo_ast::AstReconstructor for OptionLoweringVisitor<'_> { ( Type::Composite(CompositeType { path: Path::from(Identifier::new(struct_name, self.state.node_builder.next_id())).into_absolute(), - const_arguments: vec![], // this is not a generic struct - program: None, // current program + const_arguments: vec![], // this is not a generic struct + program: Some(self.program), // current program }), Default::default(), ) @@ -114,7 +114,7 @@ impl leo_ast::AstReconstructor for OptionLoweringVisitor<'_> { "Type table must contain type for this expression ID; IDs are not modified during lowering", ); - if actual_expr_type.can_coerce_to(inner) { + if actual_expr_type.can_coerce_to(inner, self.program) { return (self.wrap_optional_value(expr, *inner.clone()), stmts); } } @@ -366,7 +366,7 @@ impl leo_ast::AstReconstructor for OptionLoweringVisitor<'_> { let func_symbol = self .state .symbol_table - .lookup_function(&Location::new(callee_program, input.function.absolute_path())) + .lookup_function(callee_program, &Location::new(callee_program, input.function.absolute_path())) .expect("The symbol table creator should already have visited all functions.") .clone(); @@ -426,8 +426,8 @@ impl leo_ast::AstReconstructor for OptionLoweringVisitor<'_> { let composite_def = self .state .symbol_table - .lookup_record(&location) - .or_else(|| self.state.symbol_table.lookup_struct(&composite.path.absolute_path())) + .lookup_record(self.program, &location) + .or_else(|| self.state.symbol_table.lookup_struct(self.program, &location)) .or_else(|| self.new_structs.get(&composite.path.identifier().name)) .expect("guaranteed by type checking"); @@ -709,7 +709,7 @@ impl leo_ast::AstReconstructor for OptionLoweringVisitor<'_> { let func_symbol = self .state .symbol_table - .lookup_function(&Location::new(self.program, caller_path)) + .lookup_function(self.program, &Location::new(self.program, caller_path)) .expect("The symbol table creator should already have visited all functions."); let return_type = func_symbol.function.output_type.clone(); diff --git a/compiler/passes/src/option_lowering/program.rs b/compiler/passes/src/option_lowering/program.rs index f106ddfc2b6..b3138d4cef2 100644 --- a/compiler/passes/src/option_lowering/program.rs +++ b/compiler/passes/src/option_lowering/program.rs @@ -20,6 +20,7 @@ use leo_ast::{ ConstParameter, Function, Input, + Location, Module, Output, Program, @@ -31,23 +32,29 @@ use leo_span::Symbol; impl ProgramReconstructor for OptionLoweringVisitor<'_> { fn reconstruct_program(&mut self, input: Program) -> Program { + self.program = + *input.program_scopes.first().expect("a program must have a single program scope at this time.").0; + // Reconstruct all composites first and keep track of them in `self.reconstructed_composites`. for (_, scope) in &input.program_scopes { for (_, c) in &scope.composites { let new_composite = self.reconstruct_composite(c.clone()); - self.reconstructed_composites.insert(vec![new_composite.name()], new_composite); + self.reconstructed_composites + .insert(Location::new(scope.program_id.name.name, vec![new_composite.name()]), new_composite); } } for (module_path, module) in &input.modules { for (_, c) in &module.composites { let full_name = module_path.iter().cloned().chain(std::iter::once(c.name())).collect::>(); let new_composite = self.reconstruct_composite(c.clone()); - self.reconstructed_composites.insert(full_name, new_composite.clone()); + self.reconstructed_composites + .insert(Location::new(module.program_name, full_name), new_composite.clone()); } } // Now we're ready to reconstruct everything else. Program { + modules: input.modules.into_iter().map(|(id, module)| (id, self.reconstruct_module(module))).collect(), imports: input.imports, stubs: input.stubs.into_iter().map(|(id, stub)| (id, self.reconstruct_stub(stub))).collect(), program_scopes: input @@ -55,7 +62,6 @@ impl ProgramReconstructor for OptionLoweringVisitor<'_> { .into_iter() .map(|(id, scope)| (id, self.reconstruct_program_scope(scope))) .collect(), - modules: input.modules.into_iter().map(|(id, module)| (id, self.reconstruct_module(module))).collect(), } } @@ -74,10 +80,15 @@ impl ProgramReconstructor for OptionLoweringVisitor<'_> { composites: self .reconstructed_composites .iter() - .filter_map(|(path, s)| { - path.split_last().filter(|(_, rest)| rest.is_empty()).map(|(last, _)| (*last, s.clone())) + .filter_map(|(loc, c)| { + // Only consider structs defined at program scope within the same program. + loc.path + .split_last() + .filter(|(_, rest)| rest.is_empty() && loc.program == self.program) + .map(|(last, _)| (*last, c.clone())) }) .collect(), + mappings: input.mappings.into_iter().map(|(id, m)| (id, self.reconstruct_mapping(m))).collect(), storage_variables: input .storage_variables @@ -107,10 +118,14 @@ impl ProgramReconstructor for OptionLoweringVisitor<'_> { composites: slf .reconstructed_composites .iter() - .filter_map(|(path, c)| path.split_last().map(|(last, rest)| (last, rest, c))) - .filter(|&(_, rest, _)| input.path == rest) - .map(|(last, _, c)| (*last, c.clone())) + .filter_map(|(loc, c)| { + loc.path + .split_last() + .filter(|(_, rest)| *rest == input.path && loc.program == slf.program) + .map(|(last, _)| (*last, c.clone())) + }) .collect(), + functions: input.functions.into_iter().map(|(i, f)| (i, slf.reconstruct_function(f))).collect(), ..input }) diff --git a/compiler/passes/src/option_lowering/visitor.rs b/compiler/passes/src/option_lowering/visitor.rs index ada05554adf..d30106ceaed 100644 --- a/compiler/passes/src/option_lowering/visitor.rs +++ b/compiler/passes/src/option_lowering/visitor.rs @@ -33,7 +33,7 @@ pub struct OptionLoweringVisitor<'a> { // structs are to be inserted in the program scope. pub new_structs: IndexMap, // The reconstructed composites. These are the new versions of the existing composites in the program. - pub reconstructed_composites: IndexMap, Composite>, + pub reconstructed_composites: IndexMap, } impl OptionLoweringVisitor<'_> { @@ -89,6 +89,7 @@ impl OptionLoweringVisitor<'_> { id: self.state.node_builder.next_id(), }, ], + program: None, span: Span::default(), id: self.state.node_builder.next_id(), }; @@ -127,10 +128,10 @@ impl OptionLoweringVisitor<'_> { // Instead of relying on the symbol table (which does not get updated in this pass), we rely on the set of // reconstructed composites which is produced for all program scopes and all modules before doing anything else. let reconstructed_composites = &self.reconstructed_composites; - let struct_lookup = |sym: &[Symbol]| { + let struct_lookup = |loc: &Location| { reconstructed_composites - .get(sym) // check the new version of existing composites - .or_else(|| self.new_structs.get(sym.last().unwrap())) // check the newly produced structs + .get(loc) // check the new version of existing composites + .or_else(|| self.new_structs.get(loc.path.last().unwrap())) // check the newly produced structs .expect("must exist by construction") .members .iter() @@ -138,8 +139,14 @@ impl OptionLoweringVisitor<'_> { .collect() }; - let zero_val_expr = - Expression::zero(&lowered_inner_type, Span::default(), &self.state.node_builder, &struct_lookup).expect(""); + let zero_val_expr = Expression::zero( + &lowered_inner_type, + Span::default(), + &self.state.node_builder, + self.program, + &struct_lookup, + ) + .expect("this must work if type checking was successful"); // Create or get an optional wrapper struct for `lowered_inner_type` let struct_name = self.insert_optional_wrapper_struct(&lowered_inner_type); @@ -161,6 +168,7 @@ impl OptionLoweringVisitor<'_> { id: self.state.node_builder.next_id(), }, ], + program: None, span: Span::default(), id: self.state.node_builder.next_id(), }; diff --git a/compiler/passes/src/pass.rs b/compiler/passes/src/pass.rs index a01e53bc17d..29954dabf55 100644 --- a/compiler/passes/src/pass.rs +++ b/compiler/passes/src/pass.rs @@ -19,7 +19,7 @@ use crate::{Assigner, SymbolTable, TypeTable}; use leo_ast::{Ast, CallGraph, CompositeGraph, NetworkName, NodeBuilder}; use leo_errors::{Handler, LeoWarning, Result}; -use std::collections::HashSet; +use std::{collections::HashSet, rc::Rc}; /// Contains data shared by many compiler passes. #[derive(Default)] @@ -31,7 +31,7 @@ pub struct CompilerState { /// Maps node IDs to types. pub type_table: TypeTable, /// Creates incrementing node IDs. - pub node_builder: NodeBuilder, + pub node_builder: Rc, /// Creates unique symbols and definitions. pub assigner: Assigner, /// Contains data about the variables and other entities in the program. diff --git a/compiler/passes/src/path_resolution/ast.rs b/compiler/passes/src/path_resolution/ast.rs index 6242f00c2ac..0367878fcc8 100644 --- a/compiler/passes/src/path_resolution/ast.rs +++ b/compiler/passes/src/path_resolution/ast.rs @@ -43,7 +43,7 @@ impl AstReconstructor for PathResolutionVisitor<'_> { .into_iter() .map(|arg| self.reconstruct_expression(arg, &()).0) .collect(), - ..input + program: Some(input.program.unwrap_or(self.program)), }), Default::default(), ) @@ -103,6 +103,7 @@ impl AstReconstructor for PathResolutionVisitor<'_> { id: member.id, }) .collect(), + program: Some(input.program.unwrap_or(self.program)), ..input } .into(), diff --git a/compiler/passes/src/path_resolution/mod.rs b/compiler/passes/src/path_resolution/mod.rs index 6d74b2a0321..3a489c8e120 100644 --- a/compiler/passes/src/path_resolution/mod.rs +++ b/compiler/passes/src/path_resolution/mod.rs @@ -51,6 +51,7 @@ use crate::Pass; use leo_ast::ProgramReconstructor as _; use leo_errors::Result; +use leo_span::Symbol; mod ast; @@ -69,7 +70,7 @@ impl Pass for PathResolution { fn do_pass(_input: Self::Input, state: &mut crate::CompilerState) -> Result { let mut ast = std::mem::take(&mut state.ast); - let mut visitor = PathResolutionVisitor { state, module: Vec::new() }; + let mut visitor = PathResolutionVisitor { state, program: Symbol::intern(""), module: Vec::new() }; ast.ast = visitor.reconstruct_program(ast.ast); visitor.state.handler.last_err()?; visitor.state.ast = ast; diff --git a/compiler/passes/src/path_resolution/program.rs b/compiler/passes/src/path_resolution/program.rs index b396c37391a..201324e3e88 100644 --- a/compiler/passes/src/path_resolution/program.rs +++ b/compiler/passes/src/path_resolution/program.rs @@ -15,9 +15,35 @@ // along with the Leo library. If not, see . use super::PathResolutionVisitor; -use leo_ast::{AstReconstructor, Module, ProgramReconstructor, Statement}; +use leo_ast::{AstReconstructor, Module, ProgramReconstructor, ProgramScope, Statement}; impl ProgramReconstructor for PathResolutionVisitor<'_> { + fn reconstruct_program_scope(&mut self, input: ProgramScope) -> ProgramScope { + self.program = input.program_id.name.name; + + ProgramScope { + program_id: input.program_id, + consts: input + .consts + .into_iter() + .map(|(i, c)| match self.reconstruct_const(c) { + (Statement::Const(declaration), _) => (i, declaration), + _ => panic!("`reconstruct_const` can only return `Statement::Const`"), + }) + .collect(), + composites: input.composites.into_iter().map(|(i, c)| (i, self.reconstruct_composite(c))).collect(), + mappings: input.mappings.into_iter().map(|(id, mapping)| (id, self.reconstruct_mapping(mapping))).collect(), + storage_variables: input + .storage_variables + .into_iter() + .map(|(id, storage_variable)| (id, self.reconstruct_storage_variable(storage_variable))) + .collect(), + functions: input.functions.into_iter().map(|(i, f)| (i, self.reconstruct_function(f))).collect(), + constructor: input.constructor.map(|c| self.reconstruct_constructor(c)), + span: input.span, + } + } + fn reconstruct_module(&mut self, input: Module) -> Module { self.in_module_scope(&input.path.clone(), |slf| Module { program_name: input.program_name, diff --git a/compiler/passes/src/path_resolution/visitor.rs b/compiler/passes/src/path_resolution/visitor.rs index df1325d045c..e672c05701d 100644 --- a/compiler/passes/src/path_resolution/visitor.rs +++ b/compiler/passes/src/path_resolution/visitor.rs @@ -20,6 +20,8 @@ use leo_span::Symbol; pub struct PathResolutionVisitor<'a> { pub state: &'a mut CompilerState, + /// The current program. + pub program: Symbol, /// The current module. pub module: Vec, } diff --git a/compiler/passes/src/processing_async/ast.rs b/compiler/passes/src/processing_async/ast.rs index a38ba7748c1..0e8ad1fe7e5 100644 --- a/compiler/passes/src/processing_async/ast.rs +++ b/compiler/passes/src/processing_async/ast.rs @@ -15,67 +15,8 @@ // along with the Leo library. If not, see . use super::ProcessingAsyncVisitor; -use crate::{CompilerState, Replacer}; -use indexmap::{IndexMap, IndexSet}; -use leo_ast::{ - AstReconstructor, - AstVisitor, - AsyncExpression, - Block, - CallExpression, - Expression, - Function, - Identifier, - Input, - IterationStatement, - Location, - Node, - Path, - ProgramVisitor, - Statement, - TupleAccess, - TupleExpression, - TupleType, - Type, - Variant, -}; -use leo_span::{Span, Symbol}; - -/// Collects all symbol accesses within an async block, -/// including both direct variable identifiers (`x`) and tuple field accesses (`x.0`, `x.1`, etc.). -/// Each access is recorded as a pair: (Symbol, Option). -/// - `None` means a direct variable access. -/// - `Some(index)` means a tuple field access. -struct SymbolAccessCollector<'a> { - state: &'a CompilerState, - symbol_accesses: IndexSet<(Vec, Option)>, -} - -impl AstVisitor for SymbolAccessCollector<'_> { - type AdditionalInput = (); - type Output = (); - - fn visit_path(&mut self, input: &Path, _: &Self::AdditionalInput) -> Self::Output { - self.symbol_accesses.insert((input.absolute_path(), None)); - } - - fn visit_tuple_access(&mut self, input: &TupleAccess, _: &Self::AdditionalInput) -> Self::Output { - // Here we assume that we can't have nested tuples which is currently guaranteed by type - // checking. This may change in the future. - if let Expression::Path(path) = &input.tuple { - // Futures aren't accessed by field; treat the whole thing as a direct variable - if let Some(Type::Future(_)) = self.state.type_table.get(&input.tuple.id()) { - self.symbol_accesses.insert((path.absolute_path(), None)); - } else { - self.symbol_accesses.insert((path.absolute_path(), Some(input.index.value()))); - } - } else { - self.visit_expression(&input.tuple, &()); - } - } -} - -impl ProgramVisitor for SymbolAccessCollector<'_> {} +use crate::BlockToFunctionRewriter; +use leo_ast::{AstReconstructor, AsyncExpression, Block, Expression, IterationStatement, Node, Statement, Variant}; impl AstReconstructor for ProcessingAsyncVisitor<'_> { type AdditionalInput = (); @@ -83,265 +24,30 @@ impl AstReconstructor for ProcessingAsyncVisitor<'_> { /// Transforms an `AsyncExpression` into a standalone async `Function` and returns /// a call to this function. This process: - /// - Collects all referenced symbol accesses in the async block. - /// - Filters out mappings and constructs typed input parameters. - /// - Reconstructs an async function with those inputs and the original block. - /// - Builds and returns a `CallExpression` that invokes the new function. fn reconstruct_async(&mut self, input: AsyncExpression, _additional: &()) -> (Expression, Self::AdditionalOutput) { - // Step 1: Generate a unique name for the async function + // Generate a unique name for the async function. let finalize_fn_name = self.state.assigner.unique_symbol(self.current_function, "$"); - // Step 2: Collect all symbol accesses in the async block - let mut access_collector = SymbolAccessCollector { state: self.state, symbol_accesses: IndexSet::new() }; - access_collector.visit_async(&input, &()); - - // Stores mapping from accessed symbol (and optional index) to the expression used in replacement - let mut replacements: IndexMap<(Symbol, Option), Expression> = IndexMap::new(); - - // Helper to create a fresh `Identifier` - let make_identifier = |slf: &mut Self, symbol: Symbol| Identifier { - name: symbol, - span: Span::default(), - id: slf.state.node_builder.next_id(), - }; - - // Generates a set of `Input`s and corresponding call-site `Expression`s for a given symbol access. - // - // This function handles both: - // - Direct variable accesses (e.g., `foo`) - // - Tuple element accesses (e.g., `foo.0`) - // - // For tuple accesses: - // - If a single element (e.g. `foo.0`) is accessed, it generates a synthetic input like `"foo.0"`. - // - If the whole tuple (e.g. `foo`) is accessed, it ensures all elements are covered by: - // - Reusing existing inputs from `replacements` if already generated via prior field access. - // - Creating new inputs and arguments for any missing elements. - // - The entire tuple is reconstructed in `replacements` using the individual elements as a `TupleExpression`. - // - // This function also ensures deduplication by consulting the `replacements` map: - // - If a given `(symbol, index)` has already been processed, no duplicate input or argument is generated. - // - This prevents repeated parameters for accesses like both `foo` and `foo.0`. - // - // # Parameters - // - `symbol`: The symbol being accessed. - // - `var_type`: The type of the symbol (may be a tuple or base type). - // - `index_opt`: `Some(index)` for a tuple field (e.g., `.0`), or `None` for full-variable access. - // - // # Returns - // A `Vec<(Input, Expression)>`, where: - // - `Input` is a parameter for the generated async function. - // - `Expression` is the call-site argument expression used to invoke that parameter. - let mut make_inputs_and_arguments = - |slf: &mut Self, symbol: Symbol, var_type: &Type, index_opt: Option| -> Vec<(Input, Expression)> { - if replacements.contains_key(&(symbol, index_opt)) { - return vec![]; // No new input needed; argument already exists - } - - match index_opt { - Some(index) => { - let Type::Tuple(TupleType { elements }) = var_type else { - panic!("Expected tuple type when accessing tuple field: {symbol}.{index}"); - }; - - let synthetic_name = format!("\"{symbol}.{index}\""); - let synthetic_symbol = Symbol::intern(&synthetic_name); - let identifier = make_identifier(slf, synthetic_symbol); - - let input = Input { - identifier, - mode: leo_ast::Mode::None, - type_: elements[index].clone(), - span: Span::default(), - id: slf.state.node_builder.next_id(), - }; - - replacements.insert((symbol, Some(index)), Path::from(identifier).into_absolute().into()); - - vec![( - input, - TupleAccess { - tuple: Path::from(make_identifier(slf, symbol)).into_absolute().into(), - index: index.into(), - span: Span::default(), - id: slf.state.node_builder.next_id(), - } - .into(), - )] - } - - None => match var_type { - Type::Tuple(TupleType { elements }) => { - let mut inputs_and_arguments = Vec::with_capacity(elements.len()); - let mut tuple_elements = Vec::with_capacity(elements.len()); - - for (i, element_type) in elements.iter().enumerate() { - let key = (symbol, Some(i)); - - // Skip if this field is already handled - if let Some(existing_expr) = replacements.get(&key) { - tuple_elements.push(existing_expr.clone()); - continue; - } - - // Otherwise, synthesize identifier and input - let synthetic_name = format!("\"{symbol}.{i}\""); - let synthetic_symbol = Symbol::intern(&synthetic_name); - let identifier = make_identifier(slf, synthetic_symbol); + // Convert the block into a function and a function call. + let mut block_to_function_rewriter = BlockToFunctionRewriter::new(self.state, self.current_program); + let (function, call_to_finalize) = + block_to_function_rewriter.rewrite_block(&input.block, finalize_fn_name, Variant::AsyncFunction); - let input = Input { - identifier, - mode: leo_ast::Mode::None, - type_: element_type.clone(), - span: Span::default(), - id: slf.state.node_builder.next_id(), - }; - - let expr: Expression = Path::from(identifier).into_absolute().into(); - - replacements.insert(key, expr.clone()); - tuple_elements.push(expr.clone()); - inputs_and_arguments.push(( - input, - TupleAccess { - tuple: Path::from(make_identifier(slf, symbol)).into_absolute().into(), - index: i.into(), - span: Span::default(), - id: slf.state.node_builder.next_id(), - } - .into(), - )); - } - - // Now insert the full tuple (even if all fields were already there) - replacements.insert( - (symbol, None), - Expression::Tuple(TupleExpression { - elements: tuple_elements, - span: Span::default(), - id: slf.state.node_builder.next_id(), - }), - ); - - inputs_and_arguments - } - - _ => { - let identifier = make_identifier(slf, symbol); - let input = Input { - identifier, - mode: leo_ast::Mode::None, - type_: var_type.clone(), - span: Span::default(), - id: slf.state.node_builder.next_id(), - }; - - replacements.insert((symbol, None), Path::from(identifier).into_absolute().into()); - - let argument = Path::from(make_identifier(slf, symbol)).into_absolute().into(); - vec![(input, argument)] - } - }, - } - }; - - // Step 3: Resolve symbol accesses into inputs and call arguments - let (inputs, arguments): (Vec<_>, Vec<_>) = access_collector - .symbol_accesses - .iter() - .filter_map(|(path, index)| { - // Skip globals and variables that are local to this block or to one of its children. - - // Skip globals. - if self.state.symbol_table.lookup_global(&Location::new(self.current_program, path.to_vec())).is_some() - { - return None; - } - - // Skip variables that are local to this block or to one of its children. - let local_var_name = *path.last().expect("all paths must have at least one segment."); - if self.state.symbol_table.is_local_to_or_in_child_scope(input.block.id(), local_var_name) { - return None; - } - - // All other variables become parameters to the async function being built. - let var = self.state.symbol_table.lookup_local(local_var_name)?; - Some(make_inputs_and_arguments(self, local_var_name, &var.type_, *index)) - }) - .flatten() - .unzip(); - - // Step 4: Replacement logic used to patch the async block - let replace_expr = |expr: &Expression| -> Expression { - match expr { - Expression::Path(path) => { - replacements.get(&(path.identifier().name, None)).cloned().unwrap_or_else(|| expr.clone()) - } - - Expression::TupleAccess(ta) => { - if let Expression::Path(path) = &ta.tuple { - replacements - .get(&(path.identifier().name, Some(ta.index.value()))) - .cloned() - .unwrap_or_else(|| expr.clone()) - } else { - expr.clone() - } - } - - _ => expr.clone(), - } - }; - - // Step 5: Reconstruct the block with replaced references - let mut replacer = Replacer::new(replace_expr, true /* refresh IDs */, self.state); - let new_block = replacer.reconstruct_block(input.block.clone()).0; - - // Ensure we're not trying to capture too many variables - if inputs.len() > self.max_inputs { + // Ensure we're not trying to capture too many variables. + if function.input.len() > self.max_inputs { self.state.handler.emit_err(leo_errors::StaticAnalyzerError::async_block_capturing_too_many_vars( - inputs.len(), + function.input.len(), self.max_inputs, input.span, )); } - // Step 6: Define the new async function - let function = Function { - annotations: vec![], - variant: Variant::AsyncFunction, - identifier: make_identifier(self, finalize_fn_name), - const_parameters: vec![], - input: inputs, - output: vec![], // `async function`s can't have returns - output_type: Type::Unit, // Always the case for `async function`s - block: new_block, - span: input.span, - id: self.state.node_builder.next_id(), - }; - // Register the generated function self.new_async_functions.push((finalize_fn_name, function)); - // Step 7: Create the call expression to invoke the async function - let call_to_finalize = CallExpression { - function: Path::new( - vec![], - make_identifier(self, finalize_fn_name), - true, - Some(vec![finalize_fn_name]), // the finalize function lives in the top level program scope - Span::default(), - self.state.node_builder.next_id(), - ), - const_arguments: vec![], - arguments, - program: Some(self.current_program), - span: input.span, - id: self.state.node_builder.next_id(), - }; - self.modified = true; - (call_to_finalize.into(), ()) + (call_to_finalize, ()) } fn reconstruct_block(&mut self, input: Block) -> (Block, Self::AdditionalOutput) { diff --git a/compiler/passes/src/processing_script/ast.rs b/compiler/passes/src/processing_script/ast.rs index 59b9018a2c6..074bd7b6edc 100644 --- a/compiler/passes/src/processing_script/ast.rs +++ b/compiler/passes/src/processing_script/ast.rs @@ -28,11 +28,10 @@ impl AstReconstructor for ProcessingScriptVisitor<'_> { if !matches!(self.current_variant, Variant::Script) { let callee_program = input.program.unwrap_or(self.program_name); - let Some(func_symbol) = self - .state - .symbol_table - .lookup_function(&Location::new(callee_program, input.function.absolute_path().to_vec())) - else { + let Some(func_symbol) = self.state.symbol_table.lookup_function( + self.program_name, + &Location::new(callee_program, input.function.absolute_path().to_vec()), + ) else { panic!("Type checking should have prevented this."); }; diff --git a/compiler/passes/src/ssa_const_propagation/mod.rs b/compiler/passes/src/ssa_const_propagation/mod.rs index 4551f65c797..dce7f9aa45e 100644 --- a/compiler/passes/src/ssa_const_propagation/mod.rs +++ b/compiler/passes/src/ssa_const_propagation/mod.rs @@ -24,6 +24,7 @@ use crate::Pass; use leo_ast::ProgramReconstructor as _; use leo_errors::Result; +use leo_span::Symbol; mod ast; @@ -44,7 +45,12 @@ impl Pass for SsaConstPropagation { // Run the pass in a loop until no changes are made. for _ in 0..1024 { let mut ast = std::mem::take(&mut state.ast); - let mut visitor = SsaConstPropagationVisitor { state, constants: Default::default(), changed: false }; + let mut visitor = SsaConstPropagationVisitor { + state, + program: Symbol::intern(""), + constants: Default::default(), + changed: false, + }; ast.ast = visitor.reconstruct_program(ast.ast); visitor.state.handler.last_err()?; visitor.state.ast = ast; diff --git a/compiler/passes/src/ssa_const_propagation/program.rs b/compiler/passes/src/ssa_const_propagation/program.rs index 1c1fbc9bd67..faf0108f4dc 100644 --- a/compiler/passes/src/ssa_const_propagation/program.rs +++ b/compiler/passes/src/ssa_const_propagation/program.rs @@ -16,9 +16,35 @@ use super::SsaConstPropagationVisitor; -use leo_ast::{AstReconstructor, Constructor, Function, ProgramReconstructor}; +use leo_ast::{AstReconstructor, Constructor, Function, ProgramReconstructor, ProgramScope, Statement}; impl ProgramReconstructor for SsaConstPropagationVisitor<'_> { + fn reconstruct_program_scope(&mut self, input: ProgramScope) -> ProgramScope { + self.program = input.program_id.name.name; + + ProgramScope { + program_id: input.program_id, + consts: input + .consts + .into_iter() + .map(|(i, c)| match self.reconstruct_const(c) { + (Statement::Const(declaration), _) => (i, declaration), + _ => panic!("`reconstruct_const` can only return `Statement::Const`"), + }) + .collect(), + composites: input.composites.into_iter().map(|(i, c)| (i, self.reconstruct_composite(c))).collect(), + mappings: input.mappings.into_iter().map(|(id, mapping)| (id, self.reconstruct_mapping(mapping))).collect(), + storage_variables: input + .storage_variables + .into_iter() + .map(|(id, storage_variable)| (id, self.reconstruct_storage_variable(storage_variable))) + .collect(), + functions: input.functions.into_iter().map(|(i, f)| (i, self.reconstruct_function(f))).collect(), + constructor: input.constructor.map(|c| self.reconstruct_constructor(c)), + span: input.span, + } + } + fn reconstruct_function(&mut self, mut input: Function) -> Function { // Reset the constants map for each function. // In SSA form, each function has its own scope, so we can clear the map. diff --git a/compiler/passes/src/ssa_const_propagation/visitor.rs b/compiler/passes/src/ssa_const_propagation/visitor.rs index e84c09e1752..480bf387942 100644 --- a/compiler/passes/src/ssa_const_propagation/visitor.rs +++ b/compiler/passes/src/ssa_const_propagation/visitor.rs @@ -16,7 +16,7 @@ use crate::CompilerState; -use leo_ast::{Expression, Node, NodeID, interpreter_value::Value}; +use leo_ast::{Expression, Location, Node, NodeID, interpreter_value::Value}; use leo_errors::StaticAnalyzerError; use leo_span::{Span, Symbol}; @@ -25,6 +25,8 @@ use indexmap::IndexMap; /// Visitor that propagates constant values through the program. pub struct SsaConstPropagationVisitor<'a> { pub state: &'a mut CompilerState, + /// Current program being analyzed. + pub program: Symbol, /// Maps variable names to their constant values. /// Only variables assigned constant values are tracked here. pub constants: IndexMap, @@ -44,16 +46,16 @@ impl SsaConstPropagationVisitor<'_> { pub fn value_to_expression(&mut self, value: &Value, span: Span, id: NodeID) -> Option<(Expression, NodeID)> { let ty = self.state.type_table.get(&id)?.clone(); let symbol_table = &self.state.symbol_table; - let struct_lookup = |sym: &[Symbol]| { + let struct_lookup = |loc: &Location| { symbol_table - .lookup_struct(sym) + .lookup_struct(self.program, loc) .unwrap() .members .iter() .map(|mem| (mem.identifier.name, mem.type_.clone())) .collect() }; - let new_expr = value.to_expression(span, &self.state.node_builder, &ty, &struct_lookup)?; + let new_expr = value.to_expression(span, &self.state.node_builder, self.program, &ty, &struct_lookup)?; let new_id = new_expr.id(); // Copy the type information to the new node ID. @@ -82,13 +84,14 @@ impl SsaConstPropagationVisitor<'_> { } } (Expression::Composite(composite_expr), Type::Composite(composite_ty)) => { + let program = composite_ty.program.unwrap_or(self.program); let symbols = composite_ty.path.as_symbols(); // We only look for structs here (not records) because `copy_types_recursively` is // only called from `value_to_expression`, which never produces record expressions. let member_types: Vec = self .state .symbol_table - .lookup_struct(&symbols) + .lookup_struct(self.program, &Location::new(program, symbols)) .map(|struct_def| struct_def.members.iter().map(|m| m.type_.clone()).collect()) .unwrap_or_default(); for (member, member_ty) in composite_expr.members.iter().zip(member_types.iter()) { diff --git a/compiler/passes/src/static_analysis/visitor.rs b/compiler/passes/src/static_analysis/visitor.rs index 594be37c5f2..7ada0e37633 100644 --- a/compiler/passes/src/static_analysis/visitor.rs +++ b/compiler/passes/src/static_analysis/visitor.rs @@ -38,7 +38,7 @@ impl StaticAnalyzingVisitor<'_> { } /// Emits a type checker warning - pub fn emit_warning(&self, warning: StaticAnalyzerWarning) { + pub fn emit_warning(&mut self, warning: StaticAnalyzerWarning) { self.state.handler.emit_warning(warning); } @@ -79,7 +79,7 @@ impl StaticAnalyzingVisitor<'_> { let func_symbol = self .state .symbol_table - .lookup_function(&Location::new(program, function_path.absolute_path().to_vec())) + .lookup_function(self.current_program, &Location::new(program, function_path.absolute_path().to_vec())) .expect("Type checking guarantees functions are present."); // If it is not an async transition, return. @@ -95,7 +95,7 @@ impl StaticAnalyzingVisitor<'_> { let async_function = self .state .symbol_table - .lookup_function(&finalizer.location) + .lookup_function(self.current_program, &finalizer.location) .expect("Type checking guarantees functions are present."); // If the async function takes a future as an argument, emit an error. @@ -136,7 +136,10 @@ impl AstVisitor for StaticAnalyzingVisitor<'_> { let func_symbol = self .state .symbol_table - .lookup_function(&Location::new(function_program, input.function.absolute_path().to_vec())) + .lookup_function( + self.current_program, + &Location::new(function_program, input.function.absolute_path().to_vec()), + ) .expect("Type checking guarantees functions exist."); if func_symbol.function.variant == Variant::Transition { diff --git a/compiler/passes/src/static_single_assignment/expression.rs b/compiler/passes/src/static_single_assignment/expression.rs index 21963aab321..28c590ce680 100644 --- a/compiler/passes/src/static_single_assignment/expression.rs +++ b/compiler/passes/src/static_single_assignment/expression.rs @@ -175,8 +175,13 @@ impl ExpressionConsumer for SsaFormingVisitor<'_> { let composite_definition: &Composite = self .state .symbol_table - .lookup_record(&Location::new(self.program, input.path.absolute_path())) - .or_else(|| self.state.symbol_table.lookup_struct(&input.path.absolute_path())) + .lookup_record(self.program, &Location::new(self.program, input.path.absolute_path())) + .or_else(|| { + self.state.symbol_table.lookup_struct( + self.program, + &Location::new(input.program.unwrap_or(self.program), input.path.absolute_path()), + ) + }) .expect("Type checking guarantees this definition exists."); // Initialize the list of reordered members. diff --git a/compiler/passes/src/static_single_assignment/program.rs b/compiler/passes/src/static_single_assignment/program.rs index 0eed69e3daf..2b4ea2eb5f5 100644 --- a/compiler/passes/src/static_single_assignment/program.rs +++ b/compiler/passes/src/static_single_assignment/program.rs @@ -34,6 +34,8 @@ use leo_ast::{ ProgramScope, ProgramScopeConsumer, StatementConsumer, + Stub, + StubConsumer, }; use leo_span::{Symbol, sym}; @@ -146,7 +148,7 @@ impl ProgramConsumer for SsaFormingVisitor<'_> { Program { modules: input.modules.into_iter().map(|(path, module)| (path, self.consume_module(module))).collect(), imports: input.imports, - stubs: input.stubs, + stubs: input.stubs.into_iter().map(|(name, stub)| (name, self.consume_stub(stub))).collect(), program_scopes: input .program_scopes .into_iter() @@ -156,10 +158,22 @@ impl ProgramConsumer for SsaFormingVisitor<'_> { } } +impl StubConsumer for SsaFormingVisitor<'_> { + type Output = Stub; + + fn consume_stub(&mut self, input: Stub) -> Self::Output { + match input { + Stub::FromLeo { program, .. } => self.consume_program(program).into(), + Stub::FromAleo { .. } => input, + } + } +} + impl ModuleConsumer for SsaFormingVisitor<'_> { type Output = Module; fn consume_module(&mut self, input: Module) -> Self::Output { + self.program = input.program_name; Module { path: input.path, program_name: self.program, diff --git a/compiler/passes/src/storage_lowering/ast.rs b/compiler/passes/src/storage_lowering/ast.rs index 501a51edd54..36061768790 100644 --- a/compiler/passes/src/storage_lowering/ast.rs +++ b/compiler/passes/src/storage_lowering/ast.rs @@ -619,7 +619,8 @@ impl leo_ast::AstReconstructor for StorageLoweringVisitor<'_> { fn reconstruct_path(&mut self, input: Path, _additional: &()) -> (Expression, Self::AdditionalOutput) { // Check if this path corresponds to a global symbol. - let Some(var) = self.state.symbol_table.lookup_global(&Location::new(self.program, input.absolute_path())) + let Some(var) = + self.state.symbol_table.lookup_global(self.program, &Location::new(self.program, input.absolute_path())) else { // Nothing to do return (input.into(), vec![]); @@ -780,7 +781,8 @@ impl leo_ast::AstReconstructor for StorageLoweringVisitor<'_> { // Check if `place` is a path if let Expression::Path(path) = &place { // Check if the path corresponds to a global storage variable - if let Some(var) = self.state.symbol_table.lookup_global(&Location::new(self.program, path.absolute_path())) + if let Some(var) = + self.state.symbol_table.lookup_global(self.program, &Location::new(self.program, path.absolute_path())) { // Storage variables that are not optional nor mappings are implicitly wrapped in an optional. assert!( diff --git a/compiler/passes/src/storage_lowering/visitor.rs b/compiler/passes/src/storage_lowering/visitor.rs index cdfdb58e112..723ea5e85b2 100644 --- a/compiler/passes/src/storage_lowering/visitor.rs +++ b/compiler/passes/src/storage_lowering/visitor.rs @@ -140,16 +140,16 @@ impl StorageLoweringVisitor<'_> { pub fn zero(&self, ty: &Type) -> Expression { // zero value for element type (used as default in get_or_use) let symbol_table = &self.state.symbol_table; - let struct_lookup = |sym: &[Symbol]| { + let struct_lookup = |loc: &Location| { symbol_table - .lookup_struct(sym) + .lookup_struct(self.program, loc) .unwrap() .members .iter() .map(|mem| (mem.identifier.name, mem.type_.clone())) .collect() }; - Expression::zero(ty, Span::default(), &self.state.node_builder, &struct_lookup) + Expression::zero(ty, Span::default(), &self.state.node_builder, self.program, &struct_lookup) .expect("zero value generation failed") } } diff --git a/compiler/passes/src/symbol_table_creation/mod.rs b/compiler/passes/src/symbol_table_creation/mod.rs index e8076d0319e..b14b6b4c3e2 100644 --- a/compiler/passes/src/symbol_table_creation/mod.rs +++ b/compiler/passes/src/symbol_table_creation/mod.rs @@ -14,9 +14,10 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use crate::{CompilerState, Pass, SymbolTable, VariableSymbol, VariableType}; +use crate::{CompilerState, Pass, VariableSymbol, VariableType}; use leo_ast::{ + AleoProgram, AstVisitor, Composite, ConstDeclaration, @@ -27,7 +28,6 @@ use leo_ast::{ MappingType, Module, OptionalType, - Program, ProgramScope, ProgramVisitor, StorageVariable, @@ -36,9 +36,9 @@ use leo_ast::{ Variant, }; use leo_errors::Result; -use leo_span::{Span, Symbol}; +use leo_span::Symbol; -use indexmap::IndexMap; +use indexmap::IndexSet; /// A pass to fill the SymbolTable. /// @@ -55,10 +55,9 @@ impl Pass for SymbolTableCreation { let ast = std::mem::take(&mut state.ast); let mut visitor = SymbolTableCreationVisitor { state, - structs: IndexMap::new(), program_name: Symbol::intern(""), + parents: IndexSet::new(), module: vec![], - is_stub: false, }; visitor.visit_program(ast.as_repr()); visitor.state.handler.last_err()?; @@ -74,10 +73,8 @@ struct SymbolTableCreationVisitor<'a> { program_name: Symbol, /// The current module name. module: Vec, - /// Whether or not traversing stub. - is_stub: bool, - /// The set of local structs that have been successfully visited. - structs: IndexMap, Span>, + /// The set of programs that import the program we're visiting. + parents: IndexSet, } impl SymbolTableCreationVisitor<'_> { @@ -113,7 +110,9 @@ impl ProgramVisitor for SymbolTableCreationVisitor<'_> { fn visit_program_scope(&mut self, input: &ProgramScope) { // Set current program name self.program_name = input.program_id.name.name; - self.is_stub = false; + + // Update the `imports` map in the symbol table. + self.state.symbol_table.add_imported_by(self.program_name, &self.parents); // Visit the program scope input.consts.iter().for_each(|(_, c)| self.visit_const(c)); @@ -135,27 +134,9 @@ impl ProgramVisitor for SymbolTableCreationVisitor<'_> { }) } - fn visit_import(&mut self, input: &Program) { - self.visit_program(input) - } - fn visit_composite(&mut self, input: &Composite) { - // Allow up to one local redefinition for each external composite. let full_name = self.module.iter().cloned().chain(std::iter::once(input.name())).collect::>(); - if !input.is_record { - if let Some(prev_span) = self.structs.get(&full_name) { - // The struct already existed - return self.state.handler.emit_err(SymbolTable::emit_shadow_error( - input.identifier.name, - input.identifier.span, - *prev_span, - )); - } - - self.structs.insert(full_name.clone(), input.identifier.span); - } - if input.is_record { // While records are not allowed in submodules, we stll use their full name in the records table. // We don't expect the full name to have more than a single Symbol though. @@ -165,8 +146,15 @@ impl ProgramVisitor for SymbolTableCreationVisitor<'_> { { self.state.handler.emit_err(err); } - } else if let Err(err) = self.state.symbol_table.insert_struct(self.program_name, &full_name, input.clone()) { - self.state.handler.emit_err(err); + } else { + // First, insert for the main program. + if let Err(err) = self + .state + .symbol_table + .insert_struct(Location::new(self.program_name, full_name.clone()), input.clone()) + { + self.state.handler.emit_err(err); + } } } @@ -215,8 +203,24 @@ impl ProgramVisitor for SymbolTableCreationVisitor<'_> { } fn visit_stub(&mut self, input: &Stub) { - self.is_stub = true; + match input { + Stub::FromLeo { program, parents } => { + self.parents = parents.clone(); + self.visit_program(program); + } + Stub::FromAleo { program, parents } => { + self.parents = parents.clone(); + self.visit_aleo_program(program); + } + } + } + + fn visit_aleo_program(&mut self, input: &AleoProgram) { self.program_name = input.stub_id.name.name; + + // Update the `imports` map in the symbol table. + self.state.symbol_table.add_imported_by(self.program_name, &self.parents); + input.functions.iter().for_each(|(_, c)| self.visit_function_stub(c)); input.composites.iter().for_each(|(_, c)| self.visit_composite_stub(c)); input.mappings.iter().for_each(|(_, c)| self.visit_mapping(c)); @@ -261,7 +265,7 @@ impl ProgramVisitor for SymbolTableCreationVisitor<'_> { self.state.handler.emit_err(err); } } else if let Err(err) = - self.state.symbol_table.insert_struct(self.program_name, &[input.name()], input.clone()) + self.state.symbol_table.insert_struct(Location::new(self.program_name, vec![input.name()]), input.clone()) { self.state.handler.emit_err(err); } diff --git a/compiler/passes/src/test_passes.rs b/compiler/passes/src/test_passes.rs index f981d324cd3..e05f7b59e84 100644 --- a/compiler/passes/src/test_passes.rs +++ b/compiler/passes/src/test_passes.rs @@ -105,6 +105,7 @@ use leo_errors::{BufferEmitter, Handler}; use leo_parser::parse_ast; use leo_span::{create_session_if_not_set_then, source_map::FileName, with_session_globals}; use serial_test::serial; +use std::rc::Rc; /// Table of all compiler passes and their runner names. /// Each entry is a tuple of `(runner_name, [(pass_struct, input), ...])` @@ -232,9 +233,10 @@ macro_rules! make_runner { fn $runner_name(source: &str) -> String { let buf = BufferEmitter::new(); let handler = Handler::new(buf.clone()); + let node_builder = Rc::new(leo_ast::NodeBuilder::default()); create_session_if_not_set_then(|_| { - let mut state = CompilerState { handler: handler.clone(), ..Default::default() }; + let mut state = CompilerState { handler: handler.clone(), node_builder: Rc::clone(&node_builder), ..Default::default() }; state.ast = match handler.extend_if_error(parse_ast( handler.clone(), diff --git a/compiler/passes/src/type_checking/ast.rs b/compiler/passes/src/type_checking/ast.rs index de796e42b8e..1c5cec3c71b 100644 --- a/compiler/passes/src/type_checking/ast.rs +++ b/compiler/passes/src/type_checking/ast.rs @@ -15,7 +15,7 @@ // along with the Leo library. If not, see . use super::*; -use crate::{VariableSymbol, VariableType}; +use crate::{BlockToFunctionRewriter, VariableSymbol, VariableType}; use leo_ast::{ Type::{Future, Tuple}, @@ -239,9 +239,9 @@ impl TypeCheckingVisitor<'_> { // Returns the type of the RHS of an assign statement if it's a `Path`. // Also returns whether the RHS is a storage location. pub fn visit_path_assign(&mut self, input: &Path) -> (Type, bool) { + let current_program = self.scope_state.program_name.unwrap(); // Lookup the variable in the symbol table and retrieve its type. - let Some(var) = - self.state.symbol_table.lookup_path(self.scope_state.program_name.unwrap(), &input.absolute_path()) + let Some(var) = self.state.symbol_table.lookup_path(current_program, current_program, &input.absolute_path()) else { self.emit_err(TypeCheckerError::unknown_sym("variable", input, input.span)); return (Type::Err, false); @@ -356,6 +356,7 @@ impl AstVisitor for TypeCheckingVisitor<'_> { self.visit_expression(argument, &Some(expected.type_().clone())); } } else if !input.const_arguments.is_empty() { + // This handles erroring out on all non-structs self.emit_err(TypeCheckerError::unexpected_const_args(input, input.path.span)); } } @@ -549,6 +550,23 @@ impl AstVisitor for TypeCheckingVisitor<'_> { // This scope now already has an async block self.scope_state.already_contains_an_async_block = true; + // Here we convert the async block to an async function using the helper + // `BlockToFunctionRewriter`. We do not actually replace anything in the original AST. We + // just inspect how the async block would look like as an async function in order to + // populate the map `async_function_input_types`. + let mut block_to_function_rewriter = + BlockToFunctionRewriter::new(self.state, self.scope_state.program_name.unwrap()); + let (new_function, _) = + block_to_function_rewriter.rewrite_block(&input.block, Symbol::intern("unused"), Variant::AsyncFunction); + let input_types = new_function.input.iter().map(|Input { type_, .. }| type_.clone()).collect(); + self.async_function_input_types.insert( + Location::new(self.scope_state.program_name.unwrap(), vec![Symbol::intern(&format!( + "finalize/{}", + self.scope_state.function.unwrap(), + ))]), + input_types, + ); + // Step out of the async block self.async_block_id = None; @@ -558,10 +576,12 @@ impl AstVisitor for TypeCheckingVisitor<'_> { } fn visit_binary(&mut self, input: &BinaryExpression, destination: &Self::AdditionalInput) -> Self::Output { + let current_program = self.scope_state.program_name.unwrap(); + let assert_same_type = |slf: &Self, t1: &Type, t2: &Type| -> Type { if t1 == &Type::Err || t2 == &Type::Err { Type::Err - } else if !t1.eq_user(t2) { + } else if !t1.eq_user(t2, current_program) { slf.emit_err(TypeCheckerError::operation_types_mismatch(input.op, t1, t2, input.span())); Type::Err } else { @@ -954,12 +974,15 @@ impl AstVisitor for TypeCheckingVisitor<'_> { } fn visit_call(&mut self, input: &CallExpression, expected: &Self::AdditionalInput) -> Self::Output { + let current_program = self.scope_state.program_name.unwrap(); let callee_program = input.program.or(self.scope_state.program_name).unwrap(); let callee_path = input.function.absolute_path(); - let Some(func_symbol) = - self.state.symbol_table.lookup_function(&Location::new(callee_program, callee_path.clone())) + let Some(func_symbol) = self + .state + .symbol_table + .lookup_function(current_program, &Location::new(callee_program, callee_path.clone())) else { self.emit_err(TypeCheckerError::unknown_sym("function", input.function.clone(), input.function.span())); return Type::Err; @@ -975,13 +998,24 @@ impl AstVisitor for TypeCheckingVisitor<'_> { ), Variant::Transition | Variant::AsyncTransition if matches!(func.variant, Variant::Transition) - && input.program.is_none_or(|program| program == self.scope_state.program_name.unwrap()) => + && input.program.is_none_or(|program| program == current_program) => { self.emit_err(TypeCheckerError::cannot_invoke_call_to_local_transition_function(input.span)) } _ => {} } + // TODO: do this the right way... this is too sloppy + /*let output_type = if let Some(program) = input.program { + if let Type::Composite(composite) = func.output_type { + Type::Composite(CompositeType { program: Some(program), ..composite }) + } else { + func.output_type + } + } else { + func.output_type + };*/ + // Check that the call is not to an external `inline` function. if func.variant == Variant::Inline && input.program.is_some_and(|program| program != self.scope_state.program_name.unwrap()) @@ -1183,7 +1217,7 @@ impl AstVisitor for TypeCheckingVisitor<'_> { self.state .symbol_table .attach_finalizer( - Location::new(callee_program, caller_path), + Location::new(callee_program, caller_path.clone()), Location::new(callee_program, callee_path.clone()), input_futures, inferred_finalize_inputs.clone(), @@ -1200,11 +1234,19 @@ impl AstVisitor for TypeCheckingVisitor<'_> { // Update ret to reflect fully inferred future type. ret = Type::Future(FutureType::new( - inferred_finalize_inputs, + inferred_finalize_inputs.clone(), Some(Location::new(callee_program, callee_path.clone())), true, )); + self.async_function_input_types.insert( + Location::new(callee_program, vec![Symbol::intern(&format!( + "finalize/{}", + caller_path.last().unwrap() + ))]), + inferred_finalize_inputs.clone(), + ); + // Type check in case the expected type is known. self.assert_and_return_type(ret.clone(), expected, input.span()); } @@ -1245,7 +1287,8 @@ impl AstVisitor for TypeCheckingVisitor<'_> { input: &CompositeExpression, additional: &Self::AdditionalInput, ) -> Self::Output { - let composite = self.lookup_composite(self.scope_state.program_name, &input.path.absolute_path()).clone(); + let composite = + self.lookup_composite(input.program.or(self.scope_state.program_name), &input.path.absolute_path()); let Some(composite) = composite else { self.emit_err(TypeCheckerError::unknown_sym("struct or record", input.path.clone(), input.path.span())); return Type::Err; @@ -1266,12 +1309,10 @@ impl AstVisitor for TypeCheckingVisitor<'_> { self.visit_expression(argument, &Some(expected.type_().clone())); } - // Note that it is sufficient for the `program` to be `None` as composite types can only be initialized - // in the program in which they are defined. let type_ = Type::Composite(CompositeType { path: input.path.clone(), const_arguments: input.const_arguments.clone(), - program: None, + program: input.program, }); self.maybe_assert_type(&type_, additional, input.path.span()); @@ -1320,6 +1361,17 @@ impl AstVisitor for TypeCheckingVisitor<'_> { } if composite.is_record { + // Ensure that we're not instantating an external record + if input.program.is_some_and(|program| program != self.scope_state.program_name.unwrap()) { + self.state.handler.emit_err(TypeCheckerError::cannot_instantiate_external_record( + Location::new( + input.program.unwrap_or(self.scope_state.program_name.unwrap()), + input.path.absolute_path(), + ), + input.span(), + )); + } + // First, ensure that the current scope is not an async function. Records should not be instantiated in // async functions if self.scope_state.variant == Some(Variant::AsyncFunction) { @@ -1356,7 +1408,8 @@ impl AstVisitor for TypeCheckingVisitor<'_> { } fn visit_path(&mut self, input: &Path, expected: &Self::AdditionalInput) -> Self::Output { - let var = self.state.symbol_table.lookup_path(self.scope_state.program_name.unwrap(), &input.absolute_path()); + let current_program = self.scope_state.program_name.unwrap(); + let var = self.state.symbol_table.lookup_path(current_program, current_program, &input.absolute_path()); if let Some(var) = var { if var.declaration == VariableType::Storage && !var.type_.is_vector() && !var.type_.is_mapping() { @@ -1457,8 +1510,14 @@ impl AstVisitor for TypeCheckingVisitor<'_> { } fn visit_locator(&mut self, input: &LocatorExpression, expected: &Self::AdditionalInput) -> Self::Output { - let maybe_var = - self.state.symbol_table.lookup_global(&Location::new(input.program.name.name, vec![input.name])).cloned(); + let maybe_var = self + .state + .symbol_table + .lookup_global( + self.scope_state.program_name.unwrap(), + &Location::new(input.program.name.name, vec![input.name]), + ) + .cloned(); if let Some(var) = maybe_var { self.maybe_assert_type(&var.type_, expected, input.span()); var.type_ @@ -1469,6 +1528,8 @@ impl AstVisitor for TypeCheckingVisitor<'_> { } fn visit_ternary(&mut self, input: &TernaryExpression, expected: &Self::AdditionalInput) -> Self::Output { + let current_program = self.scope_state.program_name.unwrap(); + self.visit_expression(&input.condition, &Some(Type::Boolean)); // We try to coerce one side to another in the ternary operator whenever possible and/or needed. @@ -1512,12 +1573,12 @@ impl AstVisitor for TypeCheckingVisitor<'_> { let typ = if t1 == Type::Err || t2 == Type::Err { Type::Err - } else if !t1.can_coerce_to(&t2) && !t2.can_coerce_to(&t1) { + } else if !t1.can_coerce_to(&t2, current_program) && !t2.can_coerce_to(&t1, current_program) { self.emit_err(TypeCheckerError::ternary_branch_mismatch(t1, t2, input.span())); Type::Err } else if let Some(expected) = expected { expected.clone() - } else if t1.can_coerce_to(&t2) { + } else if t1.can_coerce_to(&t2, current_program) { t2 } else { t1 @@ -1749,6 +1810,7 @@ impl AstVisitor for TypeCheckingVisitor<'_> { } fn visit_assert(&mut self, input: &AssertStatement) { + let current_program = self.scope_state.program_name.unwrap(); match &input.variant { AssertVariant::Assert(expr) => { let _type = self.visit_expression(expr, &Some(Type::Boolean)); @@ -1757,10 +1819,10 @@ impl AstVisitor for TypeCheckingVisitor<'_> { let t1 = self.visit_expression_reject_numeric(left, &None); let t2 = self.visit_expression_reject_numeric(right, &None); - if t1 != Type::Err && t2 != Type::Err && !t1.eq_user(&t2) { + if t1 != Type::Err && t2 != Type::Err && !t1.eq_user(&t2, current_program) { let op = if matches!(input.variant, AssertVariant::AssertEq(..)) { "assert_eq" } else { "assert_neq" }; - self.emit_err(TypeCheckerError::operation_types_mismatch(op, t1, t2, input.span())); + self.emit_err(TypeCheckerError::operation_types_mismatch(op, &t1, &t2, input.span())); } } } @@ -2067,10 +2129,12 @@ impl AstVisitor for TypeCheckingVisitor<'_> { let caller_path = self.scope_state.module_name.iter().cloned().chain(std::iter::once(caller_name)).collect::>(); + let current_program = self.scope_state.program_name.unwrap(); + let func_symbol = self .state .symbol_table - .lookup_function(&Location::new(self.scope_state.program_name.unwrap(), caller_path.clone())) + .lookup_function(current_program, &Location::new(current_program, caller_path.clone())) .expect("The symbol table creator should already have visited all functions."); let mut return_type = func_symbol.function.output_type.clone(); @@ -2078,7 +2142,7 @@ impl AstVisitor for TypeCheckingVisitor<'_> { if self.scope_state.variant == Some(Variant::AsyncTransition) && self.scope_state.has_called_finalize { let inferred_future_type = Future(FutureType::new( if let Some(finalizer) = &func_symbol.finalizer { finalizer.inferred_inputs.clone() } else { vec![] }, - Some(Location::new(self.scope_state.program_name.unwrap(), caller_path)), + Some(Location::new(current_program, caller_path)), true, )); diff --git a/compiler/passes/src/type_checking/mod.rs b/compiler/passes/src/type_checking/mod.rs index dcf654b3708..7fb63f820f7 100644 --- a/compiler/passes/src/type_checking/mod.rs +++ b/compiler/passes/src/type_checking/mod.rs @@ -88,8 +88,8 @@ impl Pass for TypeChecking { let composite_names = state .symbol_table .iter_records() - .map(|(loc, _)| loc.path.clone()) - .chain(state.symbol_table.iter_structs().map(|(name, _)| name.clone())) + .map(|(loc, _)| loc.clone()) + .chain(state.symbol_table.iter_structs().map(|(loc, _)| loc.clone())) .collect(); let function_names = state.symbol_table.iter_functions().map(|(loc, _)| loc.clone()).collect(); diff --git a/compiler/passes/src/type_checking/program.rs b/compiler/passes/src/type_checking/program.rs index 8dc8ce11dda..7e38ed984dc 100644 --- a/compiler/passes/src/type_checking/program.rs +++ b/compiler/passes/src/type_checking/program.rs @@ -29,13 +29,15 @@ impl ProgramVisitor for TypeCheckingVisitor<'_> { fn visit_program(&mut self, input: &Program) { // Typecheck the program's stubs. input.stubs.iter().for_each(|(symbol, stub)| { - // Check that naming and ordering is consistent. - if symbol != &stub.stub_id.name.name { - self.emit_err(TypeCheckerError::stub_name_mismatch( - symbol, - stub.stub_id.name, - stub.stub_id.network.span, - )); + if let Stub::FromAleo { program, .. } = stub { + // Check that naming and ordering is consistent. + if symbol != &program.stub_id.name.name { + self.emit_err(TypeCheckerError::stub_name_mismatch( + symbol, + program.stub_id.name, + program.stub_id.network.span, + )); + } } self.visit_stub(stub) }); @@ -82,7 +84,7 @@ impl ProgramVisitor for TypeCheckingVisitor<'_> { // Check that the composite dependency graph does not have any cycles. if let Err(DiGraphError::CycleDetected(path)) = self.state.composite_graph.post_order() { self.emit_err(TypeCheckerError::cyclic_composite_dependency( - path.iter().map(|p| p.iter().format("::")).collect(), + path.iter().map(|loc| loc.to_string()).collect(), )); } @@ -161,7 +163,7 @@ impl ProgramVisitor for TypeCheckingVisitor<'_> { self.scope_state.module_name = parent_module; } - fn visit_stub(&mut self, input: &Stub) { + fn visit_aleo_program(&mut self, input: &AleoProgram) { // Set the scope state. self.scope_state.program_name = Some(input.stub_id.name.name); self.scope_state.is_stub = true; @@ -305,18 +307,28 @@ impl ProgramVisitor for TypeCheckingVisitor<'_> { .cloned() .chain(std::iter::once(input.identifier.name)) .collect::>(); + let this_program = self.scope_state.program_name.unwrap(); if let Type::Composite(composite_member_type) = type_ { - // Note that since there are no cycles in the program dependency graph, there are no cycles in the - // composite dependency graph caused by external composites. - self.state - .composite_graph - .add_edge(composite_path, composite_member_type.path.absolute_path().to_vec()); + // Note that since there are no cycles in the program dependency graph, there are no cycles in the composite dependency graph caused by external composites. + self.state.composite_graph.add_edge( + Location::new(this_program, composite_path), + Location::new( + composite_member_type.program.unwrap_or(this_program), + composite_member_type.path.absolute_path().to_vec(), + ), + ); } else if let Type::Array(array_type) = type_ { // Get the base element type. let base_element_type = array_type.base_element_type(); // If the base element type is a composite, then add it to the composite dependency graph. if let Type::Composite(member_type) = base_element_type { - self.state.composite_graph.add_edge(composite_path, member_type.path.absolute_path().to_vec()); + self.state.composite_graph.add_edge( + Location::new(this_program, composite_path), + Location::new( + member_type.program.unwrap_or(this_program), + member_type.path.absolute_path().to_vec(), + ), + ); } } @@ -576,7 +588,7 @@ impl ProgramVisitor for TypeCheckingVisitor<'_> { if let UpgradeVariant::Checksum { mapping, key, key_type } = &upgrade_variant { // Look up the mapping type. let Some(VariableSymbol { type_: Type::Mapping(mapping_type), .. }) = - self.state.symbol_table.lookup_global(mapping) + self.state.symbol_table.lookup_global(self.scope_state.program_name.unwrap(), mapping) else { self.emit_err(TypeCheckerError::custom( format!("The mapping '{mapping}' does not exist. Please ensure that it is imported or defined in your program."), diff --git a/compiler/passes/src/type_checking/visitor.rs b/compiler/passes/src/type_checking/visitor.rs index 702184b0af1..ab38ee46de5 100644 --- a/compiler/passes/src/type_checking/visitor.rs +++ b/compiler/passes/src/type_checking/visitor.rs @@ -40,7 +40,7 @@ pub struct TypeCheckingVisitor<'a> { /// Mapping from async function name to the names of async transition callers. pub async_function_callers: IndexMap>, /// The set of used composites. - pub used_composites: IndexSet>, + pub used_composites: IndexSet, /// So we can check if we exceed limits on array size, number of mappings, or number of functions. pub limits: TypeCheckingInput, /// For detecting the error `TypeCheckerError::async_cannot_assign_outside_conditional`. @@ -86,8 +86,9 @@ impl TypeCheckingVisitor<'_> { /// Emits an error if the two given types are not equal. pub fn check_eq_types(&self, t1: &Option, t2: &Option, span: Span) { + let current_program = self.scope_state.program_name.unwrap(); match (t1, t2) { - (Some(t1), Some(t2)) if !t1.eq_flat_relaxed(t2) => { + (Some(t1), Some(t2)) if !t1.eq_user(t2, current_program) => { self.emit_err(TypeCheckerError::type_should_be(t1, t2, span)) } (Some(type_), None) | (None, Some(type_)) => { @@ -114,7 +115,8 @@ impl TypeCheckingVisitor<'_> { } pub fn assert_type(&mut self, actual: &Type, expected: &Type, span: Span) { - if actual != &Type::Err && !actual.can_coerce_to(expected) { + let current_program = self.scope_state.program_name.unwrap(); + if actual != &Type::Err && !actual.can_coerce_to(expected, current_program) { // If `actual` is Err, we will have already reported an error. self.emit_err(TypeCheckerError::type_should_be2(actual, format!("type `{expected}`"), span)); } @@ -470,6 +472,10 @@ impl TypeCheckingVisitor<'_> { // Define a regex to match valid program IDs. let program_id_regex = regex::Regex::new(r"^[a-zA-Z][a-zA-Z0-9_]*\.aleo$").unwrap(); + fn struct_not_supported(_: &T) -> anyhow::Result { + bail!("structs are not supported") + } + // Check that the arguments are of the correct type. match intrinsic { Intrinsic::Commit(variant, type_) => { @@ -494,12 +500,21 @@ impl TypeCheckingVisitor<'_> { let input_type = &arguments[0].0; // Get the size in bits. let size_in_bits = match self.state.network { - NetworkName::TestnetV0 => input_type - .size_in_bits::(variant.is_raw(), |_| bail!("structs are not supported")), - NetworkName::MainnetV0 => input_type - .size_in_bits::(variant.is_raw(), |_| bail!("structs are not supported")), - NetworkName::CanaryV0 => input_type - .size_in_bits::(variant.is_raw(), |_| bail!("structs are not supported")), + NetworkName::TestnetV0 => input_type.size_in_bits::( + variant.is_raw(), + &struct_not_supported, + &struct_not_supported, + ), + NetworkName::MainnetV0 => input_type.size_in_bits::( + variant.is_raw(), + &struct_not_supported, + &struct_not_supported, + ), + NetworkName::CanaryV0 => input_type.size_in_bits::( + variant.is_raw(), + &struct_not_supported, + &struct_not_supported, + ), }; if let Ok(size_in_bits) = size_in_bits { // Check that the size in bits is a multiple of 8. @@ -641,12 +656,21 @@ impl TypeCheckingVisitor<'_> { let input_type = &arguments[2].0; // Get the size in bits. let size_in_bits = match self.state.network { - NetworkName::TestnetV0 => input_type - .size_in_bits::(variant.is_raw(), |_| bail!("structs are not supported")), - NetworkName::MainnetV0 => input_type - .size_in_bits::(variant.is_raw(), |_| bail!("structs are not supported")), - NetworkName::CanaryV0 => input_type - .size_in_bits::(variant.is_raw(), |_| bail!("structs are not supported")), + NetworkName::TestnetV0 => input_type.size_in_bits::( + variant.is_raw(), + &struct_not_supported, + &struct_not_supported, + ), + NetworkName::MainnetV0 => input_type.size_in_bits::( + variant.is_raw(), + &struct_not_supported, + &struct_not_supported, + ), + NetworkName::CanaryV0 => input_type.size_in_bits::( + variant.is_raw(), + &struct_not_supported, + &struct_not_supported, + ), }; if let Ok(size_in_bits) = size_in_bits { // Check that the size in bits is a multiple of 8. @@ -969,13 +993,13 @@ impl TypeCheckingVisitor<'_> { // Get the size in bits. let size_in_bits = match self.state.network { NetworkName::TestnetV0 => { - input_type.size_in_bits::(is_raw, |_| bail!("structs are not supported")) + input_type.size_in_bits::(is_raw, &struct_not_supported, &struct_not_supported) } NetworkName::MainnetV0 => { - input_type.size_in_bits::(is_raw, |_| bail!("structs are not supported")) + input_type.size_in_bits::(is_raw, &struct_not_supported, &struct_not_supported) } NetworkName::CanaryV0 => { - input_type.size_in_bits::(is_raw, |_| bail!("structs are not supported")) + input_type.size_in_bits::(is_raw, &struct_not_supported, &struct_not_supported) } }; @@ -1016,13 +1040,13 @@ impl TypeCheckingVisitor<'_> { // Get the size in bits. let size_in_bits = match self.state.network { NetworkName::TestnetV0 => { - type_.size_in_bits::(is_raw, |_| bail!("structs are not supported")) + type_.size_in_bits::(is_raw, &struct_not_supported, &struct_not_supported) } NetworkName::MainnetV0 => { - type_.size_in_bits::(is_raw, |_| bail!("structs are not supported")) + type_.size_in_bits::(is_raw, struct_not_supported, struct_not_supported) } NetworkName::CanaryV0 => { - type_.size_in_bits::(is_raw, |_| bail!("structs are not supported")) + type_.size_in_bits::(is_raw, &struct_not_supported, &struct_not_supported) } }; @@ -1498,7 +1522,7 @@ impl TypeCheckingVisitor<'_> { .iter() .flat_map(|caller| { let caller = Location::new(caller.program, caller.path.clone()); - self.state.symbol_table.lookup_function(&caller) + self.state.symbol_table.lookup_function(self.scope_state.program_name.unwrap(), &caller) }) .flat_map(|fn_symbol| fn_symbol.finalizer.clone()) }) @@ -1513,7 +1537,7 @@ impl TypeCheckingVisitor<'_> { for finalizer in caller_finalizers { assert_eq!(inferred_inputs.len(), finalizer.inferred_inputs.len()); for (t1, t2) in inferred_inputs.iter_mut().zip(finalizer.inferred_inputs.iter()) { - Self::merge_types(t1, t2); + self.merge_types(t1, t2); } } } else { @@ -1730,12 +1754,13 @@ impl TypeCheckingVisitor<'_> { /// That is, if `lhs` and `rhs` aren't equal, set `lhs` to Type::Err; /// or, if they're both futures, set any member of `lhs` that isn't /// equal to the equivalent member of `rhs` to `Type::Err`. - fn merge_types(lhs: &mut Type, rhs: &Type) { + fn merge_types(&self, lhs: &mut Type, rhs: &Type) { + let current_program = self.scope_state.program_name.unwrap(); if let Type::Future(f1) = lhs { if let Type::Future(f2) = rhs { for (i, type_) in f2.inputs.iter().enumerate() { if let Some(lhs_type) = f1.inputs.get_mut(i) { - Self::merge_types(lhs_type, type_); + self.merge_types(lhs_type, type_); } else { f1.inputs.push(Type::Err); } @@ -1743,22 +1768,30 @@ impl TypeCheckingVisitor<'_> { } else { *lhs = Type::Err; } - } else if !lhs.eq_user(rhs) { + } else if !lhs.eq_user(rhs, current_program) { *lhs = Type::Err; } } - /// Wrapper around lookup_struct and lookup_record that additionally records all structs and records that are - /// used in the program. + /// Wrapper around lookup_composite that additionally records all composite that are used in the program. pub fn lookup_composite(&mut self, program: Option, name: &[Symbol]) -> Option { - let record_comp = - program.and_then(|prog| self.state.symbol_table.lookup_record(&Location::new(prog, name.to_vec()))); - let comp = record_comp.or_else(|| self.state.symbol_table.lookup_struct(name)); + let current_program = self.scope_state.program_name.unwrap(); + let record_comp = program.and_then(|prog| { + self.state.symbol_table.lookup_record(current_program, &Location::new(prog, name.to_vec())) + }); + let comp = record_comp.or_else(|| { + program.and_then(|prog| { + self.state.symbol_table.lookup_struct(current_program, &Location::new(prog, name.to_vec())) + }) + }); // Record the usage. if let Some(s) = comp { // If it's a struct or internal record, mark it used. if !s.is_record || program == self.scope_state.program_name { - self.used_composites.insert(name.to_vec()); + self.used_composites.insert(Location::new( + program.expect("`program` can't be `None` if we're inside the `if let`"), + name.to_vec(), + )); } } comp.cloned() @@ -1827,7 +1860,7 @@ impl TypeCheckingVisitor<'_> { && self .state .symbol_table - .lookup_record(&Location::new(program, typ.path.absolute_path().to_vec())) + .lookup_record(this_program, &Location::new(program, typ.path.absolute_path().to_vec())) .is_some() } else { false diff --git a/compiler/passes/src/write_transforming/ast.rs b/compiler/passes/src/write_transforming/ast.rs index 9aee11659a1..3a30971fdcc 100644 --- a/compiler/passes/src/write_transforming/ast.rs +++ b/compiler/passes/src/write_transforming/ast.rs @@ -91,6 +91,7 @@ impl WriteTransformingVisitor<'_> { } }) .collect(), + program: None, path: comp_type.path, span: Default::default(), id, diff --git a/compiler/passes/src/write_transforming/visitor.rs b/compiler/passes/src/write_transforming/visitor.rs index a36aa385c46..8f4e50aa84c 100644 --- a/compiler/passes/src/write_transforming/visitor.rs +++ b/compiler/passes/src/write_transforming/visitor.rs @@ -310,12 +310,15 @@ impl WriteTransformingFiller<'_> { .0 .state .symbol_table - .lookup_struct(&comp.path.absolute_path()) + .lookup_struct( + self.0.program, + &Location::new(comp.program.unwrap_or(self.0.program), comp.path.absolute_path()), + ) .or_else(|| { - self.0.state.symbol_table.lookup_record(&Location::new( - comp.program.unwrap_or(self.0.program), - comp.path.absolute_path(), - )) + self.0.state.symbol_table.lookup_record( + self.0.program, + &Location::new(comp.program.unwrap_or(self.0.program), comp.path.absolute_path()), + ) }) .unwrap(); composite diff --git a/errors/src/errors/type_checker/type_checker_error.rs b/errors/src/errors/type_checker/type_checker_error.rs index 5a355594d0f..96dab9fd2da 100644 --- a/errors/src/errors/type_checker/type_checker_error.rs +++ b/errors/src/errors/type_checker/type_checker_error.rs @@ -1384,4 +1384,12 @@ create_messages!( help: None, } + @formatted + cannot_instantiate_external_record { + args: (loc: impl Display), + msg: format!( + "Cannot create external record `{loc}`. Records can only be created in the program that they are defined in", + ), + help: None, + } ); diff --git a/interpreter/Cargo.toml b/interpreter/Cargo.toml index 447644ebe6e..510bebc9dbc 100644 --- a/interpreter/Cargo.toml +++ b/interpreter/Cargo.toml @@ -29,6 +29,9 @@ workspace = true [dependencies.leo-parser] workspace = true +[dependencies.leo-passes] +workspace = true + [dependencies.leo-span] workspace = true diff --git a/interpreter/src/cursor_aleo.rs b/interpreter/src/cursor_aleo.rs index 72f3fb19fe8..465f25cd0a4 100644 --- a/interpreter/src/cursor_aleo.rs +++ b/interpreter/src/cursor_aleo.rs @@ -421,6 +421,7 @@ impl Cursor { ); (value, destination) } + CastType::Plaintext(PlaintextType::ExternalStruct(_struct_name)) => todo!(), CastType::Record(record_name) => { let program = self.current_program().unwrap(); let name = Symbol::intern(&record_name.to_string()); diff --git a/interpreter/src/test_interpreter.rs b/interpreter/src/test_interpreter.rs index 9b5581ec529..183de316730 100644 --- a/interpreter/src/test_interpreter.rs +++ b/interpreter/src/test_interpreter.rs @@ -16,9 +16,8 @@ //! These tests compare interpreter runs against ledger runs. -use leo_ast::{NetworkName, Stub, interpreter_value::Value}; +use leo_ast::{Bytecode, CompiledPrograms, NetworkName, NodeBuilder, Program, Stub, interpreter_value::Value}; use leo_compiler::{Compiler, run}; -use leo_disassembler::disassemble_from_str; use leo_errors::{BufferEmitter, Handler, Result}; use leo_span::{Symbol, create_session_if_not_set_then, source_map::FileName}; @@ -29,6 +28,7 @@ use itertools::Itertools as _; use std::{ fs, path::{Path, PathBuf}, + rc::Rc, str::FromStr, }; use walkdir::WalkDir; @@ -39,11 +39,17 @@ const PROGRAM_DELIMITER: &str = "// --- Next Program --- //"; type CurrentNetwork = TestnetV0; -fn whole_compile(source: &str, handler: &Handler, import_stubs: IndexMap) -> Result<(String, String)> { +fn whole_compile( + source: &str, + handler: &Handler, + node_builder: &Rc, + import_stubs: IndexMap, +) -> Result<(CompiledPrograms, String)> { let mut compiler = Compiler::new( None, /* is_test (a Leo test) */ false, handler.clone(), + node_builder.clone(), "/fakedirectory-wont-use".into(), None, import_stubs, @@ -57,6 +63,26 @@ fn whole_compile(source: &str, handler: &Handler, import_stubs: IndexMap) -> Result<(Program, String)> { + let mut compiler = Compiler::new( + None, + /* is_test (a Leo test) */ false, + handler.clone(), + node_builder.clone(), + "/fakedirectory-wont-use".into(), + None, + IndexMap::new(), + NetworkName::TestnetV0, + ); + + let filename = FileName::Custom("execution-test".into()); + + let program = compiler.parse_and_return_ast(source, filename, &[])?; + + Ok((program, compiler.program_name.unwrap())) +} + fn parse_cases(source: &str) -> (Vec, Vec) { let mut cases: Vec = Vec::new(); @@ -88,24 +114,42 @@ pub struct TestResult { interpreter_result: Vec, } -fn run_test(path: &Path, handler: &Handler, _buf: &BufferEmitter) -> Result { +fn run_test( + path: &Path, + handler: &Handler, + node_builder: &Rc, + _buf: &BufferEmitter, +) -> Result { let source = fs::read_to_string(path).unwrap_or_else(|e| panic!("Failed to read file {}: {e}.", path.display())); let (cases, sources) = parse_cases(&source); let mut import_stubs = IndexMap::new(); let mut ledger_config = run::Config { seed: 2, start_height: None, programs: Vec::new() }; - let mut requires_ledger = false; - for source in &sources { - let (bytecode, name) = handler.extend_if_error(whole_compile(source, handler, import_stubs.clone()))?; - requires_ledger = bytecode.contains("async"); + // Split sources into intermediate and final. + let (last, rest) = sources.split_last().expect("sources cannot be empty"); - let stub = handler - .extend_if_error(disassemble_from_str::(&name, &bytecode).map_err(|err| err.into()))?; - import_stubs.insert(Symbol::intern(&name), stub); + // Parse-only stage for intermediate programs. + for source in rest { + let (program, program_name) = handler.extend_if_error(parse(source, handler, node_builder))?; + import_stubs.insert(Symbol::intern(&program_name), program.into()); + } + + // Full compile stage for the final program. + let (compiled_programs, program_name) = + handler.extend_if_error(whole_compile(last, handler, node_builder, import_stubs.clone()))?; - ledger_config.programs.push(run::Program { bytecode, name }); + // Add imported programs. + let mut requires_ledger = false; + for Bytecode { program_name, bytecode } in compiled_programs.import_bytecodes { + requires_ledger |= bytecode.contains("async"); + ledger_config.programs.push(run::Program { bytecode, name: program_name }); } + // Add main program. + let primary_bytecode = compiled_programs.primary_bytecode.clone(); + requires_ledger |= primary_bytecode.contains("async"); + ledger_config.programs.push(run::Program { bytecode: primary_bytecode, name: program_name }); + // Extract only the outputs, ignoring status, execution, etc. let outputs: Vec = if requires_ledger { // Note. We wrap the cases in a slice to run them on a single ledger instance. @@ -196,7 +240,8 @@ fn test_interpreter() { let mut test_result = { let buf = BufferEmitter::new(); let handler = Handler::new(buf.clone()); - match run_test(path, &handler, &buf) { + let node_builder = Rc::new(NodeBuilder::default()); + match run_test(path, &handler, &node_builder, &buf) { Ok(result) => result, Err(..) => { let errs = buf.extract_errs(); diff --git a/leo/cli/cli.rs b/leo/cli/cli.rs index f8ff5336622..b5fb48d8940 100644 --- a/leo/cli/cli.rs +++ b/leo/cli/cli.rs @@ -717,15 +717,11 @@ program child.aleo { }; // Add source files `outer/src/main.leo` and `outer/inner/src/main.leo` - let outer_program = "import inner_1.aleo; + let outer_program = " +import inner_1.aleo; import inner_2.aleo; program outer.aleo { - struct ex_struct { - arg1: u32, - arg2: u32, - } - record inner_1_record { owner: address, arg1: u32, @@ -733,17 +729,19 @@ program outer.aleo { arg3: u32, } - transition inner_1_main(public a: u32, b: u32) -> (inner_1.aleo/inner_1_record, inner_2.aleo/inner_1_record, inner_1_record) { - let c: ex_struct = ex_struct {arg1: 1u32, arg2: 1u32}; + transition inner_1_main(public a: u32, b: u32) -> (inner_1.aleo/inner_1_record, inner_2.aleo/inner_2_record, inner_1_record) { + let c: inner_1.aleo/ex_struct = inner_1.aleo/ex_struct {arg1: 1u32, arg2: 1u32}; let rec_1:inner_1.aleo/inner_1_record = inner_1.aleo/inner_1_main(1u32,1u32, c); - let rec_2:inner_2.aleo/inner_1_record = inner_2.aleo/inner_1_main(1u32,1u32); + let rec_2:inner_2.aleo/inner_2_record = inner_2.aleo/inner_2_main(1u32,1u32); return (rec_1, rec_2, inner_1_record {owner: aleo14tnetva3xfvemqyg5ujzvr0qfcaxdanmgjx2wsuh2xrpvc03uc9s623ps7, arg1: 1u32, arg2: 1u32, arg3: 1u32}); } @noupgrade async constructor() {} -}"; - let inner_1_program = "program inner_1.aleo { +} + "; + let inner_1_program = " +program inner_1.aleo { mapping inner_1_mapping: u32 => u32; record inner_1_record { owner: address, @@ -762,16 +760,18 @@ program outer.aleo { @noupgrade async constructor() {} -}"; - let inner_2_program = "program inner_2.aleo { +} +"; + let inner_2_program = " +program inner_2.aleo { mapping inner_2_mapping: u32 => u32; - record inner_1_record { + record inner_2_record { owner: address, val: u32, } - transition inner_1_main(public a: u32, b: u32) -> inner_1_record { + transition inner_2_main(public a: u32, b: u32) -> inner_2_record { let c: u32 = a + b; - return inner_1_record { + return inner_2_record { owner: self.caller, val: a, }; @@ -779,7 +779,8 @@ program outer.aleo { @noupgrade async constructor() {} -}"; +} + "; // Add dependencies `outer/program.json` let add_outer_dependency_1 = CLI { debug: false, @@ -909,14 +910,15 @@ program outer_2.aleo { owner: address, a: u32, } + transition main(public a: u32, b: u32) -> (inner_2.aleo/Yoo, Hello) { - let d: Foo = inner_1.aleo/main(1u32,1u32); - let e: u32 = inner_1.aleo/main_2(Foo {a: a, b: b, c: Boo {a:1u32, b:1u32}}); + let d: inner_1.aleo/Foo = inner_1.aleo/main(1u32,1u32); + let e: u32 = inner_1.aleo/main_2(inner_1.aleo/Foo {a: a, b: b, c: inner_1.aleo/Boo {a:1u32, b:1u32}}); let f: Boo = Boo {a:1u32, b:1u32}; - let g: Foo = inner_2.aleo/main(1u32, 1u32); + let g: inner_2.aleo/Foo = inner_2.aleo/main(1u32, 1u32); inner_2.aleo/Yo_Consumer(inner_2.aleo/Yo()); let h: inner_2.aleo/Yoo = inner_2.aleo/Yo(); - let i: Goo = inner_2.aleo/Goo_creator(); + let i: inner_2.aleo/Goo = inner_2.aleo/Goo_creator(); let j: Hello = Hello {owner: self.signer, a:1u32}; return (h, j); diff --git a/leo/cli/commands/build.rs b/leo/cli/commands/build.rs index c356836fc4a..ba56fdf054c 100644 --- a/leo/cli/commands/build.rs +++ b/leo/cli/commands/build.rs @@ -16,16 +16,17 @@ use super::*; -use leo_ast::{NetworkName, Stub}; +use leo_ast::{Bytecode, CompiledPrograms, NetworkName, NodeBuilder, Program, Stub}; use leo_compiler::{AstSnapshots, Compiler, CompilerOptions}; use leo_errors::{CliError, UtilError}; use leo_package::{Manifest, Package}; use leo_span::Symbol; -use snarkvm::prelude::{CanaryV0, Itertools, MainnetV0, Program, TestnetV0}; +use snarkvm::prelude::{CanaryV0, MainnetV0, Program as SvmProgram, TestnetV0}; use indexmap::IndexMap; -use std::path::Path; +use itertools::Itertools; +use std::{path::Path, rc::Rc}; impl From for CompilerOptions { fn from(options: BuildOptions) -> Self { @@ -136,49 +137,84 @@ fn handle_build(command: &LeoBuild, context: Context) -> Result< = IndexMap::new(); - for program in package.programs.iter() { - let (bytecode, build_path) = match &program.data { + for program in &package.programs { + match &program.data { leo_package::ProgramData::Bytecode(bytecode) => { // This was a network dependency or local .aleo dependency, and we have its bytecode. - (bytecode.clone(), imports_directory.join(format!("{}.aleo", program.name))) + let build_path = imports_directory.join(format!("{}.aleo", program.name)); + + // Write the .aleo file. + std::fs::write(&build_path, bytecode).map_err(CliError::failed_to_load_instructions)?; + + // Track the Stub. + let stub = match network { + NetworkName::MainnetV0 => { + leo_disassembler::disassemble_from_str::(program.name, bytecode) + } + NetworkName::TestnetV0 => { + leo_disassembler::disassemble_from_str::(program.name, bytecode) + } + NetworkName::CanaryV0 => leo_disassembler::disassemble_from_str::(program.name, bytecode), + }?; + + stubs.insert(program.name, stub.into()); } + leo_package::ProgramData::SourcePath { directory, source } => { - // This is a local dependency, so we must compile it. - let build_path = if source == &main_source_path { - build_directory.join("main.aleo") - } else { - imports_directory.join(format!("{}.aleo", program.name)) - }; - // Load the manifest in local dependency. + // This is a local dependency, so we must compile or parse it. let source_dir = directory.join("src"); - let bytecode = compile_leo_source_directory( - source, // entry file + + if source == &main_source_path || program.is_test { + // Compile the program (main or test). + let compiled_programs = compile_leo_source_directory( + source, // entry file + &source_dir, + program.name, + program.is_test, + &outputs_directory, + &handler, + &node_builder, + command.options.clone(), + stubs.clone(), + network, + )?; + + // Where to write the primary bytecode? + let primary_path = if source == &main_source_path { + build_directory.join("main.aleo") + } else { + imports_directory.join(format!("{}.aleo", program.name)) + }; + + // Write the primary program bytecode. + std::fs::write(&primary_path, &compiled_programs.primary_bytecode) + .map_err(CliError::failed_to_load_instructions)?; + + // Write imports. + for Bytecode { program_name, bytecode } in compiled_programs.import_bytecodes { + let import_path = imports_directory.join(format!("{}.aleo", program_name)); + std::fs::write(&import_path, &bytecode).map_err(CliError::failed_to_load_instructions)?; + } + } + + // Parse intermediate dependencies only. + let leo_program = parse_leo_source_directory( + source, &source_dir, program.name, - program.is_test, - &outputs_directory, &handler, + &node_builder, command.options.clone(), - stubs.clone(), network, )?; - (bytecode, build_path) - } - }; - // Write the .aleo file. - std::fs::write(build_path, &bytecode).map_err(CliError::failed_to_load_instructions)?; - - // Track the Stub. - let stub = match network { - NetworkName::MainnetV0 => leo_disassembler::disassemble_from_str::(program.name, &bytecode), - NetworkName::TestnetV0 => leo_disassembler::disassemble_from_str::(program.name, &bytecode), - NetworkName::CanaryV0 => leo_disassembler::disassemble_from_str::(program.name, &bytecode), - }?; - stubs.insert(program.name, stub); + stubs.insert(program.name, leo_program.into()); + } + } } // SnarkVM expects to find a `program.json` file in the build directory, so make @@ -207,15 +243,17 @@ fn compile_leo_source_directory( is_test: bool, output_path: &Path, handler: &Handler, + node_builder: &Rc, options: BuildOptions, stubs: IndexMap, network: NetworkName, -) -> Result { +) -> Result { // Create a new instance of the Leo compiler. let mut compiler = Compiler::new( Some(program_name.to_string()), is_test, handler.clone(), + Rc::clone(node_builder), output_path.to_path_buf(), Some(options.into()), stubs, @@ -223,11 +261,12 @@ fn compile_leo_source_directory( ); // Compile the Leo program into Aleo instructions. - let bytecode = compiler.compile_from_directory(entry_file_path, source_directory)?; + let compiled_programs = compiler.compile_from_directory(entry_file_path, source_directory)?; + let primary_bytecode = compiled_programs.primary_bytecode.clone(); - // Check the program size limit. + // Check the program size limit for each bytecode. use leo_package::MAX_PROGRAM_SIZE; - let program_size = bytecode.len(); + let program_size = primary_bytecode.len(); if program_size > MAX_PROGRAM_SIZE { return Err(leo_errors::LeoError::UtilError(UtilError::program_size_limit_exceeded( @@ -239,9 +278,9 @@ fn compile_leo_source_directory( // Get the AVM bytecode. let checksum: String = match network { - NetworkName::MainnetV0 => Program::::from_str(&bytecode)?.to_checksum().iter().join(", "), - NetworkName::TestnetV0 => Program::::from_str(&bytecode)?.to_checksum().iter().join(", "), - NetworkName::CanaryV0 => Program::::from_str(&bytecode)?.to_checksum().iter().join(", "), + NetworkName::MainnetV0 => SvmProgram::::from_str(&primary_bytecode)?.to_checksum().iter().join(", "), + NetworkName::TestnetV0 => SvmProgram::::from_str(&primary_bytecode)?.to_checksum().iter().join(", "), + NetworkName::CanaryV0 => SvmProgram::::from_str(&primary_bytecode)?.to_checksum().iter().join(", "), }; tracing::info!(" {} statements before dead code elimination.", compiler.statements_before_dce); @@ -255,5 +294,44 @@ fn compile_leo_source_directory( } tracing::info!("✅ Compiled '{program_name}.aleo' into Aleo instructions."); - Ok(bytecode) + + // Print checksums for all additional bytecodes (dependencies). + for Bytecode { program_name: dep_name, bytecode: dep_bytecode } in &compiled_programs.import_bytecodes { + // Compute checksum depending on network. + let dep_checksum: String = match network { + NetworkName::MainnetV0 => SvmProgram::::from_str(dep_bytecode)?.to_checksum().iter().join(", "), + NetworkName::TestnetV0 => SvmProgram::::from_str(dep_bytecode)?.to_checksum().iter().join(", "), + NetworkName::CanaryV0 => SvmProgram::::from_str(dep_bytecode)?.to_checksum().iter().join(", "), + }; + + tracing::info!(" Dependency '{dep_name}.aleo': checksum = '[{dep_checksum}]'"); + } + + Ok(compiled_programs) +} + +/// Compiles a Leo file. Writes and returns the compiled bytecode. +fn parse_leo_source_directory( + entry_file_path: &Path, + source_directory: &Path, + program_name: Symbol, + handler: &Handler, + node_builder: &Rc, + options: BuildOptions, + network: NetworkName, +) -> Result { + // Create a new instance of the Leo compiler. + let mut compiler = Compiler::new( + Some(program_name.to_string()), + false, + handler.clone(), + Rc::clone(node_builder), + std::path::PathBuf::default(), + Some(options.into()), + IndexMap::new(), + network, + ); + + // Compile the Leo program into Aleo instructions. + compiler.parse_from_directory(entry_file_path, source_directory) } diff --git a/leo/cli/commands/common/options.rs b/leo/cli/commands/common/options.rs index 87fc851b89c..545cc8946eb 100644 --- a/leo/cli/commands/common/options.rs +++ b/leo/cli/commands/common/options.rs @@ -237,6 +237,7 @@ pub fn get_consensus_version( Some(10) => Ok(ConsensusVersion::V10), Some(11) => Ok(ConsensusVersion::V11), Some(12) => Ok(ConsensusVersion::V12), + Some(13) => Ok(ConsensusVersion::V13), // If none is provided, then attempt to query the current block height and use it to determine the version. None => { println!("Attempting to determine the consensus version from the latest block height at {endpoint}..."); @@ -318,6 +319,7 @@ pub fn number_to_consensus_version(index: usize) -> ConsensusVersion { 10 => ConsensusVersion::V10, 11 => ConsensusVersion::V11, 12 => ConsensusVersion::V12, + 13 => ConsensusVersion::V13, _ => panic!("Invalid consensus version: {index}"), } } @@ -434,6 +436,6 @@ mod test { #[test] fn test_latest_consensus_version() { - assert_eq!(ConsensusVersion::latest(), ConsensusVersion::V12); // If this fails, update the test and any code that matches on `ConsensusVersion`. + assert_eq!(ConsensusVersion::latest(), ConsensusVersion::V13); // If this fails, update the test and any code that matches on `ConsensusVersion`. } } diff --git a/tests/expectations/cli/local_aleo_dependency/COMMANDS b/tests/expectations/cli/local_aleo_dependency/COMMANDS new file mode 100755 index 00000000000..f4c781171ae --- /dev/null +++ b/tests/expectations/cli/local_aleo_dependency/COMMANDS @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +LEO_BIN=${1} + +${LEO_BIN} build diff --git a/tests/expectations/cli/local_aleo_dependency/STDERR b/tests/expectations/cli/local_aleo_dependency/STDERR new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/expectations/cli/local_aleo_dependency/STDOUT b/tests/expectations/cli/local_aleo_dependency/STDOUT new file mode 100644 index 00000000000..883f4a8cb99 --- /dev/null +++ b/tests/expectations/cli/local_aleo_dependency/STDOUT @@ -0,0 +1,7 @@ +⚠️ No network specified, defaulting to 'testnet'. +⚠️ No endpoint specified, defaulting to 'https://api.explorer.provable.com/v1'. + Leo 2 statements before dead code elimination. + Leo 2 statements after dead code elimination. + Leo The program checksum is: '[100u8, 207u8, 87u8, 107u8, 187u8, 153u8, 95u8, 22u8, 31u8, 75u8, 125u8, 95u8, 119u8, 144u8, 156u8, 19u8, 111u8, 156u8, 213u8, 202u8, 101u8, 112u8, 208u8, 87u8, 81u8, 218u8, 221u8, 118u8, 35u8, 125u8, 183u8, 157u8]'. + Leo Program size: 0.22 KB / 97.66 KB + Leo ✅ Compiled 'complex.aleo' into Aleo instructions. diff --git a/tests/expectations/cli/local_aleo_dependency/contents/.gitignore b/tests/expectations/cli/local_aleo_dependency/contents/.gitignore new file mode 100644 index 00000000000..f721f7f6f45 --- /dev/null +++ b/tests/expectations/cli/local_aleo_dependency/contents/.gitignore @@ -0,0 +1,5 @@ +.env +*.avm +*.prover +*.verifier +outputs/ diff --git a/tests/expectations/cli/local_aleo_dependency/contents/build/imports/simple.aleo b/tests/expectations/cli/local_aleo_dependency/contents/build/imports/simple.aleo new file mode 100644 index 00000000000..55ec6ae3502 --- /dev/null +++ b/tests/expectations/cli/local_aleo_dependency/contents/build/imports/simple.aleo @@ -0,0 +1,10 @@ +program simple.aleo; + +function main: + input r0 as u32.public; + input r1 as u32.private; + add r0 r1 into r2; + output r2 as u32.private; + +constructor: + assert.eq edition 0u16; diff --git a/tests/expectations/cli/local_aleo_dependency/contents/build/main.aleo b/tests/expectations/cli/local_aleo_dependency/contents/build/main.aleo new file mode 100644 index 00000000000..da28bd6c16a --- /dev/null +++ b/tests/expectations/cli/local_aleo_dependency/contents/build/main.aleo @@ -0,0 +1,11 @@ +import simple.aleo; +program complex.aleo; + +function main: + input r0 as u32.public; + input r1 as u32.private; + call simple.aleo/main r0 r1 into r2; + output r2 as u32.private; + +constructor: + assert.eq edition 0u16; diff --git a/tests/expectations/cli/local_aleo_dependency/contents/build/program.json b/tests/expectations/cli/local_aleo_dependency/contents/build/program.json new file mode 100644 index 00000000000..202c291d485 --- /dev/null +++ b/tests/expectations/cli/local_aleo_dependency/contents/build/program.json @@ -0,0 +1,9 @@ +{ + "program": "complex.aleo", + "version": "0.1.0", + "description": "", + "license": "", + "leo": "3.4.0", + "dependencies": null, + "dev_dependencies": null +} diff --git a/tests/expectations/cli/local_aleo_dependency/contents/program.json b/tests/expectations/cli/local_aleo_dependency/contents/program.json new file mode 100644 index 00000000000..c7783b0399b --- /dev/null +++ b/tests/expectations/cli/local_aleo_dependency/contents/program.json @@ -0,0 +1,16 @@ +{ + "program": "complex.aleo", + "version": "0.1.0", + "description": "", + "license": "MIT", + "leo": "3.4.0", + "dependencies": [ + { + "name": "simple.aleo", + "location": "local", + "path": "simple.aleo", + "edition": null + } + ], + "dev_dependencies": null +} diff --git a/tests/expectations/cli/local_aleo_dependency/contents/simple.aleo b/tests/expectations/cli/local_aleo_dependency/contents/simple.aleo new file mode 100644 index 00000000000..55ec6ae3502 --- /dev/null +++ b/tests/expectations/cli/local_aleo_dependency/contents/simple.aleo @@ -0,0 +1,10 @@ +program simple.aleo; + +function main: + input r0 as u32.public; + input r1 as u32.private; + add r0 r1 into r2; + output r2 as u32.private; + +constructor: + assert.eq edition 0u16; diff --git a/tests/expectations/cli/local_aleo_dependency/contents/src/main.leo b/tests/expectations/cli/local_aleo_dependency/contents/src/main.leo new file mode 100644 index 00000000000..15a4d510b68 --- /dev/null +++ b/tests/expectations/cli/local_aleo_dependency/contents/src/main.leo @@ -0,0 +1,11 @@ +// The 'simple' program. +import simple.aleo; + +program complex.aleo { + @noupgrade + async constructor() {} + + transition main(public a: u32, b: u32) -> u32 { + return simple.aleo/main(a, b); + } +} diff --git a/tests/expectations/cli/multiple_leo_deps/COMMANDS b/tests/expectations/cli/multiple_leo_deps/COMMANDS new file mode 100755 index 00000000000..47525daf034 --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/COMMANDS @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +LEO_BIN=${1} + +cd parent || exit 1 +$LEO_BIN build diff --git a/tests/expectations/cli/multiple_leo_deps/STDERR b/tests/expectations/cli/multiple_leo_deps/STDERR new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/expectations/cli/multiple_leo_deps/STDOUT b/tests/expectations/cli/multiple_leo_deps/STDOUT new file mode 100644 index 00000000000..afed1903c9a --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/STDOUT @@ -0,0 +1,10 @@ +⚠️ No network specified, defaulting to 'testnet'. +⚠️ No endpoint specified, defaulting to 'https://api.explorer.provable.com/v1'. + Leo 9 statements before dead code elimination. + Leo 9 statements after dead code elimination. + Leo The program checksum is: '[215u8, 203u8, 66u8, 86u8, 222u8, 65u8, 57u8, 252u8, 117u8, 231u8, 155u8, 25u8, 45u8, 60u8, 202u8, 131u8, 149u8, 90u8, 30u8, 208u8, 156u8, 22u8, 227u8, 204u8, 30u8, 127u8, 166u8, 190u8, 70u8, 12u8, 210u8, 133u8]'. + Leo Program size: 0.29 KB / 97.66 KB + Leo ✅ Compiled 'parent.aleo' into Aleo instructions. + Leo Dependency 'grandchild.aleo': checksum = '[9u8, 161u8, 225u8, 109u8, 140u8, 240u8, 34u8, 110u8, 197u8, 113u8, 9u8, 64u8, 14u8, 115u8, 105u8, 222u8, 56u8, 193u8, 21u8, 198u8, 195u8, 22u8, 18u8, 168u8, 64u8, 22u8, 193u8, 208u8, 158u8, 7u8, 94u8, 0u8]' + Leo Dependency 'child1.aleo': checksum = '[117u8, 228u8, 47u8, 107u8, 194u8, 194u8, 148u8, 143u8, 21u8, 99u8, 243u8, 112u8, 125u8, 105u8, 203u8, 69u8, 238u8, 73u8, 77u8, 220u8, 25u8, 81u8, 154u8, 247u8, 197u8, 230u8, 203u8, 180u8, 234u8, 50u8, 73u8, 16u8]' + Leo Dependency 'child2.aleo': checksum = '[198u8, 70u8, 203u8, 134u8, 248u8, 126u8, 166u8, 195u8, 11u8, 174u8, 43u8, 87u8, 169u8, 255u8, 198u8, 41u8, 185u8, 18u8, 154u8, 42u8, 108u8, 95u8, 21u8, 225u8, 85u8, 180u8, 75u8, 105u8, 197u8, 145u8, 148u8, 8u8]' diff --git a/tests/expectations/cli/multiple_leo_deps/contents/.gitignore b/tests/expectations/cli/multiple_leo_deps/contents/.gitignore new file mode 100644 index 00000000000..f721f7f6f45 --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/contents/.gitignore @@ -0,0 +1,5 @@ +.env +*.avm +*.prover +*.verifier +outputs/ diff --git a/tests/expectations/cli/multiple_leo_deps/contents/child1/.gitignore b/tests/expectations/cli/multiple_leo_deps/contents/child1/.gitignore new file mode 100644 index 00000000000..f721f7f6f45 --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/contents/child1/.gitignore @@ -0,0 +1,5 @@ +.env +*.avm +*.prover +*.verifier +outputs/ diff --git a/tests/expectations/cli/multiple_leo_deps/contents/child1/program.json b/tests/expectations/cli/multiple_leo_deps/contents/child1/program.json new file mode 100644 index 00000000000..12e4d1f1d21 --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/contents/child1/program.json @@ -0,0 +1,16 @@ +{ + "program": "child1.aleo", + "version": "0.1.0", + "description": "", + "license": "MIT", + "leo": "3.4.0", + "dependencies": [ + { + "name": "grandchild.aleo", + "location": "local", + "path": "../grandchild", + "edition": null + } + ], + "dev_dependencies": null +} diff --git a/tests/expectations/cli/multiple_leo_deps/contents/child1/src/main.leo b/tests/expectations/cli/multiple_leo_deps/contents/child1/src/main.leo new file mode 100644 index 00000000000..b8d28d6b751 --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/contents/child1/src/main.leo @@ -0,0 +1,15 @@ +import grandchild.aleo; + +program child1.aleo { + record R { + owner: address, + f1: field + } + + transition main(b: u32) -> u32 { + return grandchild.aleo/main(b); + } + + @noupgrade + async constructor() {} +} diff --git a/tests/expectations/cli/multiple_leo_deps/contents/child2/.gitignore b/tests/expectations/cli/multiple_leo_deps/contents/child2/.gitignore new file mode 100644 index 00000000000..f721f7f6f45 --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/contents/child2/.gitignore @@ -0,0 +1,5 @@ +.env +*.avm +*.prover +*.verifier +outputs/ diff --git a/tests/expectations/cli/multiple_leo_deps/contents/child2/program.json b/tests/expectations/cli/multiple_leo_deps/contents/child2/program.json new file mode 100644 index 00000000000..23b8b0a7dcd --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/contents/child2/program.json @@ -0,0 +1,16 @@ +{ + "program": "child2.aleo", + "version": "0.1.0", + "description": "", + "license": "MIT", + "leo": "3.4.0", + "dependencies": [ + { + "name": "grandchild.aleo", + "location": "local", + "path": "../grandchild", + "edition": null + } + ], + "dev_dependencies": null +} diff --git a/tests/expectations/cli/multiple_leo_deps/contents/child2/src/main.leo b/tests/expectations/cli/multiple_leo_deps/contents/child2/src/main.leo new file mode 100644 index 00000000000..e9dc101df1d --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/contents/child2/src/main.leo @@ -0,0 +1,15 @@ +import grandchild.aleo; + +program child2.aleo { + record R { + owner: address, + f2: field + } + + transition main(b: u32) -> u32 { + return grandchild.aleo/main(b); + } + + @noupgrade + async constructor() {} +} diff --git a/tests/expectations/cli/multiple_leo_deps/contents/grandchild/.gitignore b/tests/expectations/cli/multiple_leo_deps/contents/grandchild/.gitignore new file mode 100644 index 00000000000..f721f7f6f45 --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/contents/grandchild/.gitignore @@ -0,0 +1,5 @@ +.env +*.avm +*.prover +*.verifier +outputs/ diff --git a/tests/expectations/cli/multiple_leo_deps/contents/grandchild/program.json b/tests/expectations/cli/multiple_leo_deps/contents/grandchild/program.json new file mode 100644 index 00000000000..96cc4e28430 --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/contents/grandchild/program.json @@ -0,0 +1,8 @@ +{ + "program": "grandchild.aleo", + "version": "0.1.0", + "description": "", + "license": "MIT", + "dependencies": null, + "dev_dependencies": null +} diff --git a/tests/expectations/cli/multiple_leo_deps/contents/grandchild/src/main.leo b/tests/expectations/cli/multiple_leo_deps/contents/grandchild/src/main.leo new file mode 100644 index 00000000000..60c071375bb --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/contents/grandchild/src/main.leo @@ -0,0 +1,12 @@ +program grandchild.aleo { + record R { + owner: address, + } + + transition main(b: u32) -> u32 { + return b; + } + + @noupgrade + async constructor() {} +} diff --git a/tests/expectations/cli/multiple_leo_deps/contents/parent/.gitignore b/tests/expectations/cli/multiple_leo_deps/contents/parent/.gitignore new file mode 100644 index 00000000000..f721f7f6f45 --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/contents/parent/.gitignore @@ -0,0 +1,5 @@ +.env +*.avm +*.prover +*.verifier +outputs/ diff --git a/tests/expectations/cli/multiple_leo_deps/contents/parent/build/imports/child1.aleo b/tests/expectations/cli/multiple_leo_deps/contents/parent/build/imports/child1.aleo new file mode 100644 index 00000000000..30c3eb0d3ab --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/contents/parent/build/imports/child1.aleo @@ -0,0 +1,10 @@ +import grandchild.aleo; +program child1.aleo; + +function main: + input r0 as u32.private; + call grandchild.aleo/main r0 into r1; + output r1 as u32.private; + +constructor: + assert.eq edition 0u16; diff --git a/tests/expectations/cli/multiple_leo_deps/contents/parent/build/imports/child2.aleo b/tests/expectations/cli/multiple_leo_deps/contents/parent/build/imports/child2.aleo new file mode 100644 index 00000000000..cbf3f34c2c2 --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/contents/parent/build/imports/child2.aleo @@ -0,0 +1,10 @@ +import grandchild.aleo; +program child2.aleo; + +function main: + input r0 as u32.private; + call grandchild.aleo/main r0 into r1; + output r1 as u32.private; + +constructor: + assert.eq edition 0u16; diff --git a/tests/expectations/cli/multiple_leo_deps/contents/parent/build/imports/grandchild.aleo b/tests/expectations/cli/multiple_leo_deps/contents/parent/build/imports/grandchild.aleo new file mode 100644 index 00000000000..bcf4efcc908 --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/contents/parent/build/imports/grandchild.aleo @@ -0,0 +1,8 @@ +program grandchild.aleo; + +function main: + input r0 as u32.private; + output r0 as u32.private; + +constructor: + assert.eq edition 0u16; diff --git a/tests/expectations/cli/multiple_leo_deps/contents/parent/build/main.aleo b/tests/expectations/cli/multiple_leo_deps/contents/parent/build/main.aleo new file mode 100644 index 00000000000..a136b21708c --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/contents/parent/build/main.aleo @@ -0,0 +1,14 @@ +import grandchild.aleo; +import child1.aleo; +import child2.aleo; +program parent.aleo; + +function main: + input r0 as u32.private; + call child1.aleo/main r0 into r1; + call child2.aleo/main r0 into r2; + add r1 r2 into r3; + output r3 as u32.private; + +constructor: + assert.eq edition 0u16; diff --git a/tests/expectations/cli/multiple_leo_deps/contents/parent/build/program.json b/tests/expectations/cli/multiple_leo_deps/contents/parent/build/program.json new file mode 100644 index 00000000000..fc0ad5a4b9f --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/contents/parent/build/program.json @@ -0,0 +1,9 @@ +{ + "program": "parent.aleo", + "version": "0.1.0", + "description": "", + "license": "", + "leo": "3.4.0", + "dependencies": null, + "dev_dependencies": null +} diff --git a/tests/expectations/cli/multiple_leo_deps/contents/parent/program.json b/tests/expectations/cli/multiple_leo_deps/contents/parent/program.json new file mode 100644 index 00000000000..af4b89b8bfb --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/contents/parent/program.json @@ -0,0 +1,22 @@ +{ + "program": "parent.aleo", + "version": "0.1.0", + "description": "", + "license": "MIT", + "leo": "3.4.0", + "dependencies": [ + { + "name": "child1.aleo", + "location": "local", + "path": "../child1", + "edition": null + }, + { + "name": "child2.aleo", + "location": "local", + "path": "../child2", + "edition": null + } + ], + "dev_dependencies": null +} diff --git a/tests/expectations/cli/multiple_leo_deps/contents/parent/src/main.leo b/tests/expectations/cli/multiple_leo_deps/contents/parent/src/main.leo new file mode 100644 index 00000000000..36b3bf7876a --- /dev/null +++ b/tests/expectations/cli/multiple_leo_deps/contents/parent/src/main.leo @@ -0,0 +1,11 @@ +import child1.aleo; +import child2.aleo; + +program parent.aleo { + transition main(b: u32) -> u32 { + return child1.aleo/main(b) + child2.aleo/main(b); + } + + @noupgrade + async constructor() {} +} diff --git a/tests/expectations/cli/program_name_mismatch/contents/program.json b/tests/expectations/cli/program_name_mismatch/contents/program.json index a76eba3a335..e0d5a628f8f 100644 --- a/tests/expectations/cli/program_name_mismatch/contents/program.json +++ b/tests/expectations/cli/program_name_mismatch/contents/program.json @@ -4,8 +4,5 @@ "description": "", "license": "MIT", "dependencies": null, - "dev_dependencies": null, - "upgrade": { - "mode": "noupgrade" - } + "dev_dependencies": null } diff --git a/tests/expectations/cli/test_deploy/contents/program.json b/tests/expectations/cli/test_deploy/contents/program.json index f6b7caf5c08..93f916db11e 100644 --- a/tests/expectations/cli/test_deploy/contents/program.json +++ b/tests/expectations/cli/test_deploy/contents/program.json @@ -4,8 +4,5 @@ "description": "", "license": "MIT", "dependencies": null, - "dev_dependencies": null, - "upgrade": { - "mode": "noupgrade" - } + "dev_dependencies": null } diff --git a/tests/expectations/cli/test_simple_build/contents/program.json b/tests/expectations/cli/test_simple_build/contents/program.json index f6b7caf5c08..93f916db11e 100644 --- a/tests/expectations/cli/test_simple_build/contents/program.json +++ b/tests/expectations/cli/test_simple_build/contents/program.json @@ -4,8 +4,5 @@ "description": "", "license": "MIT", "dependencies": null, - "dev_dependencies": null, - "upgrade": { - "mode": "noupgrade" - } + "dev_dependencies": null } diff --git a/tests/expectations/cli/test_simple_test/COMMANDS b/tests/expectations/cli/test_simple_test/COMMANDS new file mode 100755 index 00000000000..2ee615b7deb --- /dev/null +++ b/tests/expectations/cli/test_simple_test/COMMANDS @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +LEO_BIN=${1} + +${LEO_BIN} test diff --git a/tests/expectations/cli/test_simple_test/STDERR b/tests/expectations/cli/test_simple_test/STDERR new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/expectations/cli/test_simple_test/STDOUT b/tests/expectations/cli/test_simple_test/STDOUT new file mode 100644 index 00000000000..722d6113d72 --- /dev/null +++ b/tests/expectations/cli/test_simple_test/STDOUT @@ -0,0 +1,17 @@ +⚠️ No network specified, defaulting to 'testnet'. +⚠️ No endpoint specified, defaulting to 'https://api.explorer.provable.com/v1'. + Leo 2 statements before dead code elimination. + Leo 2 statements after dead code elimination. + Leo The program checksum is: '[95u8, 99u8, 136u8, 243u8, 82u8, 169u8, 200u8, 72u8, 133u8, 227u8, 122u8, 161u8, 195u8, 178u8, 69u8, 37u8, 167u8, 114u8, 104u8, 192u8, 161u8, 175u8, 195u8, 180u8, 120u8, 4u8, 192u8, 16u8, 86u8, 199u8, 76u8, 235u8]'. + Leo Program size: 0.20 KB / 97.66 KB + Leo ✅ Compiled 'some_sample_leo_program.aleo' into Aleo instructions. + Leo 6 statements before dead code elimination. + Leo 6 statements after dead code elimination. + Leo The program checksum is: '[74u8, 114u8, 9u8, 196u8, 119u8, 213u8, 43u8, 122u8, 92u8, 155u8, 60u8, 120u8, 169u8, 0u8, 41u8, 92u8, 14u8, 87u8, 204u8, 10u8, 34u8, 79u8, 168u8, 242u8, 102u8, 138u8, 132u8, 112u8, 157u8, 113u8, 231u8, 17u8]'. + Leo Program size: 0.22 KB / 97.66 KB + Leo ✅ Compiled 'test_some_sample_leo_program.aleo' into Aleo instructions. + Leo Dependency 'some_sample_leo_program.aleo': checksum = '[95u8, 99u8, 136u8, 243u8, 82u8, 169u8, 200u8, 72u8, 133u8, 227u8, 122u8, 161u8, 195u8, 178u8, 69u8, 37u8, 167u8, 114u8, 104u8, 192u8, 161u8, 175u8, 195u8, 180u8, 120u8, 4u8, 192u8, 16u8, 86u8, 199u8, 76u8, 235u8]' + Leo Loading the ledger from storage... +2 / 2 tests passed. +PASSED: test_some_sample_leo_program.aleo/test_it +PASSED: test_some_sample_leo_program.aleo/do_nothing diff --git a/tests/expectations/cli/test_simple_test/contents/.gitignore b/tests/expectations/cli/test_simple_test/contents/.gitignore new file mode 100644 index 00000000000..f721f7f6f45 --- /dev/null +++ b/tests/expectations/cli/test_simple_test/contents/.gitignore @@ -0,0 +1,5 @@ +.env +*.avm +*.prover +*.verifier +outputs/ diff --git a/tests/expectations/cli/test_simple_test/contents/build/imports/some_sample_leo_program.aleo b/tests/expectations/cli/test_simple_test/contents/build/imports/some_sample_leo_program.aleo new file mode 100644 index 00000000000..4cc28b0a97d --- /dev/null +++ b/tests/expectations/cli/test_simple_test/contents/build/imports/some_sample_leo_program.aleo @@ -0,0 +1,10 @@ +program some_sample_leo_program.aleo; + +function main: + input r0 as u32.public; + input r1 as u32.private; + add r0 r1 into r2; + output r2 as u32.private; + +constructor: + assert.eq edition 0u16; diff --git a/tests/expectations/cli/test_simple_test/contents/build/imports/test_some_sample_leo_program.aleo b/tests/expectations/cli/test_simple_test/contents/build/imports/test_some_sample_leo_program.aleo new file mode 100644 index 00000000000..d6f8eb2cca2 --- /dev/null +++ b/tests/expectations/cli/test_simple_test/contents/build/imports/test_some_sample_leo_program.aleo @@ -0,0 +1,9 @@ +import some_sample_leo_program.aleo; +program test_some_sample_leo_program.aleo; + +function do_nothing: + call some_sample_leo_program.aleo/main 2u32 3u32 into r0; + assert.eq r0 3u32; + +constructor: + assert.eq edition 0u16; diff --git a/tests/expectations/cli/test_simple_test/contents/build/main.aleo b/tests/expectations/cli/test_simple_test/contents/build/main.aleo new file mode 100644 index 00000000000..4cc28b0a97d --- /dev/null +++ b/tests/expectations/cli/test_simple_test/contents/build/main.aleo @@ -0,0 +1,10 @@ +program some_sample_leo_program.aleo; + +function main: + input r0 as u32.public; + input r1 as u32.private; + add r0 r1 into r2; + output r2 as u32.private; + +constructor: + assert.eq edition 0u16; diff --git a/tests/expectations/cli/test_simple_test/contents/build/program.json b/tests/expectations/cli/test_simple_test/contents/build/program.json new file mode 100644 index 00000000000..8ecc86072d4 --- /dev/null +++ b/tests/expectations/cli/test_simple_test/contents/build/program.json @@ -0,0 +1,9 @@ +{ + "program": "some_sample_leo_program.aleo", + "version": "0.1.0", + "description": "", + "license": "", + "leo": "3.4.0", + "dependencies": null, + "dev_dependencies": null +} diff --git a/tests/expectations/cli/test_simple_test/contents/program.json b/tests/expectations/cli/test_simple_test/contents/program.json new file mode 100644 index 00000000000..93f916db11e --- /dev/null +++ b/tests/expectations/cli/test_simple_test/contents/program.json @@ -0,0 +1,8 @@ +{ + "program": "some_sample_leo_program.aleo", + "version": "0.1.0", + "description": "", + "license": "MIT", + "dependencies": null, + "dev_dependencies": null +} diff --git a/tests/expectations/cli/test_simple_test/contents/src/main.leo b/tests/expectations/cli/test_simple_test/contents/src/main.leo new file mode 100644 index 00000000000..40acdafaa51 --- /dev/null +++ b/tests/expectations/cli/test_simple_test/contents/src/main.leo @@ -0,0 +1,10 @@ +// The 'some_sample_leo_program' program. +program some_sample_leo_program.aleo { + transition main(public a: u32, b: u32) -> u32 { + let c: u32 = a + b; + return c; + } + + @noupgrade + async constructor() {} +} diff --git a/tests/expectations/cli/test_simple_test/contents/tests/test_some_sample_leo_program.leo b/tests/expectations/cli/test_simple_test/contents/tests/test_some_sample_leo_program.leo new file mode 100644 index 00000000000..00528592861 --- /dev/null +++ b/tests/expectations/cli/test_simple_test/contents/tests/test_some_sample_leo_program.leo @@ -0,0 +1,19 @@ +// The 'test_some_sample_leo_program' test program. +import some_sample_leo_program.aleo; +program test_some_sample_leo_program.aleo { + @test + script test_it() { + let result: u32 = some_sample_leo_program.aleo/main(1u32, 2u32); + assert_eq(result, 3u32); + } + + @test + @should_fail + transition do_nothing() { + let result: u32 = some_sample_leo_program.aleo/main(2u32, 3u32); + assert_eq(result, 3u32); + } + + @noupgrade + async constructor() {} +} diff --git a/tests/expectations/compiler/async_blocks/future_in_tuple_check_fail.out b/tests/expectations/compiler/async_blocks/future_in_tuple_check_fail.out index 4123123208b..aa007a0ae03 100644 --- a/tests/expectations/compiler/async_blocks/future_in_tuple_check_fail.out +++ b/tests/expectations/compiler/async_blocks/future_in_tuple_check_fail.out @@ -1,7 +1,7 @@ Error [ETYC0372104]: Not all futures were consumed: result2 - --> compiler-test:9:27 + --> compiler-test:9:33 | 9 | return (result.0, async { result.1.await(); }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ | = Make sure all futures are consumed exactly once. Consume by passing to an async function call or async block. diff --git a/tests/expectations/compiler/async_blocks/future_not_all_passed_to_async_block_fail.out b/tests/expectations/compiler/async_blocks/future_not_all_passed_to_async_block_fail.out index 0250f9e7433..d8e50261bb7 100644 --- a/tests/expectations/compiler/async_blocks/future_not_all_passed_to_async_block_fail.out +++ b/tests/expectations/compiler/async_blocks/future_not_all_passed_to_async_block_fail.out @@ -1,8 +1,8 @@ Error [ETYC0372104]: Not all futures were consumed: f1 - --> compiler-test:16:16 + --> compiler-test:16:22 | 16 | return async { - | ^^^^^^^ + | ^ 17 | f0.await(); | ^^^^^^^^^^^^ 18 | f3.await(); diff --git a/tests/expectations/compiler/async_blocks/nested.out b/tests/expectations/compiler/async_blocks/nested.out index 77b5ba2a3b0..4ebf7e72dc5 100644 --- a/tests/expectations/compiler/async_blocks/nested.out +++ b/tests/expectations/compiler/async_blocks/nested.out @@ -1,8 +1,8 @@ Warning [WSAZ0374000]: Not all paths through the function await all futures. 2/4 paths contain at least one future that is never awaited. - --> compiler-test:14:26 + --> compiler-test:14:32 | 14 | let f3: Future = async { - | ^^^^^^^ + | ^ 15 | if b == 1u32 { | ^^^^^^^^^^^^^^ 16 | Future::await(f); @@ -90,8 +90,8 @@ finalize main: add r1[0u32] r2[0u32] into r5; set r5 into ayo[1u32]; // --- Next Program --- // -import test_dep.aleo; import test.aleo; +import test_dep.aleo; program wrapper.aleo; function main: diff --git a/tests/expectations/compiler/async_blocks/partial_type_specification.out b/tests/expectations/compiler/async_blocks/partial_type_specification.out index d523d747d70..91e35f585b4 100644 --- a/tests/expectations/compiler/async_blocks/partial_type_specification.out +++ b/tests/expectations/compiler/async_blocks/partial_type_specification.out @@ -1,8 +1,8 @@ Warning [WSAZ0374000]: Not all paths through the function await all futures. 2/4 paths contain at least one future that is never awaited. - --> compiler-test:14:26 + --> compiler-test:14:32 | 14 | let f3: Future = async { - | ^^^^^^^ + | ^ 15 | // f.await(); | ^^^^^^^^^^^^^ 16 | if b == 1u32 { @@ -104,8 +104,8 @@ finalize main: add r1[0u32] r2[0u32] into r5; set r5 into ayo[1u32]; // --- Next Program --- // -import test_dep.aleo; import test.aleo; +import test_dep.aleo; program wrapper.aleo; function main: diff --git a/tests/expectations/compiler/const_generics/external_generic_struct.out b/tests/expectations/compiler/const_generics/external_generic_struct.out index 9e8f937d508..090d4036266 100644 --- a/tests/expectations/compiler/const_generics/external_generic_struct.out +++ b/tests/expectations/compiler/const_generics/external_generic_struct.out @@ -20,10 +20,4 @@ function main: import child.aleo; program parent.aleo; -struct Bar__DI7sPAg0NJ0: - arr as [u32; 5u32]; - -struct Bar__5Qh5JlRc8cY: - arr as [u32; 3u32]; - function main: diff --git a/tests/expectations/compiler/const_generics/external_struct_fail.out b/tests/expectations/compiler/const_generics/external_struct_fail.out index f6708500d16..de90ccf6ab4 100644 --- a/tests/expectations/compiler/const_generics/external_struct_fail.out +++ b/tests/expectations/compiler/const_generics/external_struct_fail.out @@ -1,19 +1,5 @@ -Error [ETYC0372146]: unexpected generic const argment for Bar::[4]. - --> compiler-test:5:24 +Error [EPAR0370005]: expected ')', ',', '?' -- found '::' + --> compiler-test:5:38 | - 5 | transition main(c: Bar::[4]) { - | ^^^ - | - = If this is an external struct, consider using a resolved non-generic version of it instead. External structs can't be instantiated with const arguments -Error [ETYC0372017]: The type `Bar` is not found in the current scope. - --> compiler-test:5:21 - | - 5 | transition main(c: Bar::[4]) { - | ^^^^^^^^^^^ - | - = If you are using an external type, make sure to preface with the program name. Ex: `credits.aleo/credits` instead of `credits` -Error [ETYC0372005]: Unknown struct or record `Bar` - --> compiler-test:6:17 - | - 6 | let b = Bar::[3] { - | ^^^ + 5 | transition main(c: child.aleo/Bar::[4]) { + | ^^ diff --git a/tests/expectations/compiler/const_generics/resolved_mismatched_fail.out b/tests/expectations/compiler/const_generics/resolved_mismatched_fail.out index d0bc0a719ed..1b19ee934f1 100644 --- a/tests/expectations/compiler/const_generics/resolved_mismatched_fail.out +++ b/tests/expectations/compiler/const_generics/resolved_mismatched_fail.out @@ -1,4 +1,4 @@ -Error [ETYC0372117]: Expected type `Foo::[0u32]` but type `Foo::[1u32]` was found. +Error [ETYC0372117]: Expected type `foo.aleo/Foo::[0u32]` but type `foo.aleo/Foo::[1u32]` was found. --> compiler-test:12:16 | 12 | return Foo::[2 * N + 1] { x: 0 }; diff --git a/tests/expectations/compiler/const_generics/unexpected_const_args_fail.out b/tests/expectations/compiler/const_generics/unexpected_const_args_fail.out index 641f60d962b..55b693b534a 100644 --- a/tests/expectations/compiler/const_generics/unexpected_const_args_fail.out +++ b/tests/expectations/compiler/const_generics/unexpected_const_args_fail.out @@ -1,11 +1,11 @@ -Error [ETYC0372146]: unexpected generic const argment for M::[6]. +Error [ETYC0372146]: unexpected generic const argment for test.aleo/M::[6]. --> compiler-test:7:12 | 7 | g: M::[6], | ^ | = If this is an external struct, consider using a resolved non-generic version of it instead. External structs can't be instantiated with const arguments -Error [ETYC0372146]: unexpected generic const argment for baz::[5]. +Error [ETYC0372146]: unexpected generic const argment for test.aleo/baz::[5]. --> compiler-test:8:12 | 8 | f: baz::[5], @@ -26,7 +26,7 @@ Error [ETYC0372017]: The type `baz` is not found in the current scope. | ^^^^^^^^^^^ | = If you are using an external type, make sure to preface with the program name. Ex: `credits.aleo/credits` instead of `credits` -Error [ETYC0372146]: unexpected generic const argment for x::[5]. +Error [ETYC0372146]: unexpected generic const argment for test.aleo/x::[5]. --> compiler-test:13:16 | 13 | let y: x::[5] = [3]; diff --git a/tests/expectations/compiler/expression/cast_fail.out b/tests/expectations/compiler/expression/cast_fail.out index 64c43423a80..8a90bb81444 100644 --- a/tests/expectations/compiler/expression/cast_fail.out +++ b/tests/expectations/compiler/expression/cast_fail.out @@ -8,7 +8,7 @@ Error [ETYC0372117]: Expected an integer, bool, field, group, scalar, or address | 12 | let b: string = a as string; | ^^^^^^^^^^^ -Error [ETYC0372117]: Expected an integer, bool, field, group, scalar, or address but type `Foo` was found. +Error [ETYC0372117]: Expected an integer, bool, field, group, scalar, or address but type `test.aleo/Foo` was found. --> compiler-test:15:24 | 15 | let d: field = c as field; diff --git a/tests/expectations/compiler/finalize/get_or_incorrect_type_fail.out b/tests/expectations/compiler/finalize/get_or_incorrect_type_fail.out index 6d6b020d244..6a3bbd06b00 100644 --- a/tests/expectations/compiler/finalize/get_or_incorrect_type_fail.out +++ b/tests/expectations/compiler/finalize/get_or_incorrect_type_fail.out @@ -1,9 +1,9 @@ -Error [ETYC0372117]: Expected type `Token` but type `u128` was found. +Error [ETYC0372117]: Expected type `test.aleo/Token` but type `u128` was found. --> compiler-test:16:43 | 16 | Mapping::get_or_use(tokens, addr, amount); | ^^^^^^ -Error [ETYC0372117]: Expected type `Token` but type `u128` was found. +Error [ETYC0372117]: Expected type `test.aleo/Token` but type `u128` was found. --> compiler-test:17:33 | 17 | tokens.get_or_use(addr, amount); diff --git a/tests/expectations/compiler/finalize/mapping_fail.out b/tests/expectations/compiler/finalize/mapping_fail.out index 0204ae073c6..d124b3a54ea 100644 --- a/tests/expectations/compiler/finalize/mapping_fail.out +++ b/tests/expectations/compiler/finalize/mapping_fail.out @@ -10,7 +10,7 @@ Error [ETYC0372017]: The type `baz` is not found in the current scope. | ^^^^^^^^^^^^^^^^^^^^^^^^ | = If you are using an external type, make sure to preface with the program name. Ex: `credits.aleo/credits` instead of `credits` -Error [ETYC0372017]: The type `baz` is not found in the current scope. +Error [ETYC0372017]: The type `test.aleo/baz` is not found in the current scope. --> compiler-test:5:5 | 5 | mapping floo: baz => u8; @@ -24,7 +24,7 @@ Error [ETYC0372017]: The type `foo` is not found in the current scope. | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = If you are using an external type, make sure to preface with the program name. Ex: `credits.aleo/credits` instead of `credits` -Error [ETYC0372017]: The type `foo` is not found in the current scope. +Error [ETYC0372017]: The type `test.aleo/foo` is not found in the current scope. --> compiler-test:7:5 | 7 | mapping floop: foo => foo; @@ -38,7 +38,7 @@ Error [ETYC0372017]: The type `foo` is not found in the current scope. | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = If you are using an external type, make sure to preface with the program name. Ex: `credits.aleo/credits` instead of `credits` -Error [ETYC0372017]: The type `foo` is not found in the current scope. +Error [ETYC0372017]: The type `test.aleo/foo` is not found in the current scope. --> compiler-test:7:5 | 7 | mapping floop: foo => foo; @@ -52,7 +52,7 @@ Error [ETYC0372017]: The type `foo` is not found in the current scope. | ^^^^^^^^^^^^^^^^^^^^^^^^ | = If you are using an external type, make sure to preface with the program name. Ex: `credits.aleo/credits` instead of `credits` -Error [ETYC0372017]: The type `foo` is not found in the current scope. +Error [ETYC0372017]: The type `test.aleo/foo` is not found in the current scope. --> compiler-test:9:5 | 9 | mapping bar: foo => baz; @@ -66,7 +66,7 @@ Error [ETYC0372017]: The type `baz` is not found in the current scope. | ^^^^^^^^^^^^^^^^^^^^^^^^ | = If you are using an external type, make sure to preface with the program name. Ex: `credits.aleo/credits` instead of `credits` -Error [ETYC0372017]: The type `baz` is not found in the current scope. +Error [ETYC0372017]: The type `test.aleo/baz` is not found in the current scope. --> compiler-test:9:5 | 9 | mapping bar: foo => baz; diff --git a/tests/expectations/compiler/finalize/set_incorrect_type_fail.out b/tests/expectations/compiler/finalize/set_incorrect_type_fail.out index 4168e249097..908ab02548e 100644 --- a/tests/expectations/compiler/finalize/set_incorrect_type_fail.out +++ b/tests/expectations/compiler/finalize/set_incorrect_type_fail.out @@ -1,9 +1,9 @@ -Error [ETYC0372117]: Expected type `Token` but type `u128` was found. +Error [ETYC0372117]: Expected type `test.aleo/Token` but type `u128` was found. --> compiler-test:16:36 | 16 | Mapping::set(tokens, addr, amount); | ^^^^^^ -Error [ETYC0372117]: Expected type `Token` but type `u128` was found. +Error [ETYC0372117]: Expected type `test.aleo/Token` but type `u128` was found. --> compiler-test:17:26 | 17 | tokens.set(addr, amount); diff --git a/tests/expectations/compiler/function/unknown_parameter_type_fail.out b/tests/expectations/compiler/function/unknown_parameter_type_fail.out index b4dcf913b2e..56f31b19bc8 100644 --- a/tests/expectations/compiler/function/unknown_parameter_type_fail.out +++ b/tests/expectations/compiler/function/unknown_parameter_type_fail.out @@ -12,7 +12,7 @@ Error [ETYC0372017]: The type `Foo` is not found in the current scope. | ^^^ | = If you are using an external type, make sure to preface with the program name. Ex: `credits.aleo/credits` instead of `credits` -Error [ETYC0372117]: Expected type `Foo` but type `u8` was found. +Error [ETYC0372117]: Expected type `test.aleo/Foo` but type `u8` was found. --> compiler-test:8:16 | 8 | return a; diff --git a/tests/expectations/compiler/futures/nested.out b/tests/expectations/compiler/futures/nested.out index 76df3b550b6..d031277a946 100644 --- a/tests/expectations/compiler/futures/nested.out +++ b/tests/expectations/compiler/futures/nested.out @@ -94,8 +94,8 @@ finalize main: add r0[0u32] r1[0u32] into r5; set r5 into ayo[1u32]; // --- Next Program --- // -import test_dep.aleo; import test.aleo; +import test_dep.aleo; program wrapper.aleo; function main: diff --git a/tests/expectations/compiler/futures/partial_type_specification.out b/tests/expectations/compiler/futures/partial_type_specification.out index 3efa5f90df9..b47893d621f 100644 --- a/tests/expectations/compiler/futures/partial_type_specification.out +++ b/tests/expectations/compiler/futures/partial_type_specification.out @@ -104,8 +104,8 @@ finalize main: add r0[0u32] r1[0u32] into r5; set r5 into ayo[1u32]; // --- Next Program --- // -import test_dep.aleo; import test.aleo; +import test_dep.aleo; program wrapper.aleo; function main: diff --git a/tests/expectations/compiler/option/bad_types_fail.out b/tests/expectations/compiler/option/bad_types_fail.out index 200df1e889e..34cf86dc20f 100644 --- a/tests/expectations/compiler/option/bad_types_fail.out +++ b/tests/expectations/compiler/option/bad_types_fail.out @@ -47,7 +47,7 @@ Error [ETYC0372117]: Expected type `u8?` but type `i8` was found. | 51 | let arr: [u8?; 3] = [1u8, none, 3i8]; // ERROR | ^^^ -Error [ETYC0372119]: Received different types `Foo` and `Foo?` for the operation `==`. +Error [ETYC0372119]: Received different types `bad_types.aleo/Foo` and `bad_types.aleo/Foo?` for the operation `==`. --> compiler-test:58:22 | 58 | let result = a == b; // ERROR diff --git a/tests/expectations/compiler/option/input_output_fail.out b/tests/expectations/compiler/option/input_output_fail.out index 14b5fb54f3b..aff388e1953 100644 --- a/tests/expectations/compiler/option/input_output_fail.out +++ b/tests/expectations/compiler/option/input_output_fail.out @@ -12,7 +12,7 @@ Error [ETYC0372164]: The input `y` has type `[u8?; 3]`, which is or contains an | ^^^^^^^^^^^ | = Inputs to `transition`, `async transition`, and `function` definitions cannot be optional or contain optionals. Consider moving the optionality outside the call site. -Error [ETYC0372164]: The input `f` has type `Foo`, which is or contains an optional type and is not allowed as an input to a `transition`, `async transition`, or `function`. +Error [ETYC0372164]: The input `f` has type `test.aleo/Foo`, which is or contains an optional type and is not allowed as an input to a `transition`, `async transition`, or `function`. --> compiler-test:5:41 | 5 | transition foo(x: u8?, y: [u8?; 3], f: Foo) -> (u8?, u8?, Foo) { @@ -33,7 +33,7 @@ Error [ETYC0372165]: This function has an output of type `u8?`, which is or cont | ^^^ | = Outputs of `transition`, `async transition`, and `function` definitions cannot be optional or contain optionals. Consider moving the optionality outside the function call. -Error [ETYC0372165]: This function has an output of type `Foo`, which is or contains an optional type and is not allowed as an output of a `transition`, `async transition`, or `function`. +Error [ETYC0372165]: This function has an output of type `test.aleo/Foo`, which is or contains an optional type and is not allowed as an output of a `transition`, `async transition`, or `function`. --> compiler-test:5:63 | 5 | transition foo(x: u8?, y: [u8?; 3], f: Foo) -> (u8?, u8?, Foo) { @@ -47,7 +47,7 @@ Error [ETYC0372164]: The input `x` has type `u8?`, which is or contains an optio | ^^^^^^ | = Inputs to `transition`, `async transition`, and `function` definitions cannot be optional or contain optionals. Consider moving the optionality outside the call site. -Error [ETYC0372164]: The input `f` has type `Foo`, which is or contains an optional type and is not allowed as an input to a `transition`, `async transition`, or `function`. +Error [ETYC0372164]: The input `f` has type `test.aleo/Foo`, which is or contains an optional type and is not allowed as an input to a `transition`, `async transition`, or `function`. --> compiler-test:9:34 | 9 | async transition bar(x: u8?, f: Foo) { diff --git a/tests/expectations/compiler/option/optional_in_mapping_fail.out b/tests/expectations/compiler/option/optional_in_mapping_fail.out index 56165d4f09e..eaa0de11247 100644 --- a/tests/expectations/compiler/option/optional_in_mapping_fail.out +++ b/tests/expectations/compiler/option/optional_in_mapping_fail.out @@ -3,7 +3,7 @@ Error [ETYC0372161]: The type `address?` is or contains an optional type which c | 6 | mapping optional_balances: address? => Foo; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Error [ETYC0372161]: The type `Foo` is or contains an optional type which cannot be used as the value in a mapping +Error [ETYC0372161]: The type `st.aleo/Foo` is or contains an optional type which cannot be used as the value in a mapping --> compiler-test:6:5 | 6 | mapping optional_balances: address? => Foo; diff --git a/tests/expectations/compiler/option/optional_record_fail.out b/tests/expectations/compiler/option/optional_record_fail.out index 42a8eed4f07..2a21fbf1b64 100644 --- a/tests/expectations/compiler/option/optional_record_fail.out +++ b/tests/expectations/compiler/option/optional_record_fail.out @@ -1,4 +1,4 @@ -Error [ETYC0372160]: The type `Foo` cannot be wrapped in an optional. +Error [ETYC0372160]: The type `optional_record_test.aleo/Foo` cannot be wrapped in an optional. --> compiler-test:10:9 | 10 | let f: Foo? = Foo { diff --git a/tests/expectations/compiler/option/record_with_optional_fields_fail.out b/tests/expectations/compiler/option/record_with_optional_fields_fail.out index acb584c4236..da18056b830 100644 --- a/tests/expectations/compiler/option/record_with_optional_fields_fail.out +++ b/tests/expectations/compiler/option/record_with_optional_fields_fail.out @@ -12,7 +12,7 @@ Error [ETYC0372162]: The field `flags` in this record has type `[bool?; 4]`, whi | ^^^^^^^^^^^^^^^^^ | = Records cannot have fields that are optional or contain optionals. Consider moving the optionality outside the record. -Error [ETYC0372162]: The field `inner` in this record has type `InnerStruct`, which is or contains an optional type. +Error [ETYC0372162]: The field `inner` in this record has type `record_with_optional_fields.aleo/InnerStruct`, which is or contains an optional type. --> compiler-test:20:9 | 20 | inner: InnerStruct, // ERROR: contains optional via nested struct diff --git a/tests/expectations/compiler/records/external_record_fail.out b/tests/expectations/compiler/records/external_record_fail.out new file mode 100644 index 00000000000..9f4eeef353d --- /dev/null +++ b/tests/expectations/compiler/records/external_record_fail.out @@ -0,0 +1,5 @@ +Error [ETYC0372173]: Cannot create external record `child1.aleo/R`. Records can only be created in the program that they are defined in + --> compiler-test:7:17 + | + 7 | let r = child1.aleo/R { owner: addr }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/expectations/compiler/records/return_record_instead_of_unit_fail.out b/tests/expectations/compiler/records/return_record_instead_of_unit_fail.out index 746553e35d2..4cf76c4980b 100644 --- a/tests/expectations/compiler/records/return_record_instead_of_unit_fail.out +++ b/tests/expectations/compiler/records/return_record_instead_of_unit_fail.out @@ -1,4 +1,4 @@ -Error [ETYC0372117]: Expected type `()` but type `test_credits` was found. +Error [ETYC0372117]: Expected type `()` but type `test.aleo/test_credits` was found. --> compiler-test:9:16 | 9 | return test_credits { diff --git a/tests/expectations/compiler/records/same_name_diff_type_fail.out b/tests/expectations/compiler/records/same_name_diff_type_fail.out index 78aa7581e25..566e9acab1f 100644 --- a/tests/expectations/compiler/records/same_name_diff_type_fail.out +++ b/tests/expectations/compiler/records/same_name_diff_type_fail.out @@ -1,4 +1,4 @@ -Error [ETYC0372119]: Received different types `R` and `child.aleo/R` for the operation `==`. +Error [ETYC0372119]: Received different types `parent.aleo/R` and `child.aleo/R` for the operation `==`. --> compiler-test:16:16 | 16 | return r1 == child.aleo/create(); diff --git a/tests/expectations/compiler/signature/signature_with_wrong_types_fail.out b/tests/expectations/compiler/signature/signature_with_wrong_types_fail.out index 574f422aa89..6b56bbd057b 100644 --- a/tests/expectations/compiler/signature/signature_with_wrong_types_fail.out +++ b/tests/expectations/compiler/signature/signature_with_wrong_types_fail.out @@ -18,12 +18,12 @@ Error [ETYC0372117]: Expected type `signature` but type `address` was found. | 16 | let first: bool = signature::verify(a, v, s); | ^ -Error [ETYC0372117]: Expected type `address` but type `foo` was found. +Error [ETYC0372117]: Expected type `address` but type `test.aleo/foo` was found. --> compiler-test:16:48 | 16 | let first: bool = signature::verify(a, v, s); | ^ -Error [ETYC0372117]: Expected type `address` but type `foo` was found. +Error [ETYC0372117]: Expected type `address` but type `test.aleo/foo` was found. --> compiler-test:17:37 | 17 | let second: bool = s.verify(v, a); diff --git a/tests/expectations/compiler/statements/assign_fail.out b/tests/expectations/compiler/statements/assign_fail.out index bf95f72d7bf..38dec619a54 100644 --- a/tests/expectations/compiler/statements/assign_fail.out +++ b/tests/expectations/compiler/statements/assign_fail.out @@ -61,7 +61,7 @@ Error [ETYC0372000]: Invalid assignment target: (x, x, x). | ^^^^^^^^^ | = Valid assignment targets are identifiers, tuple accesses, array accesses, and struct accesses. -Error [ETYC0372000]: Invalid assignment target: x { x: y, y: z }. +Error [ETYC0372000]: Invalid assignment target: test.aleo/x { x: y, y: z }. --> compiler-test:22:9 | 22 | x {x: y, y: z} = y; diff --git a/tests/expectations/compiler/statements/unknown_type_in_definition_fail.out b/tests/expectations/compiler/statements/unknown_type_in_definition_fail.out index 4392d1ad331..8fe1fafe47c 100644 --- a/tests/expectations/compiler/statements/unknown_type_in_definition_fail.out +++ b/tests/expectations/compiler/statements/unknown_type_in_definition_fail.out @@ -5,7 +5,7 @@ Error [ETYC0372017]: The type `Foo` is not found in the current scope. | ^^^^^^^^^^^^^^^^^ | = If you are using an external type, make sure to preface with the program name. Ex: `credits.aleo/credits` instead of `credits` -Error [ETYC0372117]: Expected type `Foo` but type `u8` was found. +Error [ETYC0372117]: Expected type `test.aleo/Foo` but type `u8` was found. --> compiler-test:4:19 | 4 | let b: Foo = 1u8; diff --git a/tests/expectations/compiler/structs/cyclic_structs_one_fail.out b/tests/expectations/compiler/structs/cyclic_structs_one_fail.out index 74afee999b6..21fe6e89e8b 100644 --- a/tests/expectations/compiler/structs/cyclic_structs_one_fail.out +++ b/tests/expectations/compiler/structs/cyclic_structs_one_fail.out @@ -1,4 +1,4 @@ -Error [ETYC0372058]: Cyclic dependency between composites: `Foo` --> `Foo` +Error [ETYC0372058]: Cyclic dependency between composites: `test.aleo/Foo` --> `test.aleo/Foo` Error [ETYC0372083]: A program must have at least one transition function. --> compiler-test:2:9 | diff --git a/tests/expectations/compiler/structs/cyclic_structs_three_fail.out b/tests/expectations/compiler/structs/cyclic_structs_three_fail.out index 1f074a48e5c..308f1d2e439 100644 --- a/tests/expectations/compiler/structs/cyclic_structs_three_fail.out +++ b/tests/expectations/compiler/structs/cyclic_structs_three_fail.out @@ -1,4 +1,4 @@ -Error [ETYC0372058]: Cyclic dependency between composites: `One` --> `Two` --> `Three` --> `One` +Error [ETYC0372058]: Cyclic dependency between composites: `test.aleo/One` --> `test.aleo/Two` --> `test.aleo/Three` --> `test.aleo/One` Error [ETYC0372083]: A program must have at least one transition function. --> compiler-test:2:9 | diff --git a/tests/expectations/compiler/structs/cyclic_structs_two_fail.out b/tests/expectations/compiler/structs/cyclic_structs_two_fail.out index 7025599fbf4..94a01d05633 100644 --- a/tests/expectations/compiler/structs/cyclic_structs_two_fail.out +++ b/tests/expectations/compiler/structs/cyclic_structs_two_fail.out @@ -1,4 +1,4 @@ -Error [ETYC0372058]: Cyclic dependency between composites: `Bar` --> `Baz` --> `Bar` +Error [ETYC0372058]: Cyclic dependency between composites: `test.aleo/Bar` --> `test.aleo/Baz` --> `test.aleo/Bar` Error [ETYC0372083]: A program must have at least one transition function. --> compiler-test:2:9 | diff --git a/tests/expectations/compiler/structs/external_struct.out b/tests/expectations/compiler/structs/external_struct.out index 3a96844336a..1f17af045e2 100644 --- a/tests/expectations/compiler/structs/external_struct.out +++ b/tests/expectations/compiler/structs/external_struct.out @@ -51,26 +51,10 @@ record BooHoo: val as u32.private; woo as Woo.private; -struct Two: - val1 as u32; - val2 as u32; - -struct One: - two as [Two; 2u32]; - -struct Baz: - one as One; - -struct Bar: - baz as [Baz; 2u32]; - -struct Foo: - bar as [Bar; 1u32]; - function create_wrapper: call child.aleo/create into r0 r1; call child.aleo/create into r2 r3; - output r2 as Foo.private; + output r2 as child.aleo/Foo.private; output r3 as child.aleo/Boo.record; function create_another_wrapper: @@ -78,7 +62,7 @@ function create_another_wrapper: cast 1u32 2u32 into r2 as Woo; cast self.signer 10u32 r2 into r3 as BooHoo.record; cast 3u32 4u32 into r4 as Woo; - output r0 as Foo.private; + output r0 as child.aleo/Foo.private; output r1 as child.aleo/Boo.record; output r3 as BooHoo.record; output r4 as Woo.private; @@ -87,26 +71,6 @@ import child.aleo; import parent.aleo; program grandparent.aleo; -struct Two: - val1 as u32; - val2 as u32; - -struct One: - two as [Two; 2u32]; - -struct Baz: - one as One; - -struct Bar: - baz as [Baz; 2u32]; - -struct Foo: - bar as [Bar; 1u32]; - -struct Woo: - a as u32; - b as u32; - function main: input r0 as u32.private; add 1u32 r0 into r1; @@ -115,7 +79,7 @@ function main: function omega_wrapper: call parent.aleo/create_another_wrapper into r0 r1 r2 r3; call parent.aleo/create_another_wrapper into r4 r5 r6 r7; - output r4 as Foo.private; + output r4 as child.aleo/Foo.private; output r5 as child.aleo/Boo.record; output r6 as parent.aleo/BooHoo.record; - output r7 as Woo.private; + output r7 as parent.aleo/Woo.private; diff --git a/tests/expectations/compiler/structs/external_struct_in_async_function.out b/tests/expectations/compiler/structs/external_struct_in_async_function.out index f2f78e8dec9..4af515474a0 100644 --- a/tests/expectations/compiler/structs/external_struct_in_async_function.out +++ b/tests/expectations/compiler/structs/external_struct_in_async_function.out @@ -16,9 +16,5 @@ finalize init: import parent.aleo; program child.aleo; -struct TestStruct: - data0 as u128; - data1 as u128; - function main: output 1u32 as u32.private; diff --git a/tests/expectations/compiler/structs/redefine_external_struct.out b/tests/expectations/compiler/structs/redefine_external_struct.out deleted file mode 100644 index f33eb6ddf4b..00000000000 --- a/tests/expectations/compiler/structs/redefine_external_struct.out +++ /dev/null @@ -1,57 +0,0 @@ -program child.aleo; - -struct Two: - val1 as u32; - val2 as u32; - -struct One: - two as [Two; 2u32]; - -struct Baz: - one as One; - -struct Bar: - baz as [Baz; 2u32]; - -struct Foo: - bar as [Bar; 1u32]; - -function create: - cast 1u32 2u32 into r0 as Two; - cast 3u32 4u32 into r1 as Two; - cast r0 r1 into r2 as [Two; 2u32]; - cast r2 into r3 as One; - cast r3 into r4 as Baz; - cast 5u32 6u32 into r5 as Two; - cast 7u32 8u32 into r6 as Two; - cast r5 r6 into r7 as [Two; 2u32]; - cast r7 into r8 as One; - cast r8 into r9 as Baz; - cast r4 r9 into r10 as [Baz; 2u32]; - cast r10 into r11 as Bar; - cast r11 into r12 as [Bar; 1u32]; - cast r12 into r13 as Foo; - output r13 as Foo.private; -// --- Next Program --- // -import child.aleo; -program parent.aleo; - -struct Two: - val1 as u32; - val2 as u32; - -struct One: - two as [Two; 2u32]; - -struct Baz: - one as One; - -struct Bar: - baz as [Baz; 2u32]; - -struct Foo: - bar as [Bar; 1u32]; - -function create_wrapper: - call child.aleo/create into r0; - output r0 as Foo.private; diff --git a/tests/expectations/compiler/structs/redefine_external_struct_fail.out b/tests/expectations/compiler/structs/redefine_external_struct_fail.out new file mode 100644 index 00000000000..4568ca862c1 --- /dev/null +++ b/tests/expectations/compiler/structs/redefine_external_struct_fail.out @@ -0,0 +1,5 @@ +Error [ETYC0372003]: Expected type `parent.aleo/Foo` but type `child.aleo/Foo` was found + --> compiler-test:28:16 + | + 28 | return child.aleo/create(); + | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/expectations/compiler/structs/shadow_external_struct_fail.out b/tests/expectations/compiler/structs/shadow_external_struct_fail.out index 51f54b74fe6..4568ca862c1 100644 --- a/tests/expectations/compiler/structs/shadow_external_struct_fail.out +++ b/tests/expectations/compiler/structs/shadow_external_struct_fail.out @@ -1,13 +1,5 @@ -Error [EAST0372015]: There are two definitions of struct `Bar` that do not match. - --> compiler-test:9:5 +Error [ETYC0372003]: Expected type `parent.aleo/Foo` but type `child.aleo/Foo` was found + --> compiler-test:28:16 | - 9 | struct Bar { - | ^^^^^^^^^^^^ - 10 | baz: [Baz; 2], - | ^^^^^^^^^^^^^^ - 11 | bog: u32, - | ^^^^^^^^^ - 12 | } - | ^ - | - = Check the import files to see if there are any struct definitions of the same name. + 28 | return child.aleo/create(); + | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/expectations/compiler/structs/struct_contains_record_fail.out b/tests/expectations/compiler/structs/struct_contains_record_fail.out index 33d2639b1cd..dfba5295387 100644 --- a/tests/expectations/compiler/structs/struct_contains_record_fail.out +++ b/tests/expectations/compiler/structs/struct_contains_record_fail.out @@ -5,7 +5,7 @@ Error [ETYC0372030]: A struct or record cannot contain another record. | ^^^^^ | = Remove the record `Token` from `Foo`. -Error [ETYC0372058]: Cyclic dependency between composites: `Token` --> `Foo` --> `Token` +Error [ETYC0372058]: Cyclic dependency between composites: `test.aleo/Token` --> `test.aleo/Foo` --> `test.aleo/Token` Error [ETYC0372083]: A program must have at least one transition function. --> compiler-test:2:9 | diff --git a/tests/expectations/execution/counter.out b/tests/expectations/execution/counter.out index 569b2b9f115..1f997be7ac1 100644 --- a/tests/expectations/execution/counter.out +++ b/tests/expectations/execution/counter.out @@ -43,20 +43,20 @@ status: accepted { "transitions": [ { - "id": "au1lrn5mu86wzrwqgmpfvk89k47rkehtp2f3vamqgwwmrvz5guz7s8qnd3w6c", + "id": "au1k3qrpjr9fwl05xsq0p3qhg36yp6jylr5dns5hk79cr3kx5j4wqfqsgalhj", "program": "test.aleo", "function": "dubble", "inputs": [], "outputs": [ { "type": "future", - "id": "4699002505616774710150113522851534941479463253233984646704309032947182186747field", + "id": "3233956273400426504677799187768392831171688829224614779171895997795596094303field", "value": "{\n program_id: test.aleo,\n function_name: dubble,\n arguments: [\n aleo17z49cl3wfpjdyu5juxaxnuttag24ygz36pg8ln2qmlcsw4w8cs9s3f45uq\n ]\n}" } ], - "tpk": "2792598392872113523769625442726729417421328914138543245439106255595108589581group", - "tcm": "7535494565811075966069535028531130763266967652806164109327127925118110971448field", - "scm": "2280325585512955590354995762895462889624737245155815941037937959226874721339field" + "tpk": "5465601947645018841081878297195945135397754179690311884909269558490091540985group", + "tcm": "4456024893941171008889659456881015783646280184588859247684246364113972719463field", + "scm": "7595424472684114678723002470028531639770730813997210355601309526631584832665field" } ], "global_state_root": "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu" diff --git a/tests/expectations/execution/counter_async_block.out b/tests/expectations/execution/counter_async_block.out index 3447b7490d5..69c52aac1c3 100644 --- a/tests/expectations/execution/counter_async_block.out +++ b/tests/expectations/execution/counter_async_block.out @@ -46,20 +46,20 @@ status: accepted { "transitions": [ { - "id": "au1lrn5mu86wzrwqgmpfvk89k47rkehtp2f3vamqgwwmrvz5guz7s8qnd3w6c", + "id": "au1k3qrpjr9fwl05xsq0p3qhg36yp6jylr5dns5hk79cr3kx5j4wqfqsgalhj", "program": "test.aleo", "function": "dubble", "inputs": [], "outputs": [ { "type": "future", - "id": "4699002505616774710150113522851534941479463253233984646704309032947182186747field", + "id": "3233956273400426504677799187768392831171688829224614779171895997795596094303field", "value": "{\n program_id: test.aleo,\n function_name: dubble,\n arguments: [\n aleo17z49cl3wfpjdyu5juxaxnuttag24ygz36pg8ln2qmlcsw4w8cs9s3f45uq\n ]\n}" } ], - "tpk": "2792598392872113523769625442726729417421328914138543245439106255595108589581group", - "tcm": "7535494565811075966069535028531130763266967652806164109327127925118110971448field", - "scm": "2280325585512955590354995762895462889624737245155815941037937959226874721339field" + "tpk": "5465601947645018841081878297195945135397754179690311884909269558490091540985group", + "tcm": "4456024893941171008889659456881015783646280184588859247684246364113972719463field", + "scm": "7595424472684114678723002470028531639770730813997210355601309526631584832665field" } ], "global_state_root": "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu" diff --git a/tests/expectations/execution/counter_storage.out b/tests/expectations/execution/counter_storage.out index a5342ff8858..d21b4031b24 100644 --- a/tests/expectations/execution/counter_storage.out +++ b/tests/expectations/execution/counter_storage.out @@ -62,20 +62,20 @@ status: accepted { "transitions": [ { - "id": "au1nx4qm8ptew3ch92fdrtv97msel5uvwyvmwav3975qevzqkvnmv9sl4z8l6", + "id": "au1hczfq642j4w3emk8wxsmz8skw967heywtt3vrgzg86w4nxjnxszs8tsdzg", "program": "test.aleo", "function": "increment", "inputs": [], "outputs": [ { "type": "future", - "id": "3124286148824508223344846915728798959157118578708868696845286916494390666359field", + "id": "6583195300408321241841212771428866298368527666258877518914082151436888144955field", "value": "{\n program_id: test.aleo,\n function_name: increment,\n arguments: []\n}" } ], - "tpk": "2792598392872113523769625442726729417421328914138543245439106255595108589581group", - "tcm": "7535494565811075966069535028531130763266967652806164109327127925118110971448field", - "scm": "2280325585512955590354995762895462889624737245155815941037937959226874721339field" + "tpk": "5465601947645018841081878297195945135397754179690311884909269558490091540985group", + "tcm": "4456024893941171008889659456881015783646280184588859247684246364113972719463field", + "scm": "7595424472684114678723002470028531639770730813997210355601309526631584832665field" } ], "global_state_root": "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu" diff --git a/tests/expectations/execution/metadata.out b/tests/expectations/execution/metadata.out index 1052ac2f7cd..71ffda5d125 100644 --- a/tests/expectations/execution/metadata.out +++ b/tests/expectations/execution/metadata.out @@ -26,26 +26,26 @@ status: rejected { "transitions": [ { - "id": "au1jackrjt67g94g926yq48c3vwfl67rggapqld3t7dutr4yytkt5zsgjutv4", + "id": "au1s0gm9yhhvgn23vx3r3pg6d7yy5ffvtzrjf89lfg38h9kfqjh0sqstxtdn3", "program": "metadata.aleo", "function": "is_block_height", "inputs": [ { "type": "private", - "id": "1294839953064067156857575820936554662953140004732414160471488174683467816510field", - "value": "ciphertext1qyqxx648c9zd2y9dxpaurthdm3fzrwxetpka2lfnvz0qpglt3zdm2pqd5ddrr" + "id": "4846963621798475150749496154581047931998293867310233519948418219451788691329field", + "value": "ciphertext1qyqyna788gg46ndwjnuk2dvm3fdhnmhljc8gfvtq5mg39nlw2wye6qqs4gv72" } ], "outputs": [ { "type": "future", - "id": "4158360135649145120140519383007103543182806766660285349681004290052865664798field", + "id": "2064355038678810937548059204940232805140347901230195252445439443570838432340field", "value": "{\n program_id: metadata.aleo,\n function_name: is_block_height,\n arguments: [\n 2u32\n ]\n}" } ], - "tpk": "6373718224838073536803860829638049194561989564755401205658301015696191366090group", - "tcm": "5664822226403379628159757940447744056069986517068960076928226892917348704296field", - "scm": "5676325233121699107048557908033121840844329323401950925709080348351425973728field" + "tpk": "1037733386133164487895105535297327187325548613067990244211437457519327093150group", + "tcm": "4161115002753557406790356437682820319476310774803166952336318536904039834699field", + "scm": "1767206995291351169359419245851931061006702694950490148547970085300330746273field" } ], "global_state_root": "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu" @@ -56,26 +56,26 @@ status: rejected { "transitions": [ { - "id": "au1x5vvndjs4vrtv6s74tufng7h6d6w3cz7qskrk3cn4nxyfrnh45qsmzc25k", + "id": "au1le5fx9jauauyr5yr6wkyype03vdjgvquvkgl0rx0sn9pygvpngxs3vcxey", "program": "metadata.aleo", "function": "is_block_height", "inputs": [ { "type": "private", - "id": "1558278947076474301307539997072376696837324145741763768200065013508250007359field", - "value": "ciphertext1qyqxs7vgp3c9j35kx6kn5mhs7u3w5mh47twvzmvg8muqu78jlphpgqgeuvhyt" + "id": "5702696966793663202499782319916724044124069473964132885381316022652329900388field", + "value": "ciphertext1qyq0zlwf4c9v00upfx6hfdatyc8f446c8ug8v07amxmfqwjgj7uqsyqvcsjev" } ], "outputs": [ { "type": "future", - "id": "3886948426754258966154680089357685334228941516416845286599856474879455286656field", + "id": "3343229426299515833174904869678186447600115021526164874391816899455483746558field", "value": "{\n program_id: metadata.aleo,\n function_name: is_block_height,\n arguments: [\n 3u32\n ]\n}" } ], - "tpk": "39172694768842210914502146058620112304946170410395658646753799744410747064group", - "tcm": "2496119321884656043910452253295201063060884739589327489089014755543056702398field", - "scm": "6724231322804026917996535940522620939424458967853759012191601743219837577781field" + "tpk": "5422441079140376386027825990630635393479030665795976804681063166579976461686group", + "tcm": "7112819813982170625714805914576650830627295345950626636683449462716465060500field", + "scm": "6916381046843960936802724789017562067965699303989918117061516147246879785979field" } ], "global_state_root": "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu" @@ -86,26 +86,26 @@ status: rejected { "transitions": [ { - "id": "au1uk2x2dc9spaqqrxj0lsxxu9sxxqjjuzsaqtm2mfff6z2u832juxs7r3rru", + "id": "au1hk2avpqar5q2nck4nx7m6zypcms6f0p8h6v5le0z3el7pnam9crshh22ne", "program": "metadata.aleo", "function": "is_block_height", "inputs": [ { "type": "private", - "id": "6379577413424842942405641019573135037957664278537258759688355266373400489725field", - "value": "ciphertext1qyqqmv40ep9r6l7pqhlra69kn6268kmsx5tvh9qdd3gzkhzxw893czgr0fxa9" + "id": "7966173243684757081607681515217639316367086449493416557478198746943710314242field", + "value": "ciphertext1qyq9cumxsmfjesdan2zxmwrsjjntncmy0lfwgqxl00qu6qlnf3j2crqtpc4f8" } ], "outputs": [ { "type": "future", - "id": "2776245219956693805106631470667957714710604290488560519040569941854764861832field", + "id": "3150477797081950289533878102225034337160507603624069175624890568060770817928field", "value": "{\n program_id: metadata.aleo,\n function_name: is_block_height,\n arguments: [\n 4u32\n ]\n}" } ], - "tpk": "7384116050582086575935631193340748878808800662028117978885163706306318514015group", - "tcm": "7958548178555411153280486439782136041901623537216192170031606835078444579309field", - "scm": "2382781258281984239008280911484366196292241519527402598792694336925628223608field" + "tpk": "1715878919017708244489643674044350712574567861495854528259980134485134198623group", + "tcm": "5019594720243264972653104700440323889935215405336436460795069654271273503118field", + "scm": "4728738306624013911560366259140291399329734466085125511892464913624776269937field" } ], "global_state_root": "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu" @@ -116,26 +116,26 @@ status: rejected { "transitions": [ { - "id": "au1sj7jsz4c0jnm93xz8hhggj6nk2dc6efvxyzwvsmjgxaaerzhrcqsrg60es", + "id": "au1c5ks3q8tsvh59hgawq055hsaxa86lamfjc8lje3s3pnmsrehgvqsxwuew9", "program": "metadata.aleo", "function": "is_block_height", "inputs": [ { "type": "private", - "id": "6359923224311755868065528744723809170051032358192737056214751584473776206035field", - "value": "ciphertext1qyq97j0zkr5tqt4hp00gj9pkg8rwt5mzz77tjg4fpaal6af8qw99sqswk5e6g" + "id": "129329169712817028830668982863869799545744778509554165841345628789140642895field", + "value": "ciphertext1qyq9n3y2sf9frfnp3g9smxhcrk4mzdnrzn4fz8hfqxmy0mwhew4puzsfsx2c8" } ], "outputs": [ { "type": "future", - "id": "3667896864885647615763977628109575028690565794207037550211113491833001498840field", + "id": "5755975591739165346224682266718075871498229645768428873577106934493568842843field", "value": "{\n program_id: metadata.aleo,\n function_name: is_block_height,\n arguments: [\n 0u32\n ]\n}" } ], - "tpk": "5841989403237930599352238695504393294286797955406418056150390796974313792897group", - "tcm": "6248428856114999695301782955203667003685028995926905376745150469929116104312field", - "scm": "7711717192842610855997755234040226957750594219730770897763551949439745717479field" + "tpk": "705616130960512286624689198794381008518610542371784592894869991578499896254group", + "tcm": "5449468415219727077425126056443108331061618712701226145125857579985164381614field", + "scm": "6068853925134294555865760493364271476254425309712443139049615406364334083342field" } ], "global_state_root": "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu" diff --git a/tests/expectations/execution/nested_external_struct.out b/tests/expectations/execution/nested_external_struct.out new file mode 100644 index 00000000000..1991ced48e2 --- /dev/null +++ b/tests/expectations/execution/nested_external_struct.out @@ -0,0 +1,50 @@ +program child1.aleo; + +struct Boo: + a as u32; + b as u32; + +struct Foo: + c as Boo; + +struct Coo: + x as u32; + y as u32; + +function main_2: + input r0 as Foo.private; + output r0.c as Boo.private; + +constructor: + assert.eq edition 0u16; +// --- Next Program --- // +import child1.aleo; +program external_structs.aleo; + +struct Foo: + c as child1.aleo/Foo; + +function main: + cast 1u32 1u32 into r0 as child1.aleo/Boo; + cast r0 into r1 as child1.aleo/Foo; + call child1.aleo/main_2 r1 into r2; + cast r1 into r3 as Foo; + cast 0u32 0u32 into r4 as child1.aleo/Coo; + output r0 as child1.aleo/Boo.private; + output r1 as child1.aleo/Foo.private; + output r2 as child1.aleo/Boo.private; + output r3 as Foo.private; + output r4 as child1.aleo/Coo.private; + +constructor: + assert.eq edition 0u16; +status: success +output: ({a: 1u32, b: 1u32}, {c: { + a: 1u32, + b: 1u32 +}}, {a: 1u32, b: 1u32}, {c: { + c: { + a: 1u32, + b: 1u32 + } +}}, {x: 0u32, y: 0u32}) diff --git a/tests/expectations/execution/optional_inputs_and_outputs_fail.out b/tests/expectations/execution/optional_inputs_and_outputs_fail.out index aabf9729af7..4721c077d7a 100644 --- a/tests/expectations/execution/optional_inputs_and_outputs_fail.out +++ b/tests/expectations/execution/optional_inputs_and_outputs_fail.out @@ -44,14 +44,14 @@ Error [ETYC0372165]: This function has an output of type `bool?`, which is or co | ^^^^^ | = Outputs of `transition`, `async transition`, and `function` definitions cannot be optional or contain optionals. Consider moving the optionality outside the function call. -Error [ETYC0372164]: The input `f` has type `Foo?`, which is or contains an optional type and is not allowed as an input to a `transition`, `async transition`, or `function`. +Error [ETYC0372164]: The input `f` has type `test.aleo/Foo?`, which is or contains an optional type and is not allowed as an input to a `transition`, `async transition`, or `function`. --> compiler-test:98:32 | 98 | transition struct_optional(f: Foo?) -> Foo? { | ^^^^^^^ | = Inputs to `transition`, `async transition`, and `function` definitions cannot be optional or contain optionals. Consider moving the optionality outside the call site. -Error [ETYC0372165]: This function has an output of type `Foo?`, which is or contains an optional type and is not allowed as an output of a `transition`, `async transition`, or `function`. +Error [ETYC0372165]: This function has an output of type `test.aleo/Foo?`, which is or contains an optional type and is not allowed as an output of a `transition`, `async transition`, or `function`. --> compiler-test:98:44 | 98 | transition struct_optional(f: Foo?) -> Foo? { diff --git a/tests/expectations/execution/program_core_functions.out b/tests/expectations/execution/program_core_functions.out index 4d9ba758125..27bec362173 100644 --- a/tests/expectations/execution/program_core_functions.out +++ b/tests/expectations/execution/program_core_functions.out @@ -24,20 +24,20 @@ status: accepted { "transitions": [ { - "id": "au1zlph9rjgjvh0w0f25ztldn9ktgln7xzhw2p7tufud78lsnpphg9s79a8km", + "id": "au1qz69xvs04x0m2ray3rvc5jacx6gws0zurkqptvfemghs8hx8hypqa002kf", "program": "test.aleo", "function": "bar", "inputs": [], "outputs": [ { "type": "future", - "id": "2830144795159410097388196712300234903742763089453232479279901002821385897244field", + "id": "4699745965752831089430567920988765685453326082501789796524746979033457740377field", "value": "{\n program_id: test.aleo,\n function_name: bar,\n arguments: []\n}" } ], - "tpk": "5284609865564132093347224778056591895184410323243253614423496030005707308355group", - "tcm": "7103811041439417745319663604957027273886603687770675752281064201434298169800field", - "scm": "7590082706830837988434962777194004427398370905191454924832312766866084361972field" + "tpk": "8231475703253287372590484741349140455137211543808322847099566813640903319657group", + "tcm": "6581805877784409078845114337504939553469074289477993496724600664947152224272field", + "scm": "5301056063063297798392181207155541699061262732731840836225713887042788696897field" } ], "global_state_root": "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu" diff --git a/tests/expectations/execution/record_and_duplicate_outputs.out b/tests/expectations/execution/record_and_duplicate_outputs.out index 34afad385c1..6f17756dd5f 100644 --- a/tests/expectations/execution/record_and_duplicate_outputs.out +++ b/tests/expectations/execution/record_and_duplicate_outputs.out @@ -56,10 +56,6 @@ import test0.aleo; import test1.aleo; program test2.aleo; -struct S: - x as field; - y as field; - function test_pass_through: call test0.aleo/make into r0; call test1.aleo/pass_through r0 into r1; diff --git a/tests/expectations/execution/serialize_deserialize_roundtrip_async.out b/tests/expectations/execution/serialize_deserialize_roundtrip_async.out index d462ac19931..4008c4c3509 100644 --- a/tests/expectations/execution/serialize_deserialize_roundtrip_async.out +++ b/tests/expectations/execution/serialize_deserialize_roundtrip_async.out @@ -62,26 +62,26 @@ status: accepted { "transitions": [ { - "id": "au1shadxrl25q2nz2sexafw3etvw5m69aufewgensju759jyua6xvrs5nm3f8", + "id": "au19nexapz087akayae3uqgh522g25fxyv0xp6a8ywek3j0m8u8cupqwz0kd6", "program": "test.aleo", "function": "test_async_u32_rt", "inputs": [ { "type": "private", - "id": "2868378881388724611406574820556945088383795096179732713204207005453275371086field", - "value": "ciphertext1qyq9tavdlnwrg7gf4uwa8u3g55nd62s86aevzkprqgdyjhdym559grc3nsn88" + "id": "7872820192532793991107841685019024387154487552441142401916800230956142850213field", + "value": "ciphertext1qyqwtgllmez3wm64vk300dz6zre47gwnvnntmrucaacxt833wdh65rcl5wlqp" } ], "outputs": [ { "type": "future", - "id": "3192120973334120765325780919114439804589613852380345951867872474894602539367field", + "id": "394152274748893770187438028945259942225380409366084377478509857243146495145field", "value": "{\n program_id: test.aleo,\n function_name: test_async_u32_rt,\n arguments: [\n aleo17z49cl3wfpjdyu5juxaxnuttag24ygz36pg8ln2qmlcsw4w8cs9s3f45uq,\n 99999u32\n ]\n}" } ], - "tpk": "5660398565090144723436677793436897092228950367852469398575650801378609384422group", - "tcm": "2738244710947030237832595256365557251325966127181449046413737318721535610545field", - "scm": "7582363085240556321928187664577258728215206353830965880405066011935578134118field" + "tpk": "4911753537381571406394978853124287153821036223125176760481690950466530957624group", + "tcm": "4759772859956569193066856316215132388678156975913082094130572670060191993828field", + "scm": "86437200825074871597814752850542673831869300690619248399347154688890501806field" } ], "global_state_root": "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu" @@ -92,26 +92,26 @@ status: accepted { "transitions": [ { - "id": "au1zqh5rvs808w88mzeg552waqruyq0mxtvrex26fky7nx60ws8p5yq4kh88q", + "id": "au1du3hdtty04nuhmkcls448kaqtkuen43vsargl9p07ujfv0te4uyq9pffql", "program": "test.aleo", "function": "test_async_field_rt", "inputs": [ { "type": "private", - "id": "2754965250772267279366359846788189449148903991770961285845655667698669603770field", - "value": "ciphertext1qgqzuqj0yvm23fdkvyazqjr9ktgt2f436rhlvpnkvkhqle7hr2na5pqx9ayav4qp9uamkdxfrrv7esf4yz6j0qe3azcn4d65zvs9sj35pgsxxr27" + "id": "7882967983309275370175862365683649532565143871886062661261304971850248619089field", + "value": "ciphertext1qgq8cevhtau3jf5mrcv8f390u7pwz8slrewst89tfcsq5qdxdaet7rd0nw8mhsks7cs4hrjrnp7gz8cxhzwrup6ldxdc02fk2v5fvmh7pq2363t0" } ], "outputs": [ { "type": "future", - "id": "6548694937029041869532281043204160112986800628271778497596626815077998521018field", + "id": "4358968372552916604427859484208239846046240832000199894172608620440789540689field", "value": "{\n program_id: test.aleo,\n function_name: test_async_field_rt,\n arguments: [\n aleo17z49cl3wfpjdyu5juxaxnuttag24ygz36pg8ln2qmlcsw4w8cs9s3f45uq,\n 999999field\n ]\n}" } ], - "tpk": "1038226886578584672742226305561309883052984123828222392342035160065569671124group", - "tcm": "7294664264527221268877682662740949695329034861352048163725286751297874591102field", - "scm": "4292356555445778388690748455863032266886149286419748150691317631828734598194field" + "tpk": "209292735147715910875270129034805055046597959131521779297707428629106136618group", + "tcm": "1970327148680905983743796062204318725140499567203193254443144844142276733617field", + "scm": "2740222249537301854462068529592413612617984825573848022086511818677931369257field" } ], "global_state_root": "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu" @@ -122,26 +122,26 @@ status: accepted { "transitions": [ { - "id": "au15d08k00tldvsu5fqzwfmvdywnuntssh7d4rr75huvvfz3c275qrqcxnm7y", + "id": "au1exawa75vr4xp4xf2c2nu2dr9krnt57xhdpg7tucefsvvn795vsys04vulf", "program": "test.aleo", "function": "test_async_array_rt", "inputs": [ { "type": "private", - "id": "6093950017082810363728534158936559023873587345837592907652770931764453094704field", - "value": "ciphertext1qyqxmwy2easq90umxdf4msle5gq8ewnulj30pyq56ff773lsu7ut5yqlhand3" + "id": "4709747805078357131267940805042985696913125997610438247353101720094349241783field", + "value": "ciphertext1qyq8jdcsd7ymjygqq2qe29g0cv2t25ul520wyjn5c8srywj7t9kcvpqyptry2" } ], "outputs": [ { "type": "future", - "id": "660935881442049201012033979395573923449139988248876478291908179535957938238field", + "id": "4469554401926944257782094891226832894198973367397860480437602991897530160443field", "value": "{\n program_id: test.aleo,\n function_name: test_async_array_rt,\n arguments: [\n aleo17z49cl3wfpjdyu5juxaxnuttag24ygz36pg8ln2qmlcsw4w8cs9s3f45uq,\n [\n 100u8,\n 200u8\n]\n ]\n}" } ], - "tpk": "3075139660232334913543363379665037087953145520234241393097183682318809189089group", - "tcm": "5233941980130452935171934847427463952994927531250035042817220464841513448228field", - "scm": "379438492354527206433964474615967510798819561162008236805270467280222610291field" + "tpk": "1330910840714250719409149336977092414613402834968623094831863775070011224457group", + "tcm": "8141395499375965316031136890030665920171108565300195813287305645385506082632field", + "scm": "3097266260761903243972527392204825700217710537295771493122579222185487283292field" } ], "global_state_root": "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu" diff --git a/tests/expectations/execution/simple_external_struct.out b/tests/expectations/execution/simple_external_struct.out new file mode 100644 index 00000000000..d74ba5e5dd0 --- /dev/null +++ b/tests/expectations/execution/simple_external_struct.out @@ -0,0 +1,81 @@ +program child1.aleo; + +struct Foo: + x as u32; + y as u32; + z as u32; + +function main1: + input r0 as Foo.private; + +function main2: + input r0 as u32.private; + input r1 as u32.private; + input r2 as u32.private; + cast r0 r1 r2 into r3 as Foo; + output r3 as Foo.private; + +constructor: + assert.eq edition 0u16; +// --- Next Program --- // +program child2.aleo; + +struct Foo: + x as u32; + y as u32; + z as u32; + w as u32; + +function main1: + input r0 as Foo.private; + +function main2: + input r0 as u32.private; + input r1 as u32.private; + input r2 as u32.private; + input r3 as u32.private; + cast r0 r1 r2 r3 into r4 as Foo; + output r4 as Foo.private; + +constructor: + assert.eq edition 0u16; +// --- Next Program --- // +import child1.aleo; +import child2.aleo; +program external_structs.aleo; + +struct Foo: + x as u32; + y as u32; + +function main1: + input r0 as Foo.private; + input r1 as child1.aleo/Foo.private; + input r2 as child2.aleo/Foo.private; + add r0.x r0.y into r3; + add r3 r1.x into r4; + add r4 r1.y into r5; + add r5 r1.z into r6; + add r6 r2.x into r7; + add r7 r2.y into r8; + add r8 r2.z into r9; + add r9 r2.w into r10; + assert.eq r10 495u32; + +function main2: + call child1.aleo/main2 1u32 2u32 3u32 into r0; + call child2.aleo/main2 1u32 2u32 3u32 4u32 into r1; + add r0.x r0.y into r2; + add r2 r0.z into r3; + add r3 r1.x into r4; + add r4 r1.y into r5; + add r5 r1.z into r6; + add r6 r1.w into r7; + output r7 as u32.public; + +constructor: + assert.eq edition 0u16; +status: success +output: () +status: success +output: 16u32 diff --git a/tests/expectations/execution/simple_vector.out b/tests/expectations/execution/simple_vector.out index fe75bec8bc8..f8997197d55 100644 --- a/tests/expectations/execution/simple_vector.out +++ b/tests/expectations/execution/simple_vector.out @@ -189,20 +189,20 @@ status: accepted { "transitions": [ { - "id": "au1zk8pmepmc56fy0zjk7h9xpudsgjtj4un07tkt83ntywmuek9l5ps2nfnaz", + "id": "au1ggx6vcnuulcl9x7ksxceqjak25e7q6j7ml80swzcdkv8xk7ydsqs4ln39s", "program": "test.aleo", "function": "test_vector_ops", "inputs": [], "outputs": [ { "type": "future", - "id": "3658718800749331249857768158928798052968410765669203454913665106390105323692field", + "id": "5142027608027802576574091123989065072230784639079368114688070529765585579166field", "value": "{\n program_id: test.aleo,\n function_name: test_vector_ops,\n arguments: []\n}" } ], - "tpk": "7668228684123037423290651560053952673915692174949790925270972374410791078537group", - "tcm": "8089269036654755001611186981136362217206084894873779054625214100448097339808field", - "scm": "1638739047108859317620099961517242030170531606591472519286466790218560082596field" + "tpk": "5217821421448171708893898476226701758596729091010258404483505858728239780855group", + "tcm": "7477948332296478291944103795851273885338086131076721669399703348779096173412field", + "scm": "7818509915162576913905185694457516827956104433621406862264469146739101113482field" } ], "global_state_root": "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu" diff --git a/tests/expectations/execution/various_storage_types_none.out b/tests/expectations/execution/various_storage_types_none.out index aac93b01c1d..bde55764c57 100644 --- a/tests/expectations/execution/various_storage_types_none.out +++ b/tests/expectations/execution/various_storage_types_none.out @@ -422,20 +422,20 @@ status: accepted { "transitions": [ { - "id": "au170l6h4eenevpr3wum4lyk0qc39r90798dzwvttv32qjg3hmfzuzqn5kgce", + "id": "au1m7flgn8wsngrlddkm29qmqwuw0ck62usd9pt28nlutw8rhtfhy9s9qykqw", "program": "none_cases.aleo", "function": "integers_none_behavior", "inputs": [], "outputs": [ { "type": "future", - "id": "2095925180209713199495646627871384963764668832869742961291273749504346404654field", + "id": "777140877736849336161834954955944431817460366791053646315305040572413846448field", "value": "{\n program_id: none_cases.aleo,\n function_name: integers_none_behavior,\n arguments: []\n}" } ], - "tpk": "3022496656438010606968518214019228071427476203961393223135090775396129070309group", - "tcm": "6897127509070331044629860789328838195880936054281624579606448337161162518852field", - "scm": "5491189826715932332587847370196368720755861859061735515466113261967513141909field" + "tpk": "3199748747054738424626228063868074142808562583917491536865402288373943719377group", + "tcm": "2291111946200208727321253641881848352116006664469168962589868922316813131029field", + "scm": "7229553028399955024499868826362858708037084464951260991059469723893306049124field" } ], "global_state_root": "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu" @@ -446,20 +446,20 @@ status: accepted { "transitions": [ { - "id": "au15kfq7vj8u0zqdtm83xa8vv3cyz3na06umcq3n4kja05e7a6em5yq7fn80x", + "id": "au1fweu2lmfymy302a5jfh6dn8rctyfvngzw5tpz22mvhtywr3pksxq4prcfw", "program": "none_cases.aleo", "function": "primitives_none_behavior", "inputs": [], "outputs": [ { "type": "future", - "id": "5582829250765808484241595856692025400340333708442889680168755895362349204239field", + "id": "476827761100120451749365719771456125873343643848275640472238747560530305078field", "value": "{\n program_id: none_cases.aleo,\n function_name: primitives_none_behavior,\n arguments: []\n}" } ], - "tpk": "2155034156727651206074116048643588463778004912504988473270076337820239418207group", - "tcm": "6350540617327696830470599027248589013733470041979954484947050399059264480710field", - "scm": "3372032122973594591727892709128906252574615350799264120330099841463916088264field" + "tpk": "4656898625527795514624494288663572682356952648348904380823190828466577248943group", + "tcm": "3173650553138867710917004191403800576576961752684431388092450122926842548789field", + "scm": "7798921162301890860244765973418314696574774180995932795683389522043857064830field" } ], "global_state_root": "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu" @@ -470,20 +470,20 @@ status: accepted { "transitions": [ { - "id": "au12exmr49dgp0xkhk97jd2lzv9xdy4x0wjnnczrql9nxue2s5u6s9qt5fss7", + "id": "au155v22emeum49puk7x2ttanvl6lkcy6tgqw8n3y3r9elzdxzrgqys9etn7g", "program": "none_cases.aleo", "function": "arrays_none_behavior", "inputs": [], "outputs": [ { "type": "future", - "id": "280149512319723689724650966945797574032635481689321317673734577276810675599field", + "id": "3140234408512591659099594191137360727684643873042545150587002448399106688683field", "value": "{\n program_id: none_cases.aleo,\n function_name: arrays_none_behavior,\n arguments: []\n}" } ], - "tpk": "5073244271415306064125555740132134661216396977964767543720035033561550784564group", - "tcm": "318482728305363911386859487676231420743770915997538397855481329038956355182field", - "scm": "574103690465821056452634005051779548148456098746680808654261791650266216218field" + "tpk": "2964143739387655529839618036125117249245988843315165088212607773680129646859group", + "tcm": "252898529996989819337844202066710871751236220948673013539510062341234424697field", + "scm": "3035122352356931348216000001150465304471261729781666002702012780471535972111field" } ], "global_state_root": "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu" @@ -494,20 +494,20 @@ status: accepted { "transitions": [ { - "id": "au1frq8cv7czxv7ffxn0wcjznu72m4plq725ez34az5huf3jf605ypqnzey96", + "id": "au1y4k4n338aa667d9mjgv3r42wx3z2mc7sv2wqqvh42k8v7agf6crsxdp76h", "program": "none_cases.aleo", "function": "structs_none_behavior", "inputs": [], "outputs": [ { "type": "future", - "id": "87152403193078256329041679229170422483157504242096974853115049540277413888field", + "id": "5191305053956011644245779097688781668970588425937173823221062870822530895780field", "value": "{\n program_id: none_cases.aleo,\n function_name: structs_none_behavior,\n arguments: []\n}" } ], - "tpk": "1249915714726010672792200308396249875175834321636139985880080092473194039920group", - "tcm": "1075070365254965945112368905077894413882823151698799912373458502951727861631field", - "scm": "443863523513593660781084341860645210743569086153608303992090421506432823517field" + "tpk": "2157184221807861305215769253263458584295561151282589287268823922208538874046group", + "tcm": "5702026260002725683831570138974316242135761176422160299509667633177577574318field", + "scm": "5016635263784801844758937148087361646447841439186370894563878180068934636624field" } ], "global_state_root": "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu" diff --git a/tests/expectations/parser-expression/expression/cast.out b/tests/expectations/parser-expression/expression/cast.out index e0b00bc5b84..bfe48bc610c 100644 --- a/tests/expectations/parser-expression/expression/cast.out +++ b/tests/expectations/parser-expression/expression/cast.out @@ -114,6 +114,7 @@ "id": 4 } ], + "program": null, "span": { "lo": 22, "hi": 38 diff --git a/tests/expectations/parser-expression/expression/struct_expr.out b/tests/expectations/parser-expression/expression/struct_expr.out index cae63a38fc9..d5ec77dc4cd 100644 --- a/tests/expectations/parser-expression/expression/struct_expr.out +++ b/tests/expectations/parser-expression/expression/struct_expr.out @@ -75,6 +75,7 @@ "id": 6 } ], + "program": null, "span": { "lo": 0, "hi": 19 @@ -197,6 +198,7 @@ "id": 6 } ], + "program": null, "span": { "lo": 20, "hi": 50 diff --git a/tests/expectations/parser-statement/statement/assert.out b/tests/expectations/parser-statement/statement/assert.out index 5d8e73fce16..017e33efedf 100644 --- a/tests/expectations/parser-statement/statement/assert.out +++ b/tests/expectations/parser-statement/statement/assert.out @@ -114,6 +114,7 @@ "id": 5 } ], + "program": null, "span": { "lo": 27, "hi": 39 @@ -179,6 +180,7 @@ "id": 12 } ], + "program": null, "span": { "lo": 41, "hi": 53 diff --git a/tests/expectations/parser-statement/statement/exported_type_fail.out b/tests/expectations/parser-statement/statement/exported_type_fail.out index eda0b41a35f..e60562dab2f 100644 --- a/tests/expectations/parser-statement/statement/exported_type_fail.out +++ b/tests/expectations/parser-statement/statement/exported_type_fail.out @@ -1,8 +1,114 @@ -Error [EPAR0370040]: Cannot create an external record. Records can only be created in the program that they are defined in. - --> test_0:1:8 - | - 1 | return credits.aleo/credits { owner: arg1, amount: arg2}; - | ^^^^^^^^^^^^^^^^^^^^ +{ + "Return": { + "expression": { + "Struct": { + "path": { + "qualifier": [], + "identifier": { + "name": "credits", + "span": { + "lo": 19, + "hi": 27 + }, + "id": 10 + }, + "is_absolute": false, + "absolute_path": null, + "span": { + "lo": 19, + "hi": 27 + }, + "id": 11 + }, + "const_arguments": [], + "members": [ + { + "identifier": { + "name": "owner", + "span": { + "lo": 30, + "hi": 35 + }, + "id": 4 + }, + "expression": { + "Path": { + "qualifier": [], + "identifier": { + "name": "arg1", + "span": { + "lo": 37, + "hi": 41 + }, + "id": 3 + }, + "is_absolute": false, + "absolute_path": null, + "span": { + "lo": 37, + "hi": 41 + }, + "id": 2 + } + }, + "span": { + "lo": 30, + "hi": 41 + }, + "id": 5 + }, + { + "identifier": { + "name": "amount", + "span": { + "lo": 43, + "hi": 49 + }, + "id": 8 + }, + "expression": { + "Path": { + "qualifier": [], + "identifier": { + "name": "arg2", + "span": { + "lo": 51, + "hi": 55 + }, + "id": 7 + }, + "is_absolute": false, + "absolute_path": null, + "span": { + "lo": 51, + "hi": 55 + }, + "id": 6 + } + }, + "span": { + "lo": 43, + "hi": 55 + }, + "id": 9 + } + ], + "program": "credits", + "span": { + "lo": 7, + "hi": 56 + }, + "id": 1 + } + }, + "span": { + "lo": 0, + "hi": 57 + }, + "id": 0 + } +} + Error [EPAR0370005]: expected '=', '::', '?' -- found '.' --> test_1:1:13 | diff --git a/tests/expectations/passes/option_lowering/b28961.out b/tests/expectations/passes/option_lowering/b28961.out index d8a37f8c7c4..5055783007a 100644 --- a/tests/expectations/passes/option_lowering/b28961.out +++ b/tests/expectations/passes/option_lowering/b28961.out @@ -4,12 +4,12 @@ program test.aleo { val: u32, } transition assign_optional_from_unwrap() -> u32 { - let a: ::"u32?" = ::"u32?" { is_some: true, val: 5u32 }; + let a: test.aleo/::"u32?" = test.aleo/::"u32?" { is_some: true, val: 5u32 }; assert(a.is_some); - let b: ::"u32?" = ::"u32?" { is_some: true, val: a.val }; + let b: test.aleo/::"u32?" = test.aleo/::"u32?" { is_some: true, val: a.val }; assert(a.is_some); - let c: ::"u32?" = ::"u32?" { is_some: true, val: a.val + 7u32 }; - let d: ::"u32?" = ::"u32?" { is_some: true, val: a.is_some ? a.val : 0 }; + let c: test.aleo/::"u32?" = test.aleo/::"u32?" { is_some: true, val: a.val + 7u32 }; + let d: test.aleo/::"u32?" = test.aleo/::"u32?" { is_some: true, val: a.is_some ? a.val : 0 }; assert(b.is_some); assert(c.is_some); assert(d.is_some); diff --git a/tests/expectations/passes/option_lowering/option_none.out b/tests/expectations/passes/option_lowering/option_none.out index 77ccbff162a..ea53d285860 100644 --- a/tests/expectations/passes/option_lowering/option_none.out +++ b/tests/expectations/passes/option_lowering/option_none.out @@ -4,7 +4,7 @@ program test.aleo { val: u32, } transition main(flag: bool) -> u32 { - let result: ::"u32?" = ::"u32?" { is_some: false, val: 0u32 }; + let result: test.aleo/::"u32?" = test.aleo/::"u32?" { is_some: false, val: 0u32 }; assert(result.is_some); return result.val; } diff --git a/tests/expectations/passes/option_lowering/option_some.out b/tests/expectations/passes/option_lowering/option_some.out index fc9bbedd5a7..8fcb6cc2975 100644 --- a/tests/expectations/passes/option_lowering/option_some.out +++ b/tests/expectations/passes/option_lowering/option_some.out @@ -4,7 +4,7 @@ program test.aleo { val: u32, } transition main(x: u32, flag: bool) -> u32 { - let result: ::"u32?" = ::"u32?" { is_some: true, val: 5u32 }; + let result: test.aleo/::"u32?" = test.aleo/::"u32?" { is_some: true, val: 5u32 }; assert(result.is_some); return result.val; } diff --git a/tests/expectations/passes/storage_lowering/aggregates.out b/tests/expectations/passes/storage_lowering/aggregates.out index 674d1133fff..304bb0166bc 100644 --- a/tests/expectations/passes/storage_lowering/aggregates.out +++ b/tests/expectations/passes/storage_lowering/aggregates.out @@ -10,17 +10,17 @@ program complex.aleo { values: [u32; 3], active: bool, } - mapping point__: bool => Point; - mapping points__: bool => [Point; 2]; - mapping stats__: bool => Stats; + mapping point__: bool => complex.aleo/Point; + mapping points__: bool => [complex.aleo/Point; 2]; + mapping stats__: bool => complex.aleo/Stats; mapping arr_u32__: bool => [u32; 3]; mapping arr_bool__: bool => [bool; 2]; mapping nested__: bool => [[u8; 2]; 2]; async transition initialize() -> Future { return async { - _mapping_set(::point__, false, Point { x: 1field, y: 2field }); - _mapping_set(::points__, false, [Point { x: 10field, y: 20field }, Point { x: 30field, y: 40field }]); - _mapping_set(::stats__, false, Stats { values: [5u32, 10u32, 15u32], active: true }); + _mapping_set(::point__, false, complex.aleo/Point { x: 1field, y: 2field }); + _mapping_set(::points__, false, [complex.aleo/Point { x: 10field, y: 20field }, complex.aleo/Point { x: 30field, y: 40field }]); + _mapping_set(::stats__, false, complex.aleo/Stats { values: [5u32, 10u32, 15u32], active: true }); _mapping_set(::arr_u32__, false, [7u32, 8u32, 9u32]); _mapping_set(::arr_bool__, false, [true, false]); _mapping_set(::nested__, false, [[1u8, 2u8], [3u8, 4u8]]); @@ -38,13 +38,13 @@ program complex.aleo { } async transition check1() -> Future { return async { - let p = _optional_unwrap(_mapping_contains(::point__, false) ? _mapping_get_or_use(::point__, false, Point { x: 0field, y: 0field }) : none); + let p = _optional_unwrap(_mapping_contains(::point__, false) ? _mapping_get_or_use(::point__, false, complex.aleo/Point { x: 0field, y: 0field }) : none); assert(p.x == 1field); assert(p.y == 2field); - let ps = _optional_unwrap(_mapping_contains(::points__, false) ? _mapping_get_or_use(::points__, false, [Point { x: 0field, y: 0field }; 2]) : none); + let ps = _optional_unwrap(_mapping_contains(::points__, false) ? _mapping_get_or_use(::points__, false, [complex.aleo/Point { x: 0field, y: 0field }; 2]) : none); assert(ps[0].x == 10field); assert(ps[1].y == 40field); - let s = _optional_unwrap(_mapping_contains(::stats__, false) ? _mapping_get_or_use(::stats__, false, Stats { values: [0u32; 3], active: false }) : none); + let s = _optional_unwrap(_mapping_contains(::stats__, false) ? _mapping_get_or_use(::stats__, false, complex.aleo/Stats { values: [0u32; 3], active: false }) : none); assert(s.values[1] == 10u32); assert(s.active == true); let a = _optional_unwrap(_mapping_contains(::arr_u32__, false) ? _mapping_get_or_use(::arr_u32__, false, [0u32; 3]) : none); @@ -59,13 +59,13 @@ program complex.aleo { } async transition check2() -> Future { return async { - let p = _optional_unwrap_or(_mapping_contains(::point__, false) ? _mapping_get_or_use(::point__, false, Point { x: 0field, y: 0field }) : none, Point { x: 0field, y: 0field }); + let p = _optional_unwrap_or(_mapping_contains(::point__, false) ? _mapping_get_or_use(::point__, false, complex.aleo/Point { x: 0field, y: 0field }) : none, complex.aleo/Point { x: 0field, y: 0field }); assert(p.x == 1field); assert(p.y == 2field); - let ps = _optional_unwrap_or(_mapping_contains(::points__, false) ? _mapping_get_or_use(::points__, false, [Point { x: 0field, y: 0field }; 2]) : none, [Point { x: 0field, y: 0field }, Point { x: 0field, y: 0field }]); + let ps = _optional_unwrap_or(_mapping_contains(::points__, false) ? _mapping_get_or_use(::points__, false, [complex.aleo/Point { x: 0field, y: 0field }; 2]) : none, [complex.aleo/Point { x: 0field, y: 0field }, complex.aleo/Point { x: 0field, y: 0field }]); assert(ps[0].x == 10field); assert(ps[1].y == 40field); - let s = _optional_unwrap_or(_mapping_contains(::stats__, false) ? _mapping_get_or_use(::stats__, false, Stats { values: [0u32; 3], active: false }) : none, Stats { values: [0u32, 0u32, 0u32], active: false }); + let s = _optional_unwrap_or(_mapping_contains(::stats__, false) ? _mapping_get_or_use(::stats__, false, complex.aleo/Stats { values: [0u32; 3], active: false }) : none, complex.aleo/Stats { values: [0u32, 0u32, 0u32], active: false }); assert(s.values[2] == 15u32); assert(s.active == true); let a = _optional_unwrap_or(_mapping_contains(::arr_u32__, false) ? _mapping_get_or_use(::arr_u32__, false, [0u32; 3]) : none, [0u32, 0u32, 0u32]); diff --git a/tests/tests/cli/local_aleo_dependency/COMMANDS b/tests/tests/cli/local_aleo_dependency/COMMANDS new file mode 100755 index 00000000000..f4c781171ae --- /dev/null +++ b/tests/tests/cli/local_aleo_dependency/COMMANDS @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +LEO_BIN=${1} + +${LEO_BIN} build diff --git a/tests/tests/cli/local_aleo_dependency/contents/.gitignore b/tests/tests/cli/local_aleo_dependency/contents/.gitignore new file mode 100644 index 00000000000..f721f7f6f45 --- /dev/null +++ b/tests/tests/cli/local_aleo_dependency/contents/.gitignore @@ -0,0 +1,5 @@ +.env +*.avm +*.prover +*.verifier +outputs/ diff --git a/tests/tests/cli/local_aleo_dependency/contents/program.json b/tests/tests/cli/local_aleo_dependency/contents/program.json new file mode 100644 index 00000000000..c7783b0399b --- /dev/null +++ b/tests/tests/cli/local_aleo_dependency/contents/program.json @@ -0,0 +1,16 @@ +{ + "program": "complex.aleo", + "version": "0.1.0", + "description": "", + "license": "MIT", + "leo": "3.4.0", + "dependencies": [ + { + "name": "simple.aleo", + "location": "local", + "path": "simple.aleo", + "edition": null + } + ], + "dev_dependencies": null +} diff --git a/tests/tests/cli/local_aleo_dependency/contents/simple.aleo b/tests/tests/cli/local_aleo_dependency/contents/simple.aleo new file mode 100644 index 00000000000..55ec6ae3502 --- /dev/null +++ b/tests/tests/cli/local_aleo_dependency/contents/simple.aleo @@ -0,0 +1,10 @@ +program simple.aleo; + +function main: + input r0 as u32.public; + input r1 as u32.private; + add r0 r1 into r2; + output r2 as u32.private; + +constructor: + assert.eq edition 0u16; diff --git a/tests/tests/cli/local_aleo_dependency/contents/src/main.leo b/tests/tests/cli/local_aleo_dependency/contents/src/main.leo new file mode 100644 index 00000000000..15a4d510b68 --- /dev/null +++ b/tests/tests/cli/local_aleo_dependency/contents/src/main.leo @@ -0,0 +1,11 @@ +// The 'simple' program. +import simple.aleo; + +program complex.aleo { + @noupgrade + async constructor() {} + + transition main(public a: u32, b: u32) -> u32 { + return simple.aleo/main(a, b); + } +} diff --git a/tests/tests/cli/multiple_leo_deps/COMMANDS b/tests/tests/cli/multiple_leo_deps/COMMANDS new file mode 100755 index 00000000000..47525daf034 --- /dev/null +++ b/tests/tests/cli/multiple_leo_deps/COMMANDS @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +LEO_BIN=${1} + +cd parent || exit 1 +$LEO_BIN build diff --git a/tests/tests/cli/multiple_leo_deps/contents/.gitignore b/tests/tests/cli/multiple_leo_deps/contents/.gitignore new file mode 100644 index 00000000000..f721f7f6f45 --- /dev/null +++ b/tests/tests/cli/multiple_leo_deps/contents/.gitignore @@ -0,0 +1,5 @@ +.env +*.avm +*.prover +*.verifier +outputs/ diff --git a/tests/tests/cli/multiple_leo_deps/contents/child1/.gitignore b/tests/tests/cli/multiple_leo_deps/contents/child1/.gitignore new file mode 100644 index 00000000000..f721f7f6f45 --- /dev/null +++ b/tests/tests/cli/multiple_leo_deps/contents/child1/.gitignore @@ -0,0 +1,5 @@ +.env +*.avm +*.prover +*.verifier +outputs/ diff --git a/tests/tests/cli/multiple_leo_deps/contents/child1/program.json b/tests/tests/cli/multiple_leo_deps/contents/child1/program.json new file mode 100644 index 00000000000..12e4d1f1d21 --- /dev/null +++ b/tests/tests/cli/multiple_leo_deps/contents/child1/program.json @@ -0,0 +1,16 @@ +{ + "program": "child1.aleo", + "version": "0.1.0", + "description": "", + "license": "MIT", + "leo": "3.4.0", + "dependencies": [ + { + "name": "grandchild.aleo", + "location": "local", + "path": "../grandchild", + "edition": null + } + ], + "dev_dependencies": null +} diff --git a/tests/tests/cli/multiple_leo_deps/contents/child1/src/main.leo b/tests/tests/cli/multiple_leo_deps/contents/child1/src/main.leo new file mode 100644 index 00000000000..b8d28d6b751 --- /dev/null +++ b/tests/tests/cli/multiple_leo_deps/contents/child1/src/main.leo @@ -0,0 +1,15 @@ +import grandchild.aleo; + +program child1.aleo { + record R { + owner: address, + f1: field + } + + transition main(b: u32) -> u32 { + return grandchild.aleo/main(b); + } + + @noupgrade + async constructor() {} +} diff --git a/tests/tests/cli/multiple_leo_deps/contents/child2/.gitignore b/tests/tests/cli/multiple_leo_deps/contents/child2/.gitignore new file mode 100644 index 00000000000..f721f7f6f45 --- /dev/null +++ b/tests/tests/cli/multiple_leo_deps/contents/child2/.gitignore @@ -0,0 +1,5 @@ +.env +*.avm +*.prover +*.verifier +outputs/ diff --git a/tests/tests/cli/multiple_leo_deps/contents/child2/program.json b/tests/tests/cli/multiple_leo_deps/contents/child2/program.json new file mode 100644 index 00000000000..23b8b0a7dcd --- /dev/null +++ b/tests/tests/cli/multiple_leo_deps/contents/child2/program.json @@ -0,0 +1,16 @@ +{ + "program": "child2.aleo", + "version": "0.1.0", + "description": "", + "license": "MIT", + "leo": "3.4.0", + "dependencies": [ + { + "name": "grandchild.aleo", + "location": "local", + "path": "../grandchild", + "edition": null + } + ], + "dev_dependencies": null +} diff --git a/tests/tests/cli/multiple_leo_deps/contents/child2/src/main.leo b/tests/tests/cli/multiple_leo_deps/contents/child2/src/main.leo new file mode 100644 index 00000000000..e9dc101df1d --- /dev/null +++ b/tests/tests/cli/multiple_leo_deps/contents/child2/src/main.leo @@ -0,0 +1,15 @@ +import grandchild.aleo; + +program child2.aleo { + record R { + owner: address, + f2: field + } + + transition main(b: u32) -> u32 { + return grandchild.aleo/main(b); + } + + @noupgrade + async constructor() {} +} diff --git a/tests/tests/cli/multiple_leo_deps/contents/grandchild/.gitignore b/tests/tests/cli/multiple_leo_deps/contents/grandchild/.gitignore new file mode 100644 index 00000000000..f721f7f6f45 --- /dev/null +++ b/tests/tests/cli/multiple_leo_deps/contents/grandchild/.gitignore @@ -0,0 +1,5 @@ +.env +*.avm +*.prover +*.verifier +outputs/ diff --git a/tests/tests/cli/multiple_leo_deps/contents/grandchild/program.json b/tests/tests/cli/multiple_leo_deps/contents/grandchild/program.json new file mode 100644 index 00000000000..96cc4e28430 --- /dev/null +++ b/tests/tests/cli/multiple_leo_deps/contents/grandchild/program.json @@ -0,0 +1,8 @@ +{ + "program": "grandchild.aleo", + "version": "0.1.0", + "description": "", + "license": "MIT", + "dependencies": null, + "dev_dependencies": null +} diff --git a/tests/tests/cli/multiple_leo_deps/contents/grandchild/src/main.leo b/tests/tests/cli/multiple_leo_deps/contents/grandchild/src/main.leo new file mode 100644 index 00000000000..60c071375bb --- /dev/null +++ b/tests/tests/cli/multiple_leo_deps/contents/grandchild/src/main.leo @@ -0,0 +1,12 @@ +program grandchild.aleo { + record R { + owner: address, + } + + transition main(b: u32) -> u32 { + return b; + } + + @noupgrade + async constructor() {} +} diff --git a/tests/tests/cli/multiple_leo_deps/contents/parent/.gitignore b/tests/tests/cli/multiple_leo_deps/contents/parent/.gitignore new file mode 100644 index 00000000000..f721f7f6f45 --- /dev/null +++ b/tests/tests/cli/multiple_leo_deps/contents/parent/.gitignore @@ -0,0 +1,5 @@ +.env +*.avm +*.prover +*.verifier +outputs/ diff --git a/tests/tests/cli/multiple_leo_deps/contents/parent/program.json b/tests/tests/cli/multiple_leo_deps/contents/parent/program.json new file mode 100644 index 00000000000..af4b89b8bfb --- /dev/null +++ b/tests/tests/cli/multiple_leo_deps/contents/parent/program.json @@ -0,0 +1,22 @@ +{ + "program": "parent.aleo", + "version": "0.1.0", + "description": "", + "license": "MIT", + "leo": "3.4.0", + "dependencies": [ + { + "name": "child1.aleo", + "location": "local", + "path": "../child1", + "edition": null + }, + { + "name": "child2.aleo", + "location": "local", + "path": "../child2", + "edition": null + } + ], + "dev_dependencies": null +} diff --git a/tests/tests/cli/multiple_leo_deps/contents/parent/src/main.leo b/tests/tests/cli/multiple_leo_deps/contents/parent/src/main.leo new file mode 100644 index 00000000000..36b3bf7876a --- /dev/null +++ b/tests/tests/cli/multiple_leo_deps/contents/parent/src/main.leo @@ -0,0 +1,11 @@ +import child1.aleo; +import child2.aleo; + +program parent.aleo { + transition main(b: u32) -> u32 { + return child1.aleo/main(b) + child2.aleo/main(b); + } + + @noupgrade + async constructor() {} +} diff --git a/tests/tests/cli/program_name_mismatch/contents/program.json b/tests/tests/cli/program_name_mismatch/contents/program.json index a76eba3a335..e0d5a628f8f 100644 --- a/tests/tests/cli/program_name_mismatch/contents/program.json +++ b/tests/tests/cli/program_name_mismatch/contents/program.json @@ -4,8 +4,5 @@ "description": "", "license": "MIT", "dependencies": null, - "dev_dependencies": null, - "upgrade": { - "mode": "noupgrade" - } + "dev_dependencies": null } diff --git a/tests/tests/cli/test_deploy/contents/program.json b/tests/tests/cli/test_deploy/contents/program.json index f6b7caf5c08..93f916db11e 100644 --- a/tests/tests/cli/test_deploy/contents/program.json +++ b/tests/tests/cli/test_deploy/contents/program.json @@ -4,8 +4,5 @@ "description": "", "license": "MIT", "dependencies": null, - "dev_dependencies": null, - "upgrade": { - "mode": "noupgrade" - } + "dev_dependencies": null } diff --git a/tests/tests/cli/test_simple_build/contents/program.json b/tests/tests/cli/test_simple_build/contents/program.json index f6b7caf5c08..93f916db11e 100644 --- a/tests/tests/cli/test_simple_build/contents/program.json +++ b/tests/tests/cli/test_simple_build/contents/program.json @@ -4,8 +4,5 @@ "description": "", "license": "MIT", "dependencies": null, - "dev_dependencies": null, - "upgrade": { - "mode": "noupgrade" - } + "dev_dependencies": null } diff --git a/tests/tests/cli/test_simple_test/COMMANDS b/tests/tests/cli/test_simple_test/COMMANDS new file mode 100755 index 00000000000..2ee615b7deb --- /dev/null +++ b/tests/tests/cli/test_simple_test/COMMANDS @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +LEO_BIN=${1} + +${LEO_BIN} test diff --git a/tests/tests/cli/test_simple_test/contents/.gitignore b/tests/tests/cli/test_simple_test/contents/.gitignore new file mode 100644 index 00000000000..f721f7f6f45 --- /dev/null +++ b/tests/tests/cli/test_simple_test/contents/.gitignore @@ -0,0 +1,5 @@ +.env +*.avm +*.prover +*.verifier +outputs/ diff --git a/tests/tests/cli/test_simple_test/contents/program.json b/tests/tests/cli/test_simple_test/contents/program.json new file mode 100644 index 00000000000..93f916db11e --- /dev/null +++ b/tests/tests/cli/test_simple_test/contents/program.json @@ -0,0 +1,8 @@ +{ + "program": "some_sample_leo_program.aleo", + "version": "0.1.0", + "description": "", + "license": "MIT", + "dependencies": null, + "dev_dependencies": null +} diff --git a/tests/tests/cli/test_simple_test/contents/src/main.leo b/tests/tests/cli/test_simple_test/contents/src/main.leo new file mode 100644 index 00000000000..40acdafaa51 --- /dev/null +++ b/tests/tests/cli/test_simple_test/contents/src/main.leo @@ -0,0 +1,10 @@ +// The 'some_sample_leo_program' program. +program some_sample_leo_program.aleo { + transition main(public a: u32, b: u32) -> u32 { + let c: u32 = a + b; + return c; + } + + @noupgrade + async constructor() {} +} diff --git a/tests/tests/cli/test_simple_test/contents/tests/test_some_sample_leo_program.leo b/tests/tests/cli/test_simple_test/contents/tests/test_some_sample_leo_program.leo new file mode 100644 index 00000000000..00528592861 --- /dev/null +++ b/tests/tests/cli/test_simple_test/contents/tests/test_some_sample_leo_program.leo @@ -0,0 +1,19 @@ +// The 'test_some_sample_leo_program' test program. +import some_sample_leo_program.aleo; +program test_some_sample_leo_program.aleo { + @test + script test_it() { + let result: u32 = some_sample_leo_program.aleo/main(1u32, 2u32); + assert_eq(result, 3u32); + } + + @test + @should_fail + transition do_nothing() { + let result: u32 = some_sample_leo_program.aleo/main(2u32, 3u32); + assert_eq(result, 3u32); + } + + @noupgrade + async constructor() {} +} diff --git a/tests/tests/compiler/const_generics/external_struct_fail.leo b/tests/tests/compiler/const_generics/external_struct_fail.leo index 1ec9b6df616..e872403ef22 100644 --- a/tests/tests/compiler/const_generics/external_struct_fail.leo +++ b/tests/tests/compiler/const_generics/external_struct_fail.leo @@ -10,8 +10,8 @@ program child.aleo { import child.aleo; program parent.aleo { - transition main(c: Bar::[4]) { - let b = Bar::[3] { + transition main(c: child.aleo/Bar::[4]) { + let b = child.aleo/Bar::[3] { x: 0, }; } diff --git a/tests/tests/compiler/records/external_record_fail.leo b/tests/tests/compiler/records/external_record_fail.leo new file mode 100644 index 00000000000..583a6a556e9 --- /dev/null +++ b/tests/tests/compiler/records/external_record_fail.leo @@ -0,0 +1,18 @@ +program child1.aleo { + record R {owner: address} + transition main() { + } + + @noupgrade + async constructor() {} +} + +// --- Next Program --- // + +import child1.aleo; +program parent.aleo { + + transition main(addr: address) { + let r = child1.aleo/R { owner: addr }; + } +} diff --git a/tests/tests/compiler/structs/external_struct.leo b/tests/tests/compiler/structs/external_struct.leo index 5339bb74259..57ea32d7ff4 100644 --- a/tests/tests/compiler/structs/external_struct.leo +++ b/tests/tests/compiler/structs/external_struct.leo @@ -49,15 +49,15 @@ program parent.aleo { b: u32, } - transition create_wrapper() -> (Foo, child.aleo/Boo) { - let f: Foo = Foo {bar: [Bar {baz: [Baz {one: One {two: [Two {val1: 1u32, val2: 2u32}, Two {val1: 3u32, val2: 4u32}]}}, Baz {one: One {two: [Two {val1: 5u32, val2: 6u32}, Two {val1: 7u32, val2: 8u32}]}}]}]}; - let (f1, b1): (Foo, child.aleo/Boo) = child.aleo/create(); + transition create_wrapper() -> (child.aleo/Foo, child.aleo/Boo) { + let f: child.aleo/Foo = child.aleo/Foo {bar: [child.aleo/Bar {baz: [child.aleo/Baz {one: child.aleo/One {two: [child.aleo/Two {val1: 1u32, val2: 2u32}, child.aleo/Two {val1: 3u32, val2: 4u32}]}}, child.aleo/Baz {one: child.aleo/One {two: [child.aleo/Two {val1: 5u32, val2: 6u32}, child.aleo/Two {val1: 7u32, val2: 8u32}]}}]}]}; + let (f1, b1): (child.aleo/Foo, child.aleo/Boo) = child.aleo/create(); return child.aleo/create(); } - transition create_another_wrapper() -> (Foo, child.aleo/Boo, BooHoo, Woo) { - let f: Foo = Foo {bar: [Bar {baz: [Baz {one: One {two: [Two {val1: 1u32, val2: 2u32}, Two {val1: 3u32, val2: 4u32}]}}, Baz {one: One {two: [Two {val1: 5u32, val2: 6u32}, Two {val1: 7u32, val2: 8u32}]}}]}]}; - let (f1, b1): (Foo, child.aleo/Boo) = child.aleo/create(); + transition create_another_wrapper() -> (child.aleo/Foo, child.aleo/Boo, BooHoo, Woo) { + let f: child.aleo/Foo = child.aleo/Foo {bar: [child.aleo/Bar {baz: [child.aleo/Baz {one: child.aleo/One {two: [child.aleo/Two {val1: 1u32, val2: 2u32}, child.aleo/Two {val1: 3u32, val2: 4u32}]}}, child.aleo/Baz {one: child.aleo/One {two: [child.aleo/Two {val1: 5u32, val2: 6u32}, child.aleo/Two {val1: 7u32, val2: 8u32}]}}]}]}; + let (f1, b1): (child.aleo/Foo, child.aleo/Boo) = child.aleo/create(); return (f1, b1, BooHoo {owner: self.signer, val: 10u32, woo: Woo {a: 1u32, b: 2u32}}, Woo {a: 3u32, b: 4u32}); } @@ -73,8 +73,8 @@ program grandparent.aleo { return a; } - transition omega_wrapper() -> (Foo, child.aleo/Boo, parent.aleo/BooHoo, Woo) { - let (f, b, bh, w): (Foo, child.aleo/Boo, parent.aleo/BooHoo, Woo) = parent.aleo/create_another_wrapper(); + transition omega_wrapper() -> (child.aleo/Foo, child.aleo/Boo, parent.aleo/BooHoo, parent.aleo/Woo) { + let (f, b, bh, w): (child.aleo/Foo, child.aleo/Boo, parent.aleo/BooHoo, parent.aleo/Woo) = parent.aleo/create_another_wrapper(); return parent.aleo/create_another_wrapper(); } } diff --git a/tests/tests/compiler/structs/redefine_external_struct.leo b/tests/tests/compiler/structs/redefine_external_struct_fail.leo similarity index 100% rename from tests/tests/compiler/structs/redefine_external_struct.leo rename to tests/tests/compiler/structs/redefine_external_struct_fail.leo diff --git a/tests/tests/compiler/symbols/illegal_names_in_library_fail.leo b/tests/tests/compiler/symbols/illegal_names_in_library_fail.leo index 6d64a4d619a..7810b66bf89 100644 --- a/tests/tests/compiler/symbols/illegal_names_in_library_fail.leo +++ b/tests/tests/compiler/symbols/illegal_names_in_library_fail.leo @@ -22,7 +22,7 @@ program childaleo.aleo { // --- Next Program --- // -import child.aleo; +import childaleo.aleo; program foo.aleo { transition foo () {} diff --git a/tests/tests/execution/nested_external_struct.leo b/tests/tests/execution/nested_external_struct.leo new file mode 100644 index 00000000000..e17d503fa9e --- /dev/null +++ b/tests/tests/execution/nested_external_struct.leo @@ -0,0 +1,48 @@ +/* +seed = 123456789 +min_height = 16 + +[case] +program = "external_structs.aleo" +function = "main" +input = [] +*/ + +program child1.aleo { + struct Foo { + c: Boo, + } + struct Boo { + a: u32, + b: u32, + } + struct Coo { // Coo is only used in `external_structs.aleo`. Make sure it doesn't get removed from `child1`. + x: u32, + y: u32, + } + transition main_2(a: Foo) -> Boo { + return a.c; + } + + @noupgrade + async constructor() {} +} + +// --- Next Program --- // + +import child1.aleo; +program external_structs.aleo { + struct Foo { + c: child1.aleo/Foo // ensures we don't think this is a cyclical dependency + } + + transition main() -> (child1.aleo/Boo, child1.aleo/Foo, child1.aleo/Boo, Foo, child1.aleo/Coo) { + let bb: child1.aleo/Boo = child1.aleo/Boo {a:1u32, b:1u32}; + let ff = child1.aleo/Foo {c: bb}; + + return (bb, ff, child1.aleo/main_2(ff), Foo {c: ff}, child1.aleo/Coo { x: 0, y: 0}); + } + + @noupgrade + async constructor() {} +} diff --git a/tests/tests/execution/simple_external_struct.leo b/tests/tests/execution/simple_external_struct.leo new file mode 100644 index 00000000000..7625b39e3b7 --- /dev/null +++ b/tests/tests/execution/simple_external_struct.leo @@ -0,0 +1,81 @@ +/* +seed = 123456789 +[case] +program = "external_structs.aleo" +function = "main1" +input = ["{x:11u32, y:22u32}", "{x:33u32, y:44u32, z:55u32}", "{x:66u32, y:77u32, z:88u32, w:99u32}"] +[case] +program = "external_structs.aleo" +function = "main2" +input = [] +*/ + +program child1.aleo { + struct Foo { + x: u32, + y: u32, + z: u32, + } + + transition main1(foo: Foo) { } + + transition main2(x: u32, y: u32, z: u32) -> Foo { + return Foo { x, y, z }; + } + + @noupgrade + async constructor() {} +} + +// --- Next Program --- // + +program child2.aleo { + struct Foo { + x: u32, + y: u32, + z: u32, + w: u32, + } + + transition main1(foo: Foo) { } + + transition main2(x: u32, y: u32, z: u32, w: u32) -> Foo { + return Foo { x, y, z, w }; + } + + @noupgrade + async constructor() {} +} + +// --- Next Program --- // + +import child1.aleo; +import child2.aleo; + +program external_structs.aleo { + struct Foo { + x: u32, + y: u32, + } + + transition main1(local: Foo, + external1: child1.aleo/Foo, + external2: child2.aleo/Foo) { + assert_eq( + local.x + local.y + + external1.x + external1.y + external1.z + + external2.x + external2.y + external2.z + external2.w, + 11u32 + 22 + 33 + 44 + 55 + 66 + 77 + 88 + 99 + ); + } + + transition main2() -> public u32 { + let external1 = child1.aleo/main2(1, 2, 3); + let external2 = child2.aleo/main2(1, 2, 3, 4); + return external1.x + external1.y + external1.z + + external2.x + external2.y + external2.z + external2.w; + } + + @noupgrade + async constructor() {} +} diff --git a/utils/disassembler/src/lib.rs b/utils/disassembler/src/lib.rs index 3a0aa8071b8..876e606aad0 100644 --- a/utils/disassembler/src/lib.rs +++ b/utils/disassembler/src/lib.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -use leo_ast::{Composite, FunctionStub, Identifier, Mapping, ProgramId, Stub}; +use leo_ast::{AleoProgram, Composite, FunctionStub, Identifier, Mapping, ProgramId}; use leo_errors::UtilError; use leo_span::Symbol; @@ -25,9 +25,9 @@ use snarkvm::{ use std::{fmt, str::FromStr}; -pub fn disassemble(program: ProgramCore) -> Stub { +pub fn disassemble(program: ProgramCore) -> AleoProgram { let program_id = ProgramId::from(program.id()); - Stub { + AleoProgram { imports: program.imports().into_iter().map(|(id, _)| ProgramId::from(id)).collect(), stub_id: program_id, consts: Vec::new(), @@ -84,7 +84,7 @@ pub fn disassemble(program: ProgramCore) -> Stub { } } -pub fn disassemble_from_str(name: impl fmt::Display, program: &str) -> Result { +pub fn disassemble_from_str(name: impl fmt::Display, program: &str) -> Result { match Program::::from_str(program) { Ok(p) => Ok(disassemble(p)), Err(_) => Err(UtilError::snarkvm_parsing_error(name)),