diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00cb018d29a..a1f5a7fe54a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -162,11 +162,6 @@ jobs: with: command: install args: --debug --path ./forc - - name: Install Forc fmt - uses: actions-rs/cargo@v1 - with: - command: install - args: --debug --path ./forc-plugins/forc-fmt - name: Initialize test project run: forc init test-proj - name: Update project forc manifest to use local sway-lib-std @@ -191,21 +186,6 @@ jobs: profile: minimal toolchain: stable - uses: Swatinem/rust-cache@v1 - - name: Install Forc - uses: actions-rs/cargo@v1 - with: - command: install - args: --debug --path ./forc - - name: Install Forc fmt - uses: actions-rs/cargo@v1 - with: - command: install - args: --debug --path ./forc-plugins/forc-fmt - - name: Build Sway examples - uses: actions-rs/cargo@v1 - with: - command: run - args: --bin examples-checker build --all-examples - uses: actions-rs/cargo@v1 name: Cargo Build Workspace with: @@ -223,21 +203,6 @@ jobs: with: toolchain: stable - uses: Swatinem/rust-cache@v1 - - name: Install Forc - uses: actions-rs/cargo@v1 - with: - command: install - args: --debug --path ./forc - - name: Install Forc fmt - uses: actions-rs/cargo@v1 - with: - command: install - args: --debug --path ./forc-plugins/forc-fmt - - name: Build Sway examples - uses: actions-rs/cargo@v1 - with: - command: run - args: --bin examples-checker build --all-examples - name: Check Clippy Linter uses: actions-rs/cargo@v1 with: @@ -311,21 +276,6 @@ jobs: profile: minimal toolchain: stable - uses: Swatinem/rust-cache@v1 - - name: Install Forc - uses: actions-rs/cargo@v1 - with: - command: install - args: --debug --path ./forc - - name: Install Forc fmt - uses: actions-rs/cargo@v1 - with: - command: install - args: --debug --path ./forc-plugins/forc-fmt - - name: Build Sway examples - uses: actions-rs/cargo@v1 - with: - command: run - args: --locked --bin examples-checker build --all-examples - name: Build All Tests run: cd test/src/sdk-harness && bash build.sh --locked && cd ../../../ - name: Cargo Test sway-lib-std @@ -344,21 +294,6 @@ jobs: profile: minimal toolchain: stable - uses: Swatinem/rust-cache@v1 - - name: Install Forc - uses: actions-rs/cargo@v1 - with: - command: install - args: --debug --path ./forc - - name: Install Forc fmt - uses: actions-rs/cargo@v1 - with: - command: install - args: --debug --path ./forc-plugins/forc-fmt - - name: Build Sway examples - uses: actions-rs/cargo@v1 - with: - command: run - args: --bin examples-checker build --all-examples - name: Run tests uses: actions-rs/cargo@v1 with: @@ -376,21 +311,6 @@ jobs: toolchain: nightly default: true - uses: Swatinem/rust-cache@v1 - - name: Install Forc - uses: actions-rs/cargo@v1 - with: - command: install - args: --debug --path ./forc - - name: Install Forc fmt - uses: actions-rs/cargo@v1 - with: - command: install - args: --debug --path ./forc-plugins/forc-fmt - - name: Build Sway examples - uses: actions-rs/cargo@v1 - with: - command: run - args: --locked --bin examples-checker build --all-examples - name: Install cargo-udeps uses: actions-rs/cargo@v1 with: diff --git a/.gitignore b/.gitignore index 3facbca8035..d8e9a544778 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ # will have compiled files and executables **/*/target/ **/*/json_abi_output.json +**/*/json_storage_slots_output.json target # These are backup files generated by rustfmt diff --git a/Cargo.lock b/Cargo.lock index a1b1671c2c4..a15da9a2f34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,9 +96,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" +checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" [[package]] name = "arrayref" @@ -118,12 +118,6 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" -[[package]] -name = "assert_matches" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" - [[package]] name = "async-channel" version = "1.6.1" @@ -161,17 +155,16 @@ dependencies = [ [[package]] name = "async-global-executor" -version = "2.0.4" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c290043c9a95b05d45e952fb6383c67bcb61471f60cfa21e890dba6654234f43" +checksum = "fd8b508d585e01084059b60f06ade4cb7415cd2e4084b71dd1cb44e7d3fb9880" dependencies = [ "async-channel", "async-executor", "async-io", - "async-mutex", + "async-lock", "blocking", "futures-lite", - "num_cpus", "once_cell", ] @@ -193,9 +186,9 @@ dependencies = [ [[package]] name = "async-io" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b" +checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07" dependencies = [ "concurrent-queue", "futures-lite", @@ -219,15 +212,6 @@ dependencies = [ "event-listener", ] -[[package]] -name = "async-mutex" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" -dependencies = [ - "event-listener", -] - [[package]] name = "async-std" version = "1.11.0" @@ -276,9 +260,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.53" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" +checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" dependencies = [ "proc-macro2", "quote", @@ -331,9 +315,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base-x" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" +checksum = "dc19a4937b4fbd3fe3379793130e42060d10627a360f2127802b10b87e7baf74" [[package]] name = "base64" @@ -349,9 +333,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "beef" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bed554bd50246729a1ec158d08aa3235d1b69d94ad120ebe187e28894787e736" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" [[package]] name = "bitflags" @@ -469,9 +453,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.9.1" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" +checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" [[package]] name = "byte-tools" @@ -528,7 +512,7 @@ dependencies = [ "num-integer", "num-traits", "serde", - "time 0.1.43", + "time 0.1.44", "winapi", ] @@ -558,16 +542,16 @@ dependencies = [ [[package]] name = "clap" -version = "3.1.8" +version = "3.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c" +checksum = "d53da17d37dba964b9b3ecb5c5a1f193a2762c700e6829201e645b9381c99dc7" dependencies = [ "atty", "bitflags", "clap_derive", + "clap_lex", "indexmap", - "lazy_static", - "os_str_bytes", + "once_cell", "strsim 0.10.0", "termcolor", "textwrap 0.15.0", @@ -575,18 +559,18 @@ dependencies = [ [[package]] name = "clap_complete" -version = "3.1.1" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df6f3613c0a3cddfd78b41b10203eb322cb29b600cbdf808a7d3db95691b8e25" +checksum = "0f6ebaab5f25e4f0312dfa07cb30a755204b96e6531457c2cfdecfdf5f2adf40" dependencies = [ - "clap 3.1.8", + "clap 3.2.5", ] [[package]] name = "clap_derive" -version = "3.1.7" +version = "3.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3aab4734e083b809aaf5794e14e756d1c798d2c69c7f7de7a09a2f5214993c1" +checksum = "c11d40217d16aee8508cc8e5fde8b4ff24639758608e5374e731b53f85749fb9" dependencies = [ "heck 0.4.0", "proc-macro-error", @@ -595,6 +579,15 @@ dependencies = [ "syn", ] +[[package]] +name = "clap_lex" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "cloudabi" version = "0.0.3" @@ -619,9 +612,9 @@ dependencies = [ [[package]] name = "combine" -version = "4.6.3" +version = "4.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b727aacc797f9fc28e355d21f34709ac4fc9adecfe470ad07b8f4464f53062" +checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948" dependencies = [ "bytes", "memchr", @@ -715,12 +708,12 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +checksum = "8ff1f980957787286a554052d03c7aee98d99cc32e09f6d45f0a814133c87978" dependencies = [ "cfg-if 1.0.0", - "lazy_static", + "once_cell", ] [[package]] @@ -877,13 +870,14 @@ dependencies = [ [[package]] name = "dashmap" -version = "5.2.0" +version = "5.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8858831f7781322e539ea39e72449c46b059638250c14344fec8d0aa6e539c" +checksum = "3495912c9c1ccf2e18976439f4443f3fee0fd61f424ff99fde6a66b15ecb448f" dependencies = [ "cfg-if 1.0.0", - "num_cpus", - "parking_lot 0.12.0", + "hashbrown 0.12.1", + "lock_api", + "parking_lot_core 0.9.3", ] [[package]] @@ -1038,7 +1032,7 @@ name = "examples-checker" version = "0.0.0" dependencies = [ "anyhow", - "clap 3.1.8", + "clap 3.2.5", ] [[package]] @@ -1077,11 +1071,22 @@ dependencies = [ "thiserror", ] +[[package]] +name = "filedescriptor" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7199d965852c3bac31f779ef99cbb4537f80e952e2d6aa0ffeb30cce00f4f46e" +dependencies = [ + "libc", + "thiserror", + "winapi", +] + [[package]] name = "filetime" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" +checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c" dependencies = [ "cfg-if 1.0.0", "libc", @@ -1103,11 +1108,11 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "forc" -version = "0.16.1" +version = "0.16.2" dependencies = [ "annotate-snippets", "anyhow", - "clap 3.1.8", + "clap 3.2.5", "clap_complete", "forc-pkg", "forc-util", @@ -1137,10 +1142,10 @@ dependencies = [ [[package]] name = "forc-explore" -version = "0.16.1" +version = "0.16.2" dependencies = [ "anyhow", - "clap 3.1.8", + "clap 3.2.5", "forc-util", "reqwest", "serde", @@ -1152,10 +1157,10 @@ dependencies = [ [[package]] name = "forc-fmt" -version = "0.16.1" +version = "0.16.2" dependencies = [ "anyhow", - "clap 3.1.8", + "clap 3.2.5", "forc-util", "prettydiff", "sway-core", @@ -1167,10 +1172,10 @@ dependencies = [ [[package]] name = "forc-fmt-v2" -version = "0.16.1" +version = "0.16.2" dependencies = [ "anyhow", - "clap 3.1.8", + "clap 3.2.5", "forc-util", "prettydiff", "sway-core", @@ -1182,31 +1187,32 @@ dependencies = [ [[package]] name = "forc-gm" -version = "0.16.1" +version = "0.16.2" dependencies = [ - "clap 3.1.8", + "clap 3.2.5", ] [[package]] name = "forc-lsp" -version = "0.16.1" +version = "0.16.2" dependencies = [ "anyhow", - "clap 3.1.8", + "clap 3.2.5", "sway-lsp", "tokio", ] [[package]] name = "forc-pkg" -version = "0.16.1" +version = "0.16.2" dependencies = [ "anyhow", "forc-util", + "fuel-tx", "fuels-types", "git2", "petgraph", - "semver 1.0.7", + "semver 1.0.10", "serde", "serde_ignored", "sway-core", @@ -1219,7 +1225,7 @@ dependencies = [ [[package]] name = "forc-util" -version = "0.16.1" +version = "0.16.2" dependencies = [ "annotate-snippets", "anyhow", @@ -1257,9 +1263,9 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "fuel-asm" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaa9ef12093772c88ddee15a214826527ad09fead79105f352d15ab4f271d5b5" +checksum = "16628e172a06a411c57972c55a67404f1684118be3bbca79148bf5a00fff48f0" dependencies = [ "fuel-types", "serde", @@ -1282,13 +1288,13 @@ dependencies = [ [[package]] name = "fuel-gql-client" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c0fee7db300cc83ba25d492ecef577730bf0857c51f383c10d26ef59037be9f" +checksum = "2185998563c770e718e9537b790d3cf2772d04f55a87e939f2aa6da87ab2443c" dependencies = [ "anyhow", "chrono", - "clap 3.1.8", + "clap 3.2.5", "cynic", "derive_more", "fuel-tx", @@ -1347,9 +1353,9 @@ checksum = "34b9161e86d434a93088409530a4f71f42e074b3bbcbb7a27bfe666583f92fd7" [[package]] name = "fuel-tx" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f76a720b4e32c6a51f39f609a472b9dcae35bc20a4ce9569601f3c24718b3ae" +checksum = "43dbfcd95f2b6fb19e6272832b5d6ecf14f8283c3329994baa38a4faed65d437" dependencies = [ "fuel-asm", "fuel-crypto", @@ -1372,9 +1378,9 @@ dependencies = [ [[package]] name = "fuel-vm" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a70dffe9faeb81f9d9d202eb26af4742980f884fa3109c169819ac6b2c51d3" +checksum = "e5700e457c2c6139a3b671014fa0a3bd57afb74de56abb118487b8d56ed21862" dependencies = [ "fuel-asm", "fuel-crypto", @@ -1507,6 +1513,16 @@ dependencies = [ "slab", ] +[[package]] +name = "gag" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a713bee13966e9fbffdf7193af71d54a6b35a0bb34997cd6c9519ebeb5005972" +dependencies = [ + "filedescriptor", + "tempfile", +] + [[package]] name = "generational-arena" version = "0.2.8" @@ -1535,15 +1551,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "getopts" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" -dependencies = [ - "unicode-width", -] - [[package]] name = "getrandom" version = "0.1.16" @@ -1557,13 +1564,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -1578,9 +1585,9 @@ dependencies = [ [[package]] name = "git2" -version = "0.14.2" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3826a6e0e2215d7a41c2bfc7c9244123969273f3476b939a226aac0ab56e9e3c" +checksum = "d0155506aab710a86160ddb504a480d2964d7ab5b9e62419be69e0032bc5931c" dependencies = [ "bitflags", "libc", @@ -1599,9 +1606,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "gloo-timers" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d12a7f4e95cfe710f1d624fb1210b7d961a5fb05c4fd942f4feab06e61f590e" +checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" dependencies = [ "futures-channel", "futures-core", @@ -1634,22 +1641,22 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.7.1", + "tokio-util 0.7.3", "tracing", ] [[package]] name = "handlebars" -version = "4.2.2" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d6a30320f094710245150395bc763ad23128d6a1ebbad7594dc4164b62c56b" +checksum = "b66d0c1b6e3abfd1e72818798925e16e02ed77e1b47f6c25a95a23b377ee4299" dependencies = [ "log", "pest", "pest_derive", - "quick-error 2.0.1", "serde", "serde_json", + "thiserror", ] [[package]] @@ -1658,6 +1665,12 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +[[package]] +name = "hashbrown" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" + [[package]] name = "headers" version = "0.3.7" @@ -1735,20 +1748,20 @@ dependencies = [ [[package]] name = "http" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", - "itoa 1.0.1", + "itoa 1.0.2", ] [[package]] name = "http-body" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", @@ -1757,9 +1770,9 @@ dependencies = [ [[package]] name = "http-client" -version = "6.5.1" +version = "6.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea880b03c18a7e981d7fb3608b8904a98425d53c440758fcebf7d934aa56547c" +checksum = "e023af341b797ce2c039f7c6e1d347b68d0f7fd0bc7ac234fe69cfadcca1f89a" dependencies = [ "async-h1", "async-std", @@ -1798,9 +1811,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" [[package]] name = "httpdate" @@ -1814,14 +1827,14 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" dependencies = [ - "quick-error 1.2.3", + "quick-error", ] [[package]] name = "hyper" -version = "0.14.18" +version = "0.14.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2" +checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" dependencies = [ "bytes", "futures-channel", @@ -1832,7 +1845,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.1", + "itoa 1.0.2", "pin-project-lite", "socket2", "tokio", @@ -1849,7 +1862,7 @@ checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" dependencies = [ "http", "hyper", - "rustls 0.20.4", + "rustls 0.20.6", "tokio", "tokio-rustls", ] @@ -1873,12 +1886,12 @@ dependencies = [ [[package]] name = "im" -version = "15.0.0" +version = "15.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "111c1983f3c5bb72732df25cddacee9b546d08325fb584b5ebd38148be7b0246" +checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" dependencies = [ "bitmaps", - "rand_core 0.5.1", + "rand_core 0.6.3", "rand_xoshiro", "sized-chunks", "typenum", @@ -1887,12 +1900,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +checksum = "6c6392766afd7964e2531940894cffe4bd8d7d17dbc3c1c4857040fd4b33bdb3" dependencies = [ "autocfg 1.1.0", - "hashbrown", + "hashbrown 0.12.1", ] [[package]] @@ -1912,9 +1925,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e70ee094dc02fd9c13fdad4940090f22dbd6ac7c9e7094a46cf0232a50bc7c" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" [[package]] name = "itertools" @@ -1933,9 +1946,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" [[package]] name = "jobserver" @@ -1948,9 +1961,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.57" +version = "0.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" +checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" dependencies = [ "wasm-bindgen", ] @@ -1968,9 +1981,9 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" +checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838" [[package]] name = "kstring" @@ -2011,15 +2024,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.122" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "libgit2-sys" -version = "0.13.2+1.4.2" +version = "0.13.4+1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a42de9a51a5c12e00fc0e4ca6bc2ea43582fc6418488e8f615e905d886f258b" +checksum = "d0fa6563431ede25f5cc7f6d803c6afbc1c5d3ad3d4925d12c882bf2b526f5d1" dependencies = [ "cc", "libc", @@ -2045,9 +2058,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.5" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f35facd4a5673cb5a48822be2be1d4236c1c99cb4113cab7061ac720d5bf859" +checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" dependencies = [ "cc", "libc", @@ -2067,9 +2080,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if 1.0.0", "value-bag", @@ -2077,18 +2090,18 @@ dependencies = [ [[package]] name = "logos" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427e2abca5be13136da9afdbf874e6b34ad9001dd70f2b103b083a85daa7b345" +checksum = "bf8b031682c67a8e3d5446840f9573eb7fe26efe7ec8d195c9ac4c0647c502f1" dependencies = [ "logos-derive", ] [[package]] name = "logos-derive" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56a7d287fd2ac3f75b11f19a1c8a874a7d55744bd91f7a1b3e7cf87d4343c36d" +checksum = "a1d849148dbaf9661a6151d1ca82b13bb4c4c128146a88d05253b38d4e2f496c" dependencies = [ "beef", "fnv", @@ -2096,7 +2109,6 @@ dependencies = [ "quote", "regex-syntax", "syn", - "utf8-ranges", ] [[package]] @@ -2135,13 +2147,14 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "mdbook" -version = "0.4.15" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241f10687eb3b4e0634b3b4e423f97c5f1efbd69dc9522e24a8b94583eeec3c6" +checksum = "74612ae81a3e5ee509854049dfa4c7975ae033c06f5fc4735c7dfbe60ee2a39d" dependencies = [ "anyhow", "chrono", - "clap 2.34.0", + "clap 3.2.5", + "clap_complete", "env_logger", "handlebars", "lazy_static", @@ -2164,9 +2177,9 @@ name = "mdbook-forc-documenter" version = "0.0.0" dependencies = [ "anyhow", - "clap 3.1.8", + "clap 3.2.5", "mdbook", - "semver 1.0.7", + "semver 1.0.10", "serde", "serde_json", "toml", @@ -2174,9 +2187,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" @@ -2205,25 +2218,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9" +checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" dependencies = [ "libc", "log", - "miow", - "ntapi", "wasi 0.11.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", + "windows-sys", ] [[package]] @@ -2237,7 +2239,7 @@ dependencies = [ "log", "mime", "mime_guess", - "quick-error 1.2.3", + "quick-error", "rand 0.8.5", "safemem", "tempfile", @@ -2255,15 +2257,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "ntapi" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" -dependencies = [ - "winapi", -] - [[package]] name = "num-bigint" version = "0.4.3" @@ -2277,9 +2270,9 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg 1.1.0", "num-traits", @@ -2287,9 +2280,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg 1.1.0", ] @@ -2306,9 +2299,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.10.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" [[package]] name = "opaque-debug" @@ -2349,9 +2342,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.72" +version = "0.9.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" +checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1" dependencies = [ "autocfg 1.1.0", "cc", @@ -2363,12 +2356,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.0.0" +version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" -dependencies = [ - "memchr", -] +checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" [[package]] name = "owo-colors" @@ -2393,16 +2383,6 @@ dependencies = [ "parking_lot_core 0.8.5", ] -[[package]] -name = "parking_lot" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" -dependencies = [ - "lock_api", - "parking_lot_core 0.9.2", -] - [[package]] name = "parking_lot_core" version = "0.8.5" @@ -2419,9 +2399,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" dependencies = [ "cfg-if 1.0.0", "libc", @@ -2508,9 +2488,9 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" +checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143" dependencies = [ "fixedbitset", "indexmap", @@ -2518,6 +2498,50 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.0.10" @@ -2540,9 +2564,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "pin-utils" @@ -2643,11 +2667,11 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.37" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] @@ -2657,7 +2681,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34f197a544b0c9ab3ae46c359a7ec9cbbb5c7bf97054266fecb7ead794a181d6" dependencies = [ "bitflags", - "getopts", "memchr", "unicase", ] @@ -2668,12 +2691,6 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - [[package]] name = "quote" version = "1.0.18" @@ -2786,7 +2803,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "getrandom 0.2.6", + "getrandom 0.2.7", ] [[package]] @@ -2862,11 +2879,11 @@ dependencies = [ [[package]] name = "rand_xoshiro" -version = "0.4.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" dependencies = [ - "rand_core 0.5.1", + "rand_core 0.6.3", ] [[package]] @@ -2910,16 +2927,16 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.6", + "getrandom 0.2.7", "redox_syscall 0.2.13", "thiserror", ] [[package]] name = "regex" -version = "1.5.5" +version = "1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" dependencies = [ "aho-corasick", "memchr", @@ -2937,9 +2954,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" [[package]] name = "remove_dir_all" @@ -2952,9 +2969,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.10" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb" +checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" dependencies = [ "base64 0.13.0", "bytes", @@ -2973,13 +2990,14 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rustls 0.20.4", + "rustls 0.20.6", "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "tokio", "tokio-rustls", + "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", @@ -3005,9 +3023,9 @@ dependencies = [ [[package]] name = "ropey" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0dd9b26e2a102b33d400b7b7d196c81a4014eb96eda90b1c5b48d7215d9633" +checksum = "bbd22239fafefc42138ca5da064f3c17726a80d2379d817a3521240e78dd0064" dependencies = [ "smallvec", "str_indices", @@ -3020,7 +3038,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f77412a3d1f26af0c0783c23b3555a301b1a49805cba7bf9a7827a9e9e285f0" dependencies = [ "countme", - "hashbrown", + "hashbrown 0.11.2", "memoffset", "rustc-hash", "text-size", @@ -3059,7 +3077,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.7", + "semver 1.0.10", ] [[package]] @@ -3077,9 +3095,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.4" +version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fbfeb8d0ddb84706bc597a5574ab8912817c52a397f819e5b614e2265206921" +checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" dependencies = [ "log", "ring", @@ -3089,18 +3107,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "0.3.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360" +checksum = "e7522c9de787ff061458fe9a829dc790a3f5b22dc571694fc5883f448b94d9a9" dependencies = [ "base64 0.13.0", ] [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" [[package]] name = "safemem" @@ -3205,9 +3223,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.7" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4" +checksum = "a41d061efea015927ac527063765e73601444cdc344ba855bc7bd44578b25e1c" dependencies = [ "serde", ] @@ -3220,18 +3238,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.136" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.136" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" dependencies = [ "proc-macro2", "quote", @@ -3240,20 +3258,20 @@ dependencies = [ [[package]] name = "serde_ignored" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c2c7d39d14f2f2ea82239de71594782f186fd03501ac81f0ce08e674819ff2f" +checksum = "1940036ca2411651a40012009d062087dfe62817b2191a03750fb569e11fa633" dependencies = [ "serde", ] [[package]] name = "serde_json" -version = "1.0.79" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" dependencies = [ - "itoa 1.0.1", + "itoa 1.0.2", "ryu", "serde", ] @@ -3271,9 +3289,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98d0516900518c29efa217c298fa1f4e6c6ffc85ae29fd7f4ee48f176e1a9ed5" +checksum = "a2ad84e47328a31223de7fed7a4f5087f2d6ddfe586cf3ca25b7a165bc0a5aed" dependencies = [ "proc-macro2", "quote", @@ -3287,7 +3305,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.1", + "itoa 1.0.2", "ryu", "serde", ] @@ -3422,6 +3440,12 @@ dependencies = [ "event-listener", ] +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + [[package]] name = "sized-chunks" version = "0.6.5" @@ -3526,9 +3550,9 @@ checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" [[package]] name = "str_indices" -version = "0.3.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfad63a1b47951101cd667a85b2959a62910cf03f814fff25df89c460b873f8" +checksum = "9d9199fa80c817e074620be84374a520062ebac833f358d74b37060ce4a0f2c0" [[package]] name = "strsim" @@ -3600,7 +3624,7 @@ dependencies = [ "async-trait", "cfg-if 1.0.0", "futures-util", - "getrandom 0.2.6", + "getrandom 0.2.7", "http-client", "http-types", "log", @@ -3614,14 +3638,15 @@ dependencies = [ [[package]] name = "sway-core" -version = "0.16.1" +version = "0.16.2" dependencies = [ - "clap 3.1.8", + "clap 3.2.5", "derivative", "dirs 3.0.2", "either", "fuel-asm", "fuel-crypto", + "fuel-tx", "fuel-types", "fuel-vm", "fuels-types", @@ -3646,7 +3671,7 @@ dependencies = [ [[package]] name = "sway-fmt" -version = "0.16.1" +version = "0.16.2" dependencies = [ "ropey", "sway-core", @@ -3655,7 +3680,7 @@ dependencies = [ [[package]] name = "sway-fmt-v2" -version = "0.16.1" +version = "0.16.2" dependencies = [ "anyhow", "forc-util", @@ -3670,7 +3695,7 @@ dependencies = [ [[package]] name = "sway-ir" -version = "0.16.1" +version = "0.16.2" dependencies = [ "filecheck", "generational-arena", @@ -3681,10 +3706,12 @@ dependencies = [ [[package]] name = "sway-lsp" -version = "0.16.1" +version = "0.16.2" dependencies = [ "async-trait", - "dashmap 4.0.2", + "dashmap 5.3.4", + "forc", + "forc-pkg", "forc-util", "futures", "ropey", @@ -3701,11 +3728,12 @@ dependencies = [ [[package]] name = "sway-parse" -version = "0.16.1" +version = "0.16.2" dependencies = [ "extension-trait", "num-bigint", "num-traits", + "phf", "sway-types", "thiserror", "unicode-xid", @@ -3713,7 +3741,7 @@ dependencies = [ [[package]] name = "sway-types" -version = "0.16.1" +version = "0.16.2" dependencies = [ "fuel-asm", "fuel-crypto", @@ -3724,17 +3752,17 @@ dependencies = [ [[package]] name = "sway-utils" -version = "0.16.1" +version = "0.16.2" [[package]] name = "syn" -version = "1.0.91" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -3748,7 +3776,7 @@ dependencies = [ "logos", "regex", "rowan", - "semver 1.0.7", + "semver 1.0.10", "smallvec", "toml", "wasm-bindgen", @@ -3815,16 +3843,19 @@ name = "test" version = "0.0.0" dependencies = [ "anyhow", - "assert_matches", + "filecheck", "forc", + "forc-pkg", "forc-util", "fuel-asm", "fuel-tx", "fuel-vm", + "gag", "rand 0.8.5", "regex", "serde_json", "tokio", + "toml", "tracing", ] @@ -3866,18 +3897,18 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ "proc-macro2", "quote", @@ -3895,11 +3926,12 @@ dependencies = [ [[package]] name = "time" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", + "wasi 0.10.0+wasi-snapshot-preview1", "winapi", ] @@ -3943,9 +3975,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] @@ -3958,9 +3990,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.17.0" +version = "1.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" +checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" dependencies = [ "bytes", "libc", @@ -3977,9 +4009,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" dependencies = [ "proc-macro2", "quote", @@ -3988,20 +4020,20 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.23.3" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4151fda0cf2798550ad0b34bcfc9b9dcc2a9d2471c895c68f3a8818e54f2389e" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls 0.20.4", + "rustls 0.20.6", "tokio", "webpki 0.22.0", ] [[package]] name = "tokio-stream" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" +checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" dependencies = [ "futures-core", "pin-project-lite", @@ -4023,9 +4055,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.6.9" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ "bytes", "futures-core", @@ -4037,9 +4069,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764" +checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" dependencies = [ "bytes", "futures-core", @@ -4051,9 +4083,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ "serde", ] @@ -4064,7 +4096,7 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "744e9ed5b352340aa47ce033716991b5589e23781acb97cad37d4ea70560f55b" dependencies = [ - "combine 4.6.3", + "combine 4.6.4", "indexmap", "itertools", "kstring", @@ -4078,9 +4110,9 @@ checksum = "aa7c7f42dea4b1b99439786f5633aeb9c14c1b53f75e282803c2ec2ad545873c" [[package]] name = "tower" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a89fd63ad6adf737582df5db40d286574513c69a11dac5214dc3b5603d6713e" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", @@ -4105,7 +4137,7 @@ dependencies = [ "async-trait", "auto_impl", "bytes", - "dashmap 5.2.0", + "dashmap 5.3.4", "futures", "httparse", "log", @@ -4114,7 +4146,7 @@ dependencies = [ "serde", "serde_json", "tokio", - "tokio-util 0.7.1", + "tokio-util 0.7.3", "tower", "tower-lsp-macros", ] @@ -4132,15 +4164,15 @@ dependencies = [ [[package]] name = "tower-service" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.33" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80b9fa4360528139bc96100c160b7ae879f5567f49f1782b0b02035b0358ebf3" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" dependencies = [ "cfg-if 1.0.0", "log", @@ -4151,9 +4183,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" dependencies = [ "proc-macro2", "quote", @@ -4162,11 +4194,11 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.24" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90442985ee2f57c9e1b548ee72ae842f4a9a20e3f417cc38dbc5dc684d9bb4ee" +checksum = "7709595b8878a4965ce5e87ebf880a7d39c9afc6837721b21a5a816a8117d921" dependencies = [ - "lazy_static", + "once_cell", "valuable", ] @@ -4281,9 +4313,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" [[package]] name = "unicode-normalization" @@ -4308,9 +4346,9 @@ checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] name = "unicode-xid" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" [[package]] name = "universal-hash" @@ -4356,12 +4394,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf8-ranges" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcfc827f90e53a02eaef5e535ee14266c1d569214c6aa70133a624d8a3164ba" - [[package]] name = "uwuify" version = "0.2.2" @@ -4370,7 +4402,7 @@ checksum = "3db6840b7adcfd2e866c79157cc890ecdbbc1f739607134039ae64eaa6c07e24" dependencies = [ "clap 2.34.0", "owo-colors", - "parking_lot 0.11.2", + "parking_lot", "thiserror", ] @@ -4382,9 +4414,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.0.0-alpha.8" +version = "1.0.0-alpha.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79923f7731dc61ebfba3633098bf3ac533bbd35ccd8c57e7088d9a5eebe0263f" +checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" dependencies = [ "ctor", "version_check", @@ -4466,7 +4498,7 @@ dependencies = [ "tokio", "tokio-stream", "tokio-tungstenite", - "tokio-util 0.6.9", + "tokio-util 0.6.10", "tower-service", "tracing", ] @@ -4479,9 +4511,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "wasi" @@ -4491,9 +4523,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" +checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" dependencies = [ "cfg-if 1.0.0", "serde", @@ -4503,9 +4535,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" +checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" dependencies = [ "bumpalo", "lazy_static", @@ -4518,9 +4550,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.30" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f741de44b75e14c35df886aff5f1eb73aa114fa5d4d00dcd37b5e01259bf3b2" +checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -4530,9 +4562,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" +checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4540,9 +4572,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" +checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" dependencies = [ "proc-macro2", "quote", @@ -4553,15 +4585,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" +checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" [[package]] name = "web-sys" -version = "0.3.57" +version = "0.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" +checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" dependencies = [ "js-sys", "wasm-bindgen", @@ -4657,9 +4689,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ "windows_aarch64_msvc", "windows_i686_gnu", @@ -4670,33 +4702,33 @@ dependencies = [ [[package]] name = "windows_aarch64_msvc" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] name = "windows_i686_gnu" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" [[package]] name = "windows_i686_msvc" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" [[package]] name = "windows_x86_64_gnu" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" [[package]] name = "windows_x86_64_msvc" -version = "0.34.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] name = "winreg" @@ -4709,9 +4741,9 @@ dependencies = [ [[package]] name = "xattr" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" dependencies = [ "libc", ] diff --git a/README.md b/README.md index 2e5016b1cef..3e80e4d2153 100644 --- a/README.md +++ b/README.md @@ -49,4 +49,4 @@ cargo run --bin forc -- --help We welcome contributions to Sway! -Please see the [Contributing To Sway](https://fuellabs.github.io/sway/latest/reference/contributing_to_sway.html) section of the Sway book for guidelines and instructions to help you get started. +Please see the [Contributing To Sway](https://fuellabs.github.io/sway/master/reference/contributing_to_sway.html) section of the Sway book for guidelines and instructions to help you get started. diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 1b563884e9b..7398a95bd9b 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -56,6 +56,7 @@ - [Commands](./forc/commands/index.md) - [forc addr2line](./forc/commands/forc_addr2line.md) - [forc build](./forc/commands/forc_build.md) + - [forc check](./forc/commands/forc_check.md) - [forc clean](./forc/commands/forc_clean.md) - [forc completions](./forc/commands/forc_completions.md) - [forc deploy](./forc/commands/forc_deploy.md) diff --git a/docs/src/basics/blockchain_types.md b/docs/src/basics/blockchain_types.md index 0a17b542d49..bfbb0d59e88 100644 --- a/docs/src/basics/blockchain_types.md +++ b/docs/src/basics/blockchain_types.md @@ -43,3 +43,35 @@ let my_number: b256 = 0x00000000000000000000000000000000000000000000000000000000 let my_contract_id: ContractId = ~ContractId::from(my_number); let forty_two: b256 = my_contract_id.into(); ``` + +## `Identity` Type + +The `Identity` type is an enum that allows for the handling of both `Address` and `ContractId` types. This is useful in cases where either type is accepted, e.g. receiving funds from an identified sender, but not caring if the sender is an address or a contract. + +An `Identity` is implemented as follows. + +```sway +{{#include ../../../sway-lib-std/src/identity.sw:docs_identity}} +``` + +Casting to an `Identity` must be done explicitly: + +```sway +{{#include ../../../examples/identity/src/main.sw:cast_to_identity}} +``` + +A `match` statement can be used to return to an `Address` or `ContractId` as well as handle cases in which their execution differs. + +```sway +{{#include ../../../examples/identity/src/main.sw:identity_to_contract_id}} +``` + +```sway +{{#include ../../../examples/identity/src/main.sw:different_executions}} +``` + +A common use case for `Identity` is for access control. The use of `Identity` uniquely allows both `ContractId` and `Address` to have access control inclusively. + +```sway +{{#include ../../../examples/identity/src/main.sw:access_control_with_identity}} +``` diff --git a/docs/src/forc/commands/forc_check.md b/docs/src/forc/commands/forc_check.md new file mode 100644 index 00000000000..49f24c6bc70 --- /dev/null +++ b/docs/src/forc/commands/forc_check.md @@ -0,0 +1 @@ +# forc check diff --git a/docs/src/forc/manifest_reference.md b/docs/src/forc/manifest_reference.md index 30d2120aefa..47e0f20716a 100644 --- a/docs/src/forc/manifest_reference.md +++ b/docs/src/forc/manifest_reference.md @@ -17,6 +17,8 @@ The `Forc.toml` (the _manifest_ file) is a compulsory file for each package and * [`[build-profiles]`](#the-build-profiles--section) - Defines the build profiles. +* [`[patch]`](#the-patch-section) - Defines the patches. + ## The `[project]` section An example `Forc.toml` is shown below. Under `[project]` the following fields are optional: @@ -99,3 +101,47 @@ Note that providing the corresponding cli options (like `--print-finalized-asm`) * print-intermediate-asm - false * print-ir - false * silent - false + +## The `[patch]` section + +The [patch] section of `Forc.toml` can be used to override dependencies with other copies. The example provided below patches source with master branch of the same repo. + +```toml +[project] +authors = ["user"] +entry = "main.sw" +organization = "Fuel_Labs" +license = "Apache-2.0" +name = "wallet_contract" + +[dependencies] + +[patch.'https://github.com/fuellabs/sway'] +std = { git = "https://github.com/fuellabs/sway", branch = "test" } +``` + +In the example above, `std` is patched with the `test` branch from `std` repo. You can also patch git dependencies with dependencies defined with a path. + +```toml +[patch.'https://github.com/fuellabs/sway'] +std = { path = "/path/to/local_std_version" } +``` + +Just like `std` or `core` you can also patch dependencies you declared with a git repo. + +```toml +[project] +authors = ["user"] +entry = "main.sw" +organization = "Fuel_Labs" +license = "Apache-2.0" +name = "wallet_contract" + +[dependencies] +foo = { git = "https://github.com/foo/foo", branch = "master" } + +[patch.'https://github.com/foo'] +foo = { git = "https://github.com/foo/foo", branch = "test" } +``` + +Note that each key after the `[patch]` is a URL of the source that is being patched. diff --git a/docs/src/introduction/installation.md b/docs/src/introduction/installation.md index 8debcf7c659..31605a41d78 100644 --- a/docs/src/introduction/installation.md +++ b/docs/src/introduction/installation.md @@ -12,24 +12,26 @@ Start by installing `fuelup` with: ```sh curl --proto '=https' --tlsv1.2 -sSf \ - https://fuellabs.github.io/fuelup/fuelup-init.sh | sh -s install + https://fuellabs.github.io/fuelup/fuelup-init.sh | sh ``` -You may need to add `~/.fuelup/bin` to your PATH. For Bash: +`fuelup-init` will ask for permission to add `~/.fuelup/bin` to your PATH. Otherwise, you can also pass `--no-modify-path` so that `fuelup-init` does not modify your PATH: ```sh -export PATH="${HOME}/.fuelup/bin/:${PATH}" +curl --proto '=https' --tlsv1.2 -sSf \ + https://fuellabs.github.io/fuelup/fuelup-init.sh | sh -s -- --no-modify-path ``` -Then run +Once `fuelup` is installed, `fuelup-init` automatically runs the command below ```sh -fuelup install +fuelup toolchain install latest ``` -to install the latest Sway toolchain. +to install the latest Sway toolchain. + +You can run the same command at a later time to update the toolchain. -> **Note**: A `curl: (22) The requested URL returned error: 404` error when running any of the steps above is most likely an indication that a new release was published but binaries were not yet uploaded by CI. See: . Simply re-try the commands after binaries are uploaded. ## Installing from Source diff --git a/docs/src/reference/known_issues_and_workarounds.md b/docs/src/reference/known_issues_and_workarounds.md index 47d0ad399ee..741c8159875 100644 --- a/docs/src/reference/known_issues_and_workarounds.md +++ b/docs/src/reference/known_issues_and_workarounds.md @@ -2,11 +2,7 @@ ## Known Issues -* [#1663](https://github.com/FuelLabs/sway/issues/1663): Using an explicit `return` in all branches of an `if let` expression causes a compile error. The workaround is to use implicit returns instead. - -* [#1387](https://github.com/FuelLabs/sway/issues/1387): In order to use `unwrap()` from the `result` library, all symbols of `result` needs to be imported via `use::result::*;`. - -* [#870](https://github.com/FuelLabs/sway/issues/870): All `impl` blocks need to be defined before any of the functions they define can be called. +* [#870](https://github.com/FuelLabs/sway/issues/870): All `impl` blocks need to be defined before any of the functions they define can be called. This includes sibling functions in the same `impl` declaration, i.e., functions in an `impl` can't call each other yet. ## Missing Features @@ -14,7 +10,9 @@ * [#428](https://github.com/FuelLabs/sway/issues/428): Arrays are currently immutable which means that changing elements of an array once initialized is not yet possible. -* [#1077](https://github.com/FuelLabs/sway/issues/1077): Dynamic vectors, i.e. `Vec`, have not yet been implemented. +* [#2035](https://github.com/FuelLabs/sway/issues/2035): Dynamic vectors _in storage_ have not yet been implemented. Only [vectors in memory](https://github.com/FuelLabs/sway/blob/master/sway-lib-std/src/vec.sw) are available at the moment. + +* [#1188](https://github.com/FuelLabs/sway/issues/1188): Mutable function arguments are not yet allowed except for `self`. ## General diff --git a/docs/theme/highlight.js b/docs/theme/highlight.js index 54196c7cedd..b4285d3ebe8 100644 --- a/docs/theme/highlight.js +++ b/docs/theme/highlight.js @@ -303,7 +303,7 @@ anyNumberOfTimes:g};for(const e in A)"object"==typeof A[e]&&n(A[e]) grmr_sway:e=>{const t={className:"title.function.invoke",relevance:0, begin:b(/\b/,/(?!let\b)/,e.IDENT_RE,u(/\s*\(/))},n="([u](8|16|32|64))?";return{ name:"Sway",aliases:["sw"],keywords:{$pattern:e.IDENT_RE+"!?", -keyword:["abi","as","asm","const","contract","deref","enum","fn","if","impl","let","library","match","mut","else","predicate","ref","return","script","Self","self","str","struct","trait","use","where","while"], +keyword:["abi","as","asm","const","contract","deref","enum","fn","if","impl","let","library","match","mut","else","predicate","ref","return","script","Self","self","storage","str","struct","trait","use","where","while"], literal:["true","false"], built_in:["bool","char","u8","u16","u32","u64","b256","str","Self"]}, illegal:""] +entry = "main.sw" +license = "Apache-2.0" +name = "identity" + +[dependencies] +std = { path = "../../sway-lib-std" } diff --git a/examples/identity/src/abi.sw b/examples/identity/src/abi.sw new file mode 100644 index 00000000000..0f54e4428f5 --- /dev/null +++ b/examples/identity/src/abi.sw @@ -0,0 +1,10 @@ +library r#abi; + +use std::identity::Identity; + +abi IdentityExample { + #[storage(read)]fn access_control_with_identity(); + fn cast_to_identity(); + fn different_executions(my_identity: Identity); + fn identity_to_contract_id(my_identity: Identity); +} diff --git a/examples/identity/src/errors.sw b/examples/identity/src/errors.sw new file mode 100644 index 00000000000..a730411d47e --- /dev/null +++ b/examples/identity/src/errors.sw @@ -0,0 +1,5 @@ +library errors; + +pub enum MyError { + UnauthorizedUser: (), +} diff --git a/examples/identity/src/main.sw b/examples/identity/src/main.sw new file mode 100644 index 00000000000..e48827aa469 --- /dev/null +++ b/examples/identity/src/main.sw @@ -0,0 +1,68 @@ +contract; + +dep r#abi; +dep errors; + +use r#abi::IdentityExample; +use errors::MyError; + +use std::{ + address::Address, + assert::require, + chain::auth::{AuthError, msg_sender}, + constants::BASE_ASSET_ID, + contract_id::ContractId, + identity::*, + result::*, + revert::revert, + token::{force_transfer_to_contract, transfer_to_output} +}; + +storage { + owner: Identity, +} + +impl IdentityExample for Contract { + fn cast_to_identity() { + // ANCHOR: cast_to_identity + let raw_address: b256 = 0xddec0e7e6a9a4a4e3e57d08d080d71a299c628a46bc609aab4627695679421ca; + let my_identity: Identity = Identity::Address(~Address::from(raw_address)); + // ANCHOR_END: cast_to_identity + } + + fn identity_to_contract_id(my_identity: Identity) { + // ANCHOR: identity_to_contract_id + let my_contract_id: ContractId = match my_identity { + Identity::ContractId(identity) => { + identity + }, + _ => { + revert(0); + } + }; + // ANCHOR_END: identity_to_contract_ids + } + + fn different_executions(my_identity: Identity) { + let amount = 1; + let token_id = BASE_ASSET_ID; + + // ANCHOR: different_executions + match my_identity { + Identity::Address(identity) => { + transfer_to_output(amount, token_id, identity); + }, + Identity::ContractId(identity) => { + force_transfer_to_contract(amount, token_id, identity); + }, + }; + // ANCHOR_END: different_executions + } + + #[storage(read)]fn access_control_with_identity() { + // ANCHOR: access_control_with_identity + let sender: Result = msg_sender(); + require(sender.unwrap() == storage.owner, MyError::UnauthorizedUser); + // ANCHOR_END: access_control_with_identity + } +} diff --git a/examples/storage_map/src/main.sw b/examples/storage_map/src/main.sw index 75bb617cab3..2a8f538e30f 100644 --- a/examples/storage_map/src/main.sw +++ b/examples/storage_map/src/main.sw @@ -16,11 +16,11 @@ storage { abi StorageMapExample { #[storage(write)]fn insert_into_map1(key: u64, value: u64); - #[storage(write)]fn get_from_map1(key: u64, value: u64); + #[storage(read)]fn get_from_map1(key: u64, value: u64) -> u64; - #[storage(read)]fn insert_into_map2(key: (b256, bool), value: Data); + #[storage(write)]fn insert_into_map2(key: (b256, bool), value: Data); - #[storage(read)]fn get_from_map2(key: (b256, bool), value: Data); + #[storage(read)]fn get_from_map2(key: (b256, bool), value: Data) -> Data; } impl StorageMapExample for Contract { @@ -28,15 +28,15 @@ impl StorageMapExample for Contract { storage.map1.insert(key, value); } - #[storage(write)]fn get_from_map1(key: u64, value: u64) { - storage.map1.insert(key, value); + #[storage(read)]fn get_from_map1(key: u64, value: u64) -> u64 { + storage.map1.get(key) } - #[storage(read)]fn insert_into_map2(key: (b256, bool), value: Data) { - storage.map2.get(key); + #[storage(write)]fn insert_into_map2(key: (b256, bool), value: Data) { + storage.map2.insert(key, value); } - #[storage(read)]fn get_from_map2(key: (b256, bool), value: Data) { - storage.map2.get(key); + #[storage(read)]fn get_from_map2(key: (b256, bool), value: Data) -> Data { + storage.map2.get(key) } } diff --git a/examples/wallet_smart_contract/src/main.sw b/examples/wallet_smart_contract/src/main.sw index c3a6a290bb6..a12018ca526 100644 --- a/examples/wallet_smart_contract/src/main.sw +++ b/examples/wallet_smart_contract/src/main.sw @@ -26,8 +26,8 @@ abi Wallet { impl Wallet for Contract { #[storage(read, write)]fn receive_funds() { - if msg_asset_id() == ~ContractId::from(BASE_ASSET_ID) { - // If we received `NATIVE_ASSET_ID` then keep track of the balance. + if msg_asset_id() == BASE_ASSET_ID { + // If we received `BASE_ASSET_ID` then keep track of the balance. // Otherwise, we're receiving other native assets and don't care // about our balance of tokens. storage.balance = storage.balance + msg_amount(); @@ -54,6 +54,6 @@ impl Wallet for Contract { // Note: `transfer_to_output()` is not a call and thus not an // interaction. Regardless, this code conforms to // checks-effects-interactions to avoid re-entrancy. - transfer_to_output(amount_to_send, ~ContractId::from(BASE_ASSET_ID), recipient_address); + transfer_to_output(amount_to_send, BASE_ASSET_ID, recipient_address); } } diff --git a/forc-gm/Cargo.toml b/forc-gm/Cargo.toml index 6f6face4ff6..a715bb90274 100644 --- a/forc-gm/Cargo.toml +++ b/forc-gm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "forc-gm" -version = "0.16.1" +version = "0.16.2" authors = ["Fuel Labs "] edition = "2021" homepage = "https://fuel.network/" diff --git a/forc-pkg/Cargo.toml b/forc-pkg/Cargo.toml index 44d35dfb952..07ee117b2bd 100644 --- a/forc-pkg/Cargo.toml +++ b/forc-pkg/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "forc-pkg" -version = "0.16.1" +version = "0.16.2" authors = ["Fuel Labs "] edition = "2021" homepage = "https://fuel.network/" @@ -10,15 +10,16 @@ description = "Building, locking, fetching and updating Sway projects as Forc pa [dependencies] anyhow = "1" -forc-util = { version = "0.16.1", path = "../forc-util" } +forc-util = { version = "0.16.2", path = "../forc-util" } +fuel-tx = "0.13" fuels-types = "0.12" git2 = { version = "0.14", features = ["vendored-libgit2", "vendored-openssl"] } petgraph = { version = "0.6", features = ["serde-1"] } semver = { version = "1.0", features = ["serde"] } serde = { version = "1.0", features = ["derive"] } serde_ignored = "0.1" -sway-core = { version = "0.16.1", path = "../sway-core" } -sway-utils = { version = "0.16.1", path = "../sway-utils" } +sway-core = { version = "0.16.2", path = "../sway-core" } +sway-utils = { version = "0.16.2", path = "../sway-utils" } toml = "0.5" tracing = "0.1" url = { version = "2.2", features = ["serde"] } diff --git a/forc-pkg/src/lib.rs b/forc-pkg/src/lib.rs index 80afe2b5af3..0282d485e27 100644 --- a/forc-pkg/src/lib.rs +++ b/forc-pkg/src/lib.rs @@ -9,6 +9,9 @@ pub mod manifest; mod pkg; pub use lock::Lock; -pub use manifest::{Manifest, ManifestFile}; +pub use manifest::{BuildProfile, Manifest, ManifestFile}; #[doc(inline)] pub use pkg::*; + +const CORE: &str = "core"; +const STD: &str = "std"; diff --git a/forc-pkg/src/manifest.rs b/forc-pkg/src/manifest.rs index 61bdeaab19e..3ef4bd0715c 100644 --- a/forc-pkg/src/manifest.rs +++ b/forc-pkg/src/manifest.rs @@ -1,7 +1,4 @@ -use crate::{ - pkg::{manifest_file_missing, parsing_failed, wrong_program_type}, - BuildConfig, -}; +use crate::pkg::{manifest_file_missing, parsing_failed, wrong_program_type}; use anyhow::{anyhow, bail, Result}; use forc_util::{find_manifest_dir, println_yellow_err, validate_name}; use serde::{Deserialize, Serialize}; @@ -14,6 +11,8 @@ use std::{ use sway_core::{parse, TreeType}; use sway_utils::constants; +type PatchMap = BTreeMap; + /// A [Manifest] that was deserialized from a file at a particular path. #[derive(Debug)] pub struct ManifestFile { @@ -30,7 +29,8 @@ pub struct Manifest { pub project: Project, pub network: Option, pub dependencies: Option>, - pub build_profile: Option>, + pub patch: Option>, + build_profile: Option>, } #[derive(Serialize, Deserialize, Debug)] @@ -76,6 +76,17 @@ pub struct DependencyDetails { pub(crate) rev: Option, } +/// Parameters to pass through to the `sway_core::BuildConfig` during compilation. +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "kebab-case")] +pub struct BuildProfile { + pub print_ir: bool, + pub print_finalized_asm: bool, + pub print_intermediate_asm: bool, + pub silent: bool, + pub time_phases: bool, +} + impl Dependency { /// The string of the `package` field if specified. pub fn package(&self) -> Option<&str> { @@ -182,6 +193,13 @@ impl ManifestFile { Ok(()) } } + + /// Access the build profile associated with the given profile name. + pub fn build_profile(&self, profile_name: &str) -> Option<&BuildProfile> { + self.build_profile + .as_ref() + .and_then(|profiles| profiles.get(profile_name)) + } } impl Manifest { @@ -241,7 +259,7 @@ impl Manifest { } /// Produce an iterator yielding all listed build profiles. - pub fn build_profiles(&self) -> impl Iterator { + pub fn build_profiles(&self) -> impl Iterator { self.build_profile .as_ref() .into_iter() @@ -256,6 +274,14 @@ impl Manifest { }) } + /// Produce an iterator yielding all listed patches. + pub fn patches(&self) -> impl Iterator { + self.patch + .as_ref() + .into_iter() + .flat_map(|patches| patches.iter()) + } + /// Check for the `core` and `std` packages under `[dependencies]`. If both are missing, add /// `std` implicitly. /// @@ -265,8 +291,7 @@ impl Manifest { /// Note: If only `core` is specified, we are unable to implicitly add `std` as we cannot /// guarantee that the user's `core` is compatible with the implicit `std`. fn implicitly_include_std_if_missing(&mut self, sway_git_tag: &str) { - const CORE: &str = "core"; - const STD: &str = "std"; + use crate::{CORE, STD}; // Don't include `std` if: // - this *is* `core` or `std`. // - either `core` or `std` packages are already specified. @@ -291,32 +316,13 @@ impl Manifest { /// If they are provided, use the provided `debug` or `release` so that they override the default `debug` /// and `release`. fn implicitly_include_default_build_profiles_if_missing(&mut self) { - const DEBUG: &str = "debug"; - const RELEASE: &str = "release"; - let build_profiles = self.build_profile.get_or_insert_with(Default::default); - if build_profiles.get(DEBUG).is_none() { - build_profiles.insert( - DEBUG.to_string(), - BuildConfig { - print_ir: false, - print_finalized_asm: false, - print_intermediate_asm: false, - silent: false, - }, - ); + if build_profiles.get(BuildProfile::DEBUG).is_none() { + build_profiles.insert(BuildProfile::DEBUG.into(), BuildProfile::debug()); } - if build_profiles.get(RELEASE).is_none() { - build_profiles.insert( - RELEASE.to_string(), - BuildConfig { - print_ir: false, - print_finalized_asm: false, - print_intermediate_asm: false, - silent: false, - }, - ); + if build_profiles.get(BuildProfile::RELEASE).is_none() { + build_profiles.insert(BuildProfile::RELEASE.into(), BuildProfile::release()); } } @@ -341,6 +347,32 @@ impl Manifest { } } +impl BuildProfile { + pub const DEBUG: &'static str = "debug"; + pub const RELEASE: &'static str = "release"; + pub const DEFAULT: &'static str = Self::DEBUG; + + pub fn debug() -> Self { + Self { + print_ir: false, + print_finalized_asm: false, + print_intermediate_asm: false, + silent: false, + time_phases: false, + } + } + + pub fn release() -> Self { + Self { + print_ir: false, + print_finalized_asm: false, + print_intermediate_asm: false, + silent: false, + time_phases: false, + } + } +} + impl std::ops::Deref for ManifestFile { type Target = Manifest; fn deref(&self) -> &Self::Target { @@ -348,6 +380,12 @@ impl std::ops::Deref for ManifestFile { } } +impl Default for BuildProfile { + fn default() -> Self { + Self::debug() + } +} + /// The definition for the implicit `std` dependency. fn implicit_std_dep(sway_git_tag: String) -> Dependency { const SWAY_GIT_REPO_URL: &str = "https://github.com/fuellabs/sway"; diff --git a/forc-pkg/src/pkg.rs b/forc-pkg/src/pkg.rs index f77ebdbc60d..0d46b0fb61c 100644 --- a/forc-pkg/src/pkg.rs +++ b/forc-pkg/src/pkg.rs @@ -1,12 +1,14 @@ use crate::{ lock::Lock, - manifest::{Dependency, Manifest, ManifestFile}, + manifest::{BuildProfile, Dependency, Manifest, ManifestFile}, + CORE, STD, }; use anyhow::{anyhow, bail, Context, Error, Result}; use forc_util::{ find_file_name, git_checkouts_directory, kebab_to_snake_case, print_on_failure, print_on_success, print_on_success_library, println_yellow_err, }; +use fuel_tx::StorageSlot; use fuels_types::JsonABI; use petgraph::{ self, @@ -15,8 +17,8 @@ use petgraph::{ }; use serde::{Deserialize, Serialize}; use std::{ - collections::{hash_map, BTreeSet, HashMap, HashSet}, - fmt, + collections::{hash_map, BTreeSet, HashMap}, + fmt, fs, hash::{Hash, Hasher}, path::{Path, PathBuf}, str::FromStr, @@ -45,6 +47,7 @@ pub struct PinnedId(u64); /// The result of successfully compiling a package. pub struct Compiled { pub json_abi: JsonABI, + pub storage_slots: Vec, pub bytecode: Vec, pub tree_type: TreeType, } @@ -166,16 +169,6 @@ pub struct BuildPlan { compilation_order: Vec, } -/// Parameters to pass through to the `sway_core::BuildConfig` during compilation. -#[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "kebab-case")] -pub struct BuildConfig { - pub print_ir: bool, - pub print_finalized_asm: bool, - pub print_intermediate_asm: bool, - pub silent: bool, -} - /// Error returned upon failed parsing of `PinnedId::from_str`. #[derive(Clone, Debug)] pub struct PinnedIdParseError; @@ -235,6 +228,71 @@ impl BuildPlan { }) } + /// Create a new build plan taking into account the state of both the Manifest and the existing lock file if there is one. + /// + /// This will first attempt to load a build plan from the lock file and validate the resulting graph using the current state of the Manifest. + /// + /// This includes checking if the [dependencies] or [patch] tables have changed and checking the validity of the local path dependencies. + /// If any changes are detected, the graph is updated and any new packages that require fetching are fetched. + /// + /// The resulting build plan should always be in a valid state that is ready for building or checking. + pub fn load_from_manifest( + manifest: &ManifestFile, + locked: bool, + offline: bool, + sway_git_tag: &str, + ) -> Result { + let lock_path = forc_util::lock_path(manifest.dir()); + let plan_result = BuildPlan::from_lock_file(&lock_path, sway_git_tag); + + // Retrieve the old lock file state so we can produce a diff. + let old_lock = plan_result + .as_ref() + .ok() + .map(|plan| Lock::from_graph(plan.graph())) + .unwrap_or_default(); + + // Check if there are any errors coming from the BuildPlan generation from the lock file + // If there are errors we will need to create the BuildPlan from scratch, i.e fetch & pin everything + let mut new_lock_cause = None; + let mut plan = plan_result.or_else(|e| -> Result { + new_lock_cause = if e.to_string().contains("No such file or directory") { + Some(anyhow!("lock file did not exist")) + } else { + Some(e) + }; + let plan = BuildPlan::new(manifest, sway_git_tag, offline)?; + Ok(plan) + })?; + + // If there are no issues with the BuildPlan generated from the lock file + // Check and apply the diff. + if new_lock_cause.is_none() { + let diff = plan.validate(manifest, sway_git_tag)?; + if !diff.added.is_empty() || !diff.removed.is_empty() { + new_lock_cause = Some(anyhow!("lock file did not match manifest `diff`")); + plan = plan.apply_pkg_diff(diff, sway_git_tag, offline)?; + } + } + + if let Some(cause) = new_lock_cause { + if locked { + bail!( + "The lock file {} needs to be updated (Cause: {}) \ + but --locked was passed to prevent this.", + lock_path.to_string_lossy(), + cause, + ); + } + + info!(" Creating a new `Forc.lock` file. (Cause: {})", cause); + create_new_lock(&plan, &old_lock, manifest, &lock_path)?; + info!(" Created new lock file at {}", lock_path.display()); + } + + Ok(plan) + } + /// Create a new build plan from an existing one. Needs the difference with the existing plan with the lock. pub fn apply_pkg_diff( &self, @@ -333,7 +391,8 @@ impl BuildPlan { } let name = dep.package().unwrap_or(dep_name).to_string(); - let source = dep_to_source(proj_path, dep)?; + let source = + apply_patch(&name, &dep_to_source(proj_path, dep)?, manifest, proj_path)?; let dep_pkg = Pkg { name, source }; Ok((dep_name, dep_pkg)) }) @@ -827,10 +886,24 @@ pub fn graph_to_path_map( bail!("missing path info for dependency: {}", &dep_name); } })?; - let rel_dep_path = detailed - .path - .as_ref() - .ok_or_else(|| anyhow!("missing path info for dependency: {}", dep.name))?; + // Check if there is a patch for this dep + let patch = parent_manifest + .patches() + .find_map(|patches| patches.1.get(&dep_name)); + // If there is one fetch the details. + let patch_details = patch.and_then(|patch| match patch { + Dependency::Simple(_) => None, + Dependency::Detailed(detailed) => Some(detailed), + }); + // If there is a detail we should have the path. + // If not either we do not have a patch so we are checking dependencies of parent + // If we can't find the path there, either patch or dep is provided as a basic dependency, so we are missing the path info. + let rel_dep_path = if let Some(patch_details) = patch_details { + patch_details.path.as_ref() + } else { + detailed.path.as_ref() + } + .ok_or_else(|| anyhow!("missing path info for dep: {}", &dep_name))?; let path = parent_path.join(rel_dep_path); if !path.exists() { bail!("pinned `path` dependency \"{}\" source missing", dep.name); @@ -930,6 +1003,38 @@ pub(crate) fn fetch_deps( Ok((graph, path_map)) } +fn apply_patch( + name: &str, + source: &Source, + manifest: &Manifest, + parent_path: &Path, +) -> Result { + match source { + // Check if the patch is for a git dependency. + Source::Git(git) => { + // Check if we got a patch for the git dependency. + if let Some(source_patches) = manifest + .patch + .as_ref() + .and_then(|patches| patches.get(git.repo.as_str())) + { + if let Some(patch) = source_patches.get(name) { + Ok(dep_to_source(parent_path, patch)?) + } else { + bail!( + "Cannot find the patch for the {} for package {}", + git.repo, + name + ) + } + } else { + Ok(source.clone()) + } + } + _ => Ok(source.clone()), + } +} + /// Produce a unique ID for a particular fetch pass. /// /// This is used in the temporary git directory and allows for avoiding contention over the git repo directory. @@ -958,7 +1063,12 @@ fn fetch_children( let parent_path = path_map[&parent_id].clone(); for (dep_name, dep) in manifest.deps() { let name = dep.package().unwrap_or(dep_name).to_string(); - let source = dep_to_source(&parent_path, dep)?; + let source = apply_patch( + &name, + &dep_to_source(&parent_path, dep)?, + manifest, + &parent_path, + )?; if offline_mode && !matches!(source, Source::Path(_)) { bail!("Unable to fetch pkg {:?} in offline mode", source); } @@ -1237,7 +1347,9 @@ fn dep_to_source(pkg_path: &Path, dep: &Dependency) -> Result { Dependency::Detailed(ref det) => match (&det.path, &det.version, &det.git) { (Some(relative_path), _, _) => { let path = pkg_path.join(relative_path); - Source::Path(path.canonicalize()?) + Source::Path(path.canonicalize().map_err(|err| { + anyhow!("Cant apply patch from {}, cause: {}", relative_path, &err) + })?) } (_, _, Some(repo)) => { let reference = match (&det.branch, &det.tag, &det.rev) { @@ -1262,12 +1374,12 @@ fn dep_to_source(pkg_path: &Path, dep: &Dependency) -> Result { Ok(source) } -/// Given a `forc_pkg::BuildConfig`, produce the necessary `sway_core::BuildConfig` required for +/// Given a `forc_pkg::BuildProfile`, produce the necessary `sway_core::BuildConfig` required for /// compilation. pub fn sway_build_config( manifest_dir: &Path, entry_path: &Path, - build_conf: &BuildConfig, + build_profile: &BuildProfile, ) -> Result { // Prepare the build config to pass through to the compiler. let file_name = find_file_name(manifest_dir, entry_path)?; @@ -1275,45 +1387,98 @@ pub fn sway_build_config( file_name.to_path_buf(), manifest_dir.to_path_buf(), ) - .print_finalized_asm(build_conf.print_finalized_asm) - .print_intermediate_asm(build_conf.print_intermediate_asm) - .print_ir(build_conf.print_ir); + .print_finalized_asm(build_profile.print_finalized_asm) + .print_intermediate_asm(build_profile.print_intermediate_asm) + .print_ir(build_profile.print_ir); Ok(build_config) } /// Builds the dependency namespace for the package at the given node index within the graph. /// /// This function is designed to be called for each node in order of compilation. +/// +/// This function ensures that if `core` exists in the graph (the vastly common case) it is also +/// present within the namespace. This is a necessity for operators to work for example. pub fn dependency_namespace( namespace_map: &HashMap, graph: &Graph, - compilation_order: &[NodeIx], node: NodeIx, ) -> namespace::Module { + let mut namespace = namespace::Module::default(); + + // Add direct dependencies. + let mut core_added = false; + for edge in graph.edges_directed(node, Direction::Outgoing) { + let dep_node = edge.target(); + let dep_namespace = &namespace_map[&dep_node]; + let dep_name = kebab_to_snake_case(edge.weight()); + namespace.insert_submodule(dep_name, dep_namespace.clone()); + let dep = &graph[dep_node]; + if dep.name == CORE { + core_added = true; + } + } + + // Add `core` if not already added. + if !core_added { + if let Some(core_node) = find_core_dep(graph, node) { + let core_namespace = &namespace_map[&core_node]; + namespace.insert_submodule(CORE.to_string(), core_namespace.clone()); + } + } + + namespace +} + +/// Find the `core` dependency (whether direct or transitive) for the given node if it exists. +fn find_core_dep(graph: &Graph, node: NodeIx) -> Option { use petgraph::visit::{Dfs, Walker}; - // Find all nodes that are a dependency of this one with a depth-first search. - let deps: HashSet = Dfs::new(graph, node).iter(graph).collect(); + // If we are `core`, do nothing. + let pkg = &graph[node]; + if pkg.name == CORE { + return None; + } - // In order of compilation, accumulate dependency namespaces as submodules. - let mut namespace = namespace::Module::default(); - for &dep_node in compilation_order.iter().filter(|n| deps.contains(n)) { - if dep_node == node { - break; + // If we have `core` as a direct dep, use it. + let mut maybe_std = None; + for edge in graph.edges_directed(node, Direction::Outgoing) { + let dep_node = edge.target(); + let dep = &graph[dep_node]; + match &dep.name[..] { + CORE => return Some(dep_node), + STD => maybe_std = Some(dep_node), + _ => (), } - // Add the namespace once for each of its names. - let dep_namespace = &namespace_map[&dep_node]; - let dep_names: BTreeSet<_> = graph - .edges_directed(dep_node, Direction::Incoming) - .map(|e| e.weight()) - .collect(); - for dep_name in dep_names { - let dep_name = kebab_to_snake_case(dep_name); - namespace.insert_submodule(dep_name.to_string(), dep_namespace.clone()); + } + + // If we have `std`, select `core` via `std`. + if let Some(std) = maybe_std { + return find_core_dep(graph, std); + } + + // Otherwise, search from this node. + for dep_node in Dfs::new(graph, node).iter(graph) { + let dep = &graph[dep_node]; + if dep.name == CORE { + return Some(dep_node); } } - namespace + None +} + +/// Compiles the package to an AST. +pub fn compile_ast( + manifest: &ManifestFile, + build_profile: &BuildProfile, + namespace: namespace::Module, +) -> Result { + let source = manifest.entry_string()?; + let sway_build_config = + sway_build_config(manifest.dir(), &manifest.entry_path(), build_profile)?; + let ast_res = sway_core::compile_to_ast(source, namespace, Some(&sway_build_config)); + Ok(ast_res) } /// Compiles the given package. @@ -1337,17 +1502,40 @@ pub fn dependency_namespace( pub fn compile( pkg: &Pinned, manifest: &ManifestFile, - build_config: &BuildConfig, + build_profile: &BuildProfile, namespace: namespace::Module, source_map: &mut SourceMap, ) -> Result<(Compiled, Option)> { + // Time the given expression and print the result if `build_config.time_phases` is true. + macro_rules! time_expr { + ($description:expr, $expression:expr) => {{ + if build_profile.time_phases { + let expr_start = std::time::Instant::now(); + let output = { $expression }; + println!( + " Time elapsed to {}: {:?}", + $description, + expr_start.elapsed() + ); + output + } else { + $expression + } + }}; + } + let entry_path = manifest.entry_path(); - let source = manifest.entry_string()?; - let sway_build_config = sway_build_config(manifest.dir(), &entry_path, build_config)?; - let silent_mode = build_config.silent; + let sway_build_config = time_expr!( + "produce `sway_core::BuildConfig`", + sway_build_config(manifest.dir(), &entry_path, build_profile)? + ); + let silent_mode = build_profile.silent; // First, compile to an AST. We'll update the namespace and check for JSON ABI output. - let ast_res = sway_core::compile_to_ast(source, namespace, Some(&sway_build_config)); + let ast_res = time_expr!( + "compile to ast", + compile_ast(manifest, build_profile, namespace)? + ); match &ast_res { CompileAstResult::Failure { warnings, errors } => { print_on_failure(silent_mode, warnings, errors); @@ -1357,7 +1545,8 @@ pub fn compile( typed_program, warnings, } => { - let json_abi = typed_program.kind.generate_json_abi(); + let json_abi = time_expr!("generate JSON ABI", typed_program.kind.generate_json_abi()); + let storage_slots = typed_program.storage_slots.clone(); let tree_type = typed_program.kind.tree_type(); match tree_type { // If we're compiling a library, we don't need to compile any further. @@ -1368,6 +1557,7 @@ pub fn compile( let lib_namespace = typed_program.root.namespace.clone(); let compiled = Compiled { json_abi, + storage_slots, bytecode, tree_type, }; @@ -1376,14 +1566,21 @@ pub fn compile( // For all other program types, we'll compile the bytecode. TreeType::Contract | TreeType::Predicate | TreeType::Script => { - let asm_res = sway_core::ast_to_asm(ast_res, &sway_build_config); - let bc_res = sway_core::asm_to_bytecode(asm_res, source_map); + let asm_res = time_expr!( + "compile ast to asm", + sway_core::ast_to_asm(ast_res, &sway_build_config) + ); + let bc_res = time_expr!( + "compile asm to bytecode", + sway_core::asm_to_bytecode(asm_res, source_map) + ); match bc_res { BytecodeCompilationResult::Success { bytes, warnings } => { print_on_success(silent_mode, &pkg.name, &warnings, &tree_type); let bytecode = bytes; let compiled = Compiled { json_abi, + storage_slots, bytecode, tree_type, }; @@ -1410,26 +1607,27 @@ pub fn compile( /// Also returns the resulting `sway_core::SourceMap` which may be useful for debugging purposes. pub fn build( plan: &BuildPlan, - conf: &BuildConfig, + profile: &BuildProfile, sway_git_tag: &str, ) -> anyhow::Result<(Compiled, SourceMap)> { let mut namespace_map = Default::default(); let mut source_map = SourceMap::new(); let mut json_abi = vec![]; + let mut storage_slots = vec![]; let mut bytecode = vec![]; let mut tree_type = None; for &node in &plan.compilation_order { - let dep_namespace = - dependency_namespace(&namespace_map, &plan.graph, &plan.compilation_order, node); + let dep_namespace = dependency_namespace(&namespace_map, &plan.graph, node); let pkg = &plan.graph[node]; let path = &plan.path_map[&pkg.id()]; let manifest = ManifestFile::from_dir(path, sway_git_tag)?; - let res = compile(pkg, &manifest, conf, dep_namespace, &mut source_map)?; + let res = compile(pkg, &manifest, profile, dep_namespace, &mut source_map)?; let (compiled, maybe_namespace) = res; if let Some(namespace) = maybe_namespace { namespace_map.insert(node, namespace.into()); } json_abi.extend(compiled.json_abi); + storage_slots.extend(compiled.storage_slots); bytecode = compiled.bytecode; tree_type = Some(compiled.tree_type); source_map.insert_dependency(path.clone()); @@ -1439,11 +1637,46 @@ pub fn build( let compiled = Compiled { bytecode, json_abi, + storage_slots, tree_type, }; Ok((compiled, source_map)) } +/// Compile the entire forc package and return a CompileAstResult. +pub fn check( + plan: &BuildPlan, + silent_mode: bool, + sway_git_tag: &str, +) -> anyhow::Result { + let profile = BuildProfile { + silent: silent_mode, + ..BuildProfile::debug() + }; + + let mut namespace_map = Default::default(); + let mut source_map = SourceMap::new(); + for (i, &node) in plan.compilation_order.iter().enumerate() { + let dep_namespace = dependency_namespace(&namespace_map, &plan.graph, node); + let pkg = &plan.graph[node]; + let path = &plan.path_map[&pkg.id()]; + let manifest = ManifestFile::from_dir(path, sway_git_tag)?; + let ast_res = compile_ast(&manifest, &profile, dep_namespace)?; + if let CompileAstResult::Success { typed_program, .. } = &ast_res { + if let TreeType::Library { .. } = typed_program.kind.tree_type() { + namespace_map.insert(node, typed_program.root.namespace.clone()); + } + } + source_map.insert_dependency(path.clone()); + + // We only need to return the final CompileAstResult + if i == plan.compilation_order.len() - 1 { + return Ok(ast_res); + } + } + bail!("unable to check sway program: build plan contains no packages") +} + /// Attempt to find a `Forc.toml` with the given project name within the given directory. /// /// Returns the path to the package on success, or `None` in the case it could not be found. @@ -1565,3 +1798,18 @@ pub fn fuel_core_not_running(node_url: &str) -> anyhow::Error { let message = format!("could not get a response from node at the URL {}. Start a node with `fuel-core`. See https://github.com/FuelLabs/fuel-core#running for more information", node_url); Error::msg(message) } + +fn create_new_lock( + plan: &BuildPlan, + old_lock: &Lock, + manifest: &ManifestFile, + lock_path: &Path, +) -> Result<()> { + let lock = Lock::from_graph(plan.graph()); + let diff = lock.diff(old_lock); + super::lock::print_diff(&manifest.project.name, &diff); + let string = toml::ser::to_string_pretty(&lock) + .map_err(|e| anyhow!("failed to serialize lock file: {}", e))?; + fs::write(&lock_path, &string).map_err(|e| anyhow!("failed to write lock file: {}", e))?; + Ok(()) +} diff --git a/forc-plugins/forc-explore/Cargo.toml b/forc-plugins/forc-explore/Cargo.toml index 8fe0e61ec8c..4850c2dc859 100644 --- a/forc-plugins/forc-explore/Cargo.toml +++ b/forc-plugins/forc-explore/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "forc-explore" -version = "0.16.1" +version = "0.16.2" authors = ["Fuel Labs "] edition = "2021" homepage = "https://fuel.network/" @@ -11,7 +11,7 @@ description = "A `forc` plugin for running the fuel block explorer." [dependencies] anyhow = "1" clap = { version = "3", features = ["derive"] } -forc-util = { version = "0.16.1", path = "../../forc-util" } +forc-util = { version = "0.16.2", path = "../../forc-util" } reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls"] } serde = { version = "1.0", features = ["derive"] } tar = "0.4" diff --git a/forc-plugins/forc-fmt-v2/Cargo.toml b/forc-plugins/forc-fmt-v2/Cargo.toml index d6f23c178d4..3408d63f24e 100644 --- a/forc-plugins/forc-fmt-v2/Cargo.toml +++ b/forc-plugins/forc-fmt-v2/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "forc-fmt-v2" -version = "0.16.1" +version = "0.16.2" authors = ["Fuel Labs "] edition = "2021" homepage = "https://fuel.network/" @@ -12,10 +12,10 @@ description = "A `forc` plugin for running the Sway code formatter." [dependencies] anyhow = "1" clap = { version = "3", features = ["derive"] } -forc-util = { version = "0.16.1", path = "../../forc-util" } +forc-util = { version = "0.16.2", path = "../../forc-util" } prettydiff = "0.5" -sway-core = { version = "0.16.1", path = "../../sway-core" } -sway-fmt-v2 = { version = "0.16.1", path = "../../sway-fmt-v2" } -sway-utils = { version = "0.16.1", path = "../../sway-utils" } +sway-core = { version = "0.16.2", path = "../../sway-core" } +sway-fmt-v2 = { version = "0.16.2", path = "../../sway-fmt-v2" } +sway-utils = { version = "0.16.2", path = "../../sway-utils" } taplo = "0.7" tracing = "0.1" diff --git a/forc-plugins/forc-fmt/Cargo.toml b/forc-plugins/forc-fmt/Cargo.toml index 06188bcf4b3..00dabc3b507 100644 --- a/forc-plugins/forc-fmt/Cargo.toml +++ b/forc-plugins/forc-fmt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "forc-fmt" -version = "0.16.1" +version = "0.16.2" authors = ["Fuel Labs "] edition = "2021" homepage = "https://fuel.network/" @@ -11,10 +11,10 @@ description = "A `forc` plugin for running the Sway code formatter." [dependencies] anyhow = "1" clap = { version = "3", features = ["derive"] } -forc-util = { version = "0.16.1", path = "../../forc-util" } +forc-util = { version = "0.16.2", path = "../../forc-util" } prettydiff = "0.5" -sway-core = { version = "0.16.1", path = "../../sway-core" } -sway-fmt = { version = "0.16.1", path = "../../sway-fmt" } -sway-utils = { version = "0.16.1", path = "../../sway-utils" } +sway-core = { version = "0.16.2", path = "../../sway-core" } +sway-fmt = { version = "0.16.2", path = "../../sway-fmt" } +sway-utils = { version = "0.16.2", path = "../../sway-utils" } taplo = "0.7" tracing = "0.1" diff --git a/forc-plugins/forc-lsp/Cargo.toml b/forc-plugins/forc-lsp/Cargo.toml index 64c47901d19..5c1614ba4c5 100644 --- a/forc-plugins/forc-lsp/Cargo.toml +++ b/forc-plugins/forc-lsp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "forc-lsp" -version = "0.16.1" +version = "0.16.2" authors = ["Fuel Labs "] edition = "2021" homepage = "https://fuel.network/" @@ -11,5 +11,5 @@ description = "A simple `forc` plugin for starting the sway language server." [dependencies] anyhow = "1" clap = { version = "3", features = ["derive"] } -sway-lsp = { version = "0.16.1", path = "../../sway-lsp" } +sway-lsp = { version = "0.16.2", path = "../../sway-lsp" } tokio = { version = "1.8" } diff --git a/forc-util/Cargo.toml b/forc-util/Cargo.toml index b71660fb188..731ed398ef6 100644 --- a/forc-util/Cargo.toml +++ b/forc-util/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "forc-util" -version = "0.16.1" +version = "0.16.2" authors = ["Fuel Labs "] edition = "2021" homepage = "https://fuel.network/" @@ -12,9 +12,9 @@ description = "Utility items shared between forc crates." annotate-snippets = { version = "0.9", features = ["color"] } anyhow = "1" dirs = "3.0.2" -sway-core = { version = "0.16.1", path = "../sway-core" } -sway-types = { version = "0.16.1", path = "../sway-types" } -sway-utils = { version = "0.16.1", path = "../sway-utils" } +sway-core = { version = "0.16.2", path = "../sway-core" } +sway-types = { version = "0.16.2", path = "../sway-types" } +sway-utils = { version = "0.16.2", path = "../sway-utils" } termcolor = "1.1" tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["ansi", "env-filter", "json"] } diff --git a/forc/Cargo.toml b/forc/Cargo.toml index 435a4919f78..45730610788 100644 --- a/forc/Cargo.toml +++ b/forc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "forc" -version = "0.16.1" +version = "0.16.2" authors = ["Fuel Labs "] edition = "2021" homepage = "https://fuel.network/" @@ -21,21 +21,21 @@ annotate-snippets = { version = "0.9", features = ["color"] } anyhow = "1.0.41" clap = { version = "3.1", features = ["cargo", "derive", "env"] } clap_complete = "3.1" -forc-pkg = { version = "0.16.1", path = "../forc-pkg" } -forc-util = { version = "0.16.1", path = "../forc-util" } +forc-pkg = { version = "0.16.2", path = "../forc-pkg" } +forc-util = { version = "0.16.2", path = "../forc-util" } fs_extra = "1.2" fuel-asm = "0.5" fuel-crypto = "0.5" -fuel-gql-client = { version = "0.8", default-features = false } -fuel-tx = "0.12" -fuel-vm = "0.11" +fuel-gql-client = { version = "0.9", default-features = false } +fuel-tx = "0.13" +fuel-vm = "0.12" futures = "0.3" hex = "0.4.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.73" -sway-core = { version = "0.16.1", path = "../sway-core" } -sway-types = { version = "0.16.1", path = "../sway-types" } -sway-utils = { version = "0.16.1", path = "../sway-utils" } +sway-core = { version = "0.16.2", path = "../sway-core" } +sway-types = { version = "0.16.2", path = "../sway-types" } +sway-utils = { version = "0.16.2", path = "../sway-utils" } term-table = "1.3" tokio = { version = "1.8.0", features = ["macros", "rt-multi-thread", "process"] } toml = "0.5" diff --git a/forc/src/cli/commands/build.rs b/forc/src/cli/commands/build.rs index eb7573f7413..6209bb45790 100644 --- a/forc/src/cli/commands/build.rs +++ b/forc/src/cli/commands/build.rs @@ -21,13 +21,18 @@ pub struct Command { /// Path to the project, if not specified, current working directory will be used. #[clap(short, long)] pub path: Option, - /// Whether to compile to bytecode (false) or to print out the generated ASM (true). + /// Print the finalized ASM. + /// + /// This is the state of the ASM with registers allocated and optimisations applied. #[clap(long)] pub print_finalized_asm: bool, - /// Whether to compile to bytecode (false) or to print out the generated ASM (true). + /// Print the generated ASM. + /// + /// This is the state of the ASM prior to performing register allocation and other ASM + /// optimisations. #[clap(long)] pub print_intermediate_asm: bool, - /// Whether to compile to bytecode (false) or to print out the generated IR (true). + /// Print the generated Sway IR (Intermediate Representation). #[clap(long)] pub print_ir: bool, /// If set, outputs a binary file representing the script bytes. @@ -52,6 +57,10 @@ pub struct Command { /// output will be "minified", i.e. all on one line without whitespace. #[clap(long)] pub minify_json_abi: bool, + /// By default the JSON for initial storage slots is formatted for human readability. By using + /// this option JSON output will be "minified", i.e. all on one line without whitespace. + #[clap(long)] + pub minify_json_storage_slots: bool, /// Requires that the Forc.lock file is up-to-date. If the lock file is missing, or it /// needs to be updated, Forc will exit with an error #[clap(long)] @@ -65,6 +74,9 @@ pub struct Command { /// If --build-profile is also provided, forc omits this flag and uses provided build-profile. #[clap(long)] pub release: bool, + /// Output the time elapsed over each part of the compilation process. + #[clap(long)] + pub time_phases: bool, } pub(crate) fn exec(command: Command) -> Result<()> { diff --git a/forc/src/cli/commands/check.rs b/forc/src/cli/commands/check.rs new file mode 100644 index 00000000000..94dc949b33b --- /dev/null +++ b/forc/src/cli/commands/check.rs @@ -0,0 +1,30 @@ +use crate::ops::forc_check; +use anyhow::Result; +use clap::Parser; + +/// Check the current or target project and all of its dependencies for errors. +/// +/// This will essentially compile the packages without performing the final step of code generation, +/// which is faster than running forc build. +#[derive(Debug, Default, Parser)] +pub struct Command { + /// Path to the project, if not specified, current working directory will be used. + #[clap(short, long)] + pub path: Option, + /// Offline mode, prevents Forc from using the network when managing dependencies. + /// Meaning it will only try to use previously downloaded dependencies. + #[clap(long = "offline")] + pub offline_mode: bool, + /// Silent mode. Don't output any warnings or errors to the command line. + #[clap(long = "silent", short = 's')] + pub silent_mode: bool, + /// Requires that the Forc.lock file is up-to-date. If the lock file is missing, or it + /// needs to be updated, Forc will exit with an error + #[clap(long)] + pub locked: bool, +} + +pub(crate) fn exec(command: Command) -> Result<()> { + forc_check::check(command)?; + Ok(()) +} diff --git a/forc/src/cli/commands/deploy.rs b/forc/src/cli/commands/deploy.rs index cc66e241d30..d778da1df23 100644 --- a/forc/src/cli/commands/deploy.rs +++ b/forc/src/cli/commands/deploy.rs @@ -9,13 +9,18 @@ pub struct Command { /// Path to the project, if not specified, current working directory will be used. #[clap(short, long)] pub path: Option, - /// Whether to compile to bytecode (false) or to print out the generated ASM (true). + /// Print the finalized ASM. + /// + /// This is the state of the ASM with registers allocated and optimisations applied. #[clap(long)] pub print_finalized_asm: bool, - /// Whether to compile to bytecode (false) or to print out the generated ASM (true). + /// Print the generated ASM. + /// + /// This is the state of the ASM prior to performing register allocation and other ASM + /// optimisations. #[clap(long)] pub print_intermediate_asm: bool, - /// Whether to compile to bytecode (false) or to print out the IR (true). + /// Print the generated Sway IR (Intermediate Representation). #[clap(long)] pub print_ir: bool, /// If set, outputs a binary file representing the script bytes. @@ -40,6 +45,10 @@ pub struct Command { /// output will be "minified", i.e. all on one line without whitespace. #[clap(long)] pub minify_json_abi: bool, + /// By default the JSON for initial storage slots is formatted for human readability. By using + /// this option JSON output will be "minified", i.e. all on one line without whitespace. + #[clap(long)] + pub minify_json_storage_slots: bool, /// Requires that the Forc.lock file is up-to-date. If the lock file is missing, or it /// needs to be updated, Forc will exit with an error #[clap(long)] @@ -57,6 +66,9 @@ pub struct Command { /// If --build-profile is also provided, forc omits this flag and uses provided build-profile. #[clap(long)] pub release: bool, + /// Output the time elapsed over each part of the compilation process. + #[clap(long)] + pub time_phases: bool, } pub(crate) async fn exec(command: Command) -> Result<()> { diff --git a/forc/src/cli/commands/mod.rs b/forc/src/cli/commands/mod.rs index 90e3bc394ba..987b2d27965 100644 --- a/forc/src/cli/commands/mod.rs +++ b/forc/src/cli/commands/mod.rs @@ -1,5 +1,6 @@ pub mod addr2line; pub mod build; +pub mod check; pub mod clean; pub mod completions; pub mod deploy; diff --git a/forc/src/cli/commands/plugins.rs b/forc/src/cli/commands/plugins.rs index 61bb7aa5687..f8d1c450279 100644 --- a/forc/src/cli/commands/plugins.rs +++ b/forc/src/cli/commands/plugins.rs @@ -1,7 +1,7 @@ use crate::cli::PluginsCommand; use anyhow::{anyhow, Result}; use clap::Parser; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use tracing::info; /// Find all forc plugins available via `PATH`. @@ -32,9 +32,9 @@ pub(crate) fn exec(command: PluginsCommand) -> Result<()> { /// Find a plugin's description /// -/// Given a plugin name, returns the description included in the `-h` opt. Returns -/// a generic description if a description cannot be found -fn parse_description_for_plugin(plugin: &str) -> String { +/// Given a cannonical plugin path, returns the description included in the `-h` opt. +/// Returns a generic description if a description cannot be found +fn parse_description_for_plugin(plugin: &Path) -> String { use std::process::Command; let default_description = "No description found for this plugin."; let proc = Command::new(plugin) @@ -78,7 +78,7 @@ fn format_print_description( .to_string() }; - let description = parse_description_for_plugin(&display); + let description = parse_description_for_plugin(&path); if describe { Ok(format!(" {} \t\t{}", display, description)) diff --git a/forc/src/cli/commands/run.rs b/forc/src/cli/commands/run.rs index bc23a87da39..bc70505f980 100644 --- a/forc/src/cli/commands/run.rs +++ b/forc/src/cli/commands/run.rs @@ -31,15 +31,20 @@ pub struct Command { #[clap(short, long)] pub kill_node: bool, - /// Whether to compile to bytecode (false) or to print out the generated ASM (true). + /// Print the finalized ASM. + /// + /// This is the state of the ASM with registers allocated and optimisations applied. #[clap(long)] pub print_finalized_asm: bool, - /// Whether to compile to bytecode (false) or to print out the generated ASM (true). + /// Print the generated ASM. + /// + /// This is the state of the ASM prior to performing register allocation and other ASM + /// optimisations. #[clap(long)] pub print_intermediate_asm: bool, - /// Whether to compile to bytecode (false) or to print out the IR (true). + /// Print the generated Sway IR (Intermediate Representation). #[clap(long)] pub print_ir: bool, @@ -55,6 +60,10 @@ pub struct Command { #[clap(long = "silent", short = 's')] pub silent_mode: bool, + /// Output the time elapsed over each part of the compilation process. + #[clap(long)] + pub time_phases: bool, + /// Pretty-print the outputs from the node. #[clap(long = "pretty-print", short = 'r')] pub pretty_print: bool, @@ -74,6 +83,11 @@ pub struct Command { #[clap(long)] pub minify_json_abi: bool, + /// By default the JSON for initial storage slots is formatted for human readability. By using + /// this option JSON output will be "minified", i.e. all on one line without whitespace. + #[clap(long)] + pub minify_json_storage_slots: bool, + /// Set the transaction byte price. Defaults to 0. #[clap(long)] pub byte_price: Option, diff --git a/forc/src/cli/mod.rs b/forc/src/cli/mod.rs index c4e6139014d..2a4fddce2cb 100644 --- a/forc/src/cli/mod.rs +++ b/forc/src/cli/mod.rs @@ -1,10 +1,11 @@ use self::commands::{ - addr2line, build, clean, completions, deploy, init, json_abi, parse_bytecode, plugins, run, - template, test, update, + addr2line, build, check, clean, completions, deploy, init, json_abi, parse_bytecode, plugins, + run, template, test, update, }; use addr2line::Command as Addr2LineCommand; use anyhow::{anyhow, Result}; pub use build::Command as BuildCommand; +pub use check::Command as CheckCommand; use clap::Parser; pub use clean::Command as CleanCommand; pub use completions::Command as CompletionsCommand; @@ -35,6 +36,7 @@ enum Forc { Addr2Line(Addr2LineCommand), #[clap(visible_alias = "b")] Build(BuildCommand), + Check(CheckCommand), Clean(CleanCommand), Completions(CompletionsCommand), Deploy(DeployCommand), @@ -64,6 +66,7 @@ pub async fn run_cli() -> Result<()> { match opt.command { Forc::Addr2Line(command) => addr2line::exec(command), Forc::Build(command) => build::exec(command), + Forc::Check(command) => check::exec(command), Forc::Clean(command) => clean::exec(command), Forc::Completions(command) => completions::exec(command), Forc::Deploy(command) => deploy::exec(command).await, diff --git a/forc/src/lib.rs b/forc/src/lib.rs index 61e0bf2f81a..3bd176db218 100644 --- a/forc/src/lib.rs +++ b/forc/src/lib.rs @@ -1,6 +1,6 @@ pub mod cli; mod ops; -mod utils; +pub mod utils; #[cfg(feature = "test")] pub mod test { diff --git a/forc/src/ops/forc_build.rs b/forc/src/ops/forc_build.rs index de910ef738a..00f9da19158 100644 --- a/forc/src/ops/forc_build.rs +++ b/forc/src/ops/forc_build.rs @@ -2,13 +2,13 @@ use crate::{ cli::BuildCommand, utils::{SWAY_BIN_HASH_SUFFIX, SWAY_BIN_ROOT_SUFFIX, SWAY_GIT_TAG}, }; -use anyhow::{anyhow, bail, Result}; -use forc_pkg::{self as pkg, lock, Lock, ManifestFile}; -use forc_util::{default_output_directory, lock_path}; +use anyhow::Result; +use forc_pkg::{self as pkg, ManifestFile}; +use forc_util::default_output_directory; use fuel_tx::Contract; use std::{ fs::{self, File}, - path::{Path, PathBuf}, + path::PathBuf, }; use sway_core::TreeType; use tracing::{info, warn}; @@ -25,9 +25,11 @@ pub fn build(command: BuildCommand) -> Result { silent_mode, output_directory, minify_json_abi, + minify_json_storage_slots, locked, build_profile, release, + time_phases, } = command; let key_debug: String = "debug".to_string(); @@ -56,78 +58,28 @@ pub fn build(command: BuildCommand) -> Result { let manifest = ManifestFile::from_dir(&this_dir, SWAY_GIT_TAG)?; - // If any cli parameter is passed by the user it overrides the selected build profile. - let mut config = &pkg::BuildConfig { - print_ir, - print_finalized_asm, - print_intermediate_asm, - silent: silent_mode, - }; - - // Check if any cli parameter is passed by the user if not fetch the build profile from manifest. - if !print_ir && !print_intermediate_asm && !print_finalized_asm && !silent_mode { - config = manifest - .build_profile - .as_ref() - .and_then(|profiles| profiles.get(&selected_build_profile)) - .unwrap_or_else(|| { - warn!( - "provided profile option {} is not present in the manifest file. \ - Using default config.", - selected_build_profile - ); - config - }); - } - - let lock_path = lock_path(manifest.dir()); - - let plan_result = pkg::BuildPlan::from_lock_file(&lock_path, SWAY_GIT_TAG); - - // Retrieve the old lock file state so we can produce a diff. - let old_lock = plan_result - .as_ref() - .ok() - .map(|plan| Lock::from_graph(plan.graph())) - .unwrap_or_default(); - - // Check if there are any errors coming from the BuildPlan generation from the lock file - // If there are errors we will need to create the BuildPlan from scratch, i.e fetch & pin everything - let mut new_lock_cause = None; - let mut plan = plan_result.or_else(|e| -> Result { - if locked { - bail!( - "The lock file {} needs to be updated but --locked was passed to prevent this.", - lock_path.to_string_lossy() + let plan = pkg::BuildPlan::load_from_manifest(&manifest, locked, offline, SWAY_GIT_TAG)?; + + // Retrieve the specified build profile + let mut profile = manifest + .build_profile(&selected_build_profile) + .cloned() + .unwrap_or_else(|| { + warn!( + "provided profile option {} is not present in the manifest file. \ + Using default profile.", + selected_build_profile ); - } - new_lock_cause = if e.to_string().contains("No such file or directory") { - Some(anyhow!("lock file did not exist")) - } else { - Some(e) - }; - let plan = pkg::BuildPlan::new(&manifest, SWAY_GIT_TAG, offline)?; - Ok(plan) - })?; - - // If there are no issues with the BuildPlan generated from the lock file - // Check and apply the diff. - if new_lock_cause.is_none() { - let diff = plan.validate(&manifest, SWAY_GIT_TAG)?; - if !diff.added.is_empty() || !diff.removed.is_empty() { - new_lock_cause = Some(anyhow!("lock file did not match manifest `diff`")); - plan = plan.apply_pkg_diff(diff, SWAY_GIT_TAG, offline)?; - } - } - - if let Some(cause) = new_lock_cause { - info!(" Creating a new `Forc.lock` file. (Cause: {})", cause); - create_new_lock(&plan, &old_lock, &manifest, &lock_path)?; - info!(" Created new lock file at {}", lock_path.display()); - } + Default::default() + }); + profile.print_ir |= print_ir; + profile.print_finalized_asm |= print_finalized_asm; + profile.print_intermediate_asm |= print_intermediate_asm; + profile.silent |= silent_mode; + profile.time_phases |= time_phases; // Build it! - let (compiled, source_map) = pkg::build(&plan, config, SWAY_GIT_TAG)?; + let (compiled, source_map) = pkg::build(&plan, &profile, SWAY_GIT_TAG)?; if let Some(outfile) = binary_outfile { fs::write(&outfile, &compiled.bytecode)?; @@ -165,14 +117,21 @@ pub fn build(command: BuildCommand) -> Result { info!(" Bytecode size is {} bytes.", compiled.bytecode.len()); + // Additional ops required depending on the program type match compiled.tree_type { - TreeType::Script => { - // hash the bytecode for scripts and store the result in a file in the output directory - let bytecode_hash = format!("0x{}", fuel_crypto::Hasher::hash(&compiled.bytecode)); - let hash_file_name = format!("{}{}", &manifest.project.name, SWAY_BIN_HASH_SUFFIX); - let hash_path = output_dir.join(hash_file_name); - fs::write(hash_path, &bytecode_hash)?; - info!(" Script bytecode hash: {}", bytecode_hash); + TreeType::Contract => { + // For contracts, emit a JSON file with all the initialized storage slots. + let json_storage_slots_stem = format!("{}-storage_slots", manifest.project.name); + let json_storage_slots_path = output_dir + .join(&json_storage_slots_stem) + .with_extension("json"); + let file = File::create(json_storage_slots_path)?; + let res = if minify_json_storage_slots { + serde_json::to_writer(&file, &compiled.storage_slots) + } else { + serde_json::to_writer_pretty(&file, &compiled.storage_slots) + }; + res?; } TreeType::Predicate => { // get the root hash of the bytecode for predicates and store the result in a file in the output directory @@ -182,23 +141,16 @@ pub fn build(command: BuildCommand) -> Result { fs::write(root_path, &root)?; info!(" Predicate root: {}", root); } + TreeType::Script => { + // hash the bytecode for scripts and store the result in a file in the output directory + let bytecode_hash = format!("0x{}", fuel_crypto::Hasher::hash(&compiled.bytecode)); + let hash_file_name = format!("{}{}", &manifest.project.name, SWAY_BIN_HASH_SUFFIX); + let hash_path = output_dir.join(hash_file_name); + fs::write(hash_path, &bytecode_hash)?; + info!(" Script bytecode hash: {}", bytecode_hash); + } _ => (), } Ok(compiled) } - -fn create_new_lock( - plan: &pkg::BuildPlan, - old_lock: &Lock, - manifest: &ManifestFile, - lock_path: &Path, -) -> Result<()> { - let lock = Lock::from_graph(plan.graph()); - let diff = lock.diff(old_lock); - lock::print_diff(&manifest.project.name, &diff); - let string = toml::ser::to_string_pretty(&lock) - .map_err(|e| anyhow!("failed to serialize lock file: {}", e))?; - fs::write(&lock_path, &string).map_err(|e| anyhow!("failed to write lock file: {}", e))?; - Ok(()) -} diff --git a/forc/src/ops/forc_check.rs b/forc/src/ops/forc_check.rs new file mode 100644 index 00000000000..be39de30508 --- /dev/null +++ b/forc/src/ops/forc_check.rs @@ -0,0 +1,23 @@ +use crate::{cli::CheckCommand, utils::SWAY_GIT_TAG}; +use anyhow::Result; +use forc_pkg::{self as pkg, ManifestFile}; +use std::path::PathBuf; + +pub fn check(command: CheckCommand) -> Result { + let CheckCommand { + path, + offline_mode: offline, + silent_mode, + locked, + } = command; + + let this_dir = if let Some(ref path) = path { + PathBuf::from(path) + } else { + std::env::current_dir()? + }; + let manifest = ManifestFile::from_dir(&this_dir, SWAY_GIT_TAG)?; + let plan = pkg::BuildPlan::load_from_manifest(&manifest, locked, offline, SWAY_GIT_TAG)?; + + pkg::check(&plan, silent_mode, SWAY_GIT_TAG) +} diff --git a/forc/src/ops/forc_deploy.rs b/forc/src/ops/forc_deploy.rs index 89ac3de6af0..ef2e67ec853 100644 --- a/forc/src/ops/forc_deploy.rs +++ b/forc/src/ops/forc_deploy.rs @@ -6,7 +6,7 @@ use crate::{ use anyhow::{bail, Result}; use forc_pkg::ManifestFile; use fuel_gql_client::client::FuelClient; -use fuel_tx::{Output, Salt, Transaction}; +use fuel_tx::{Output, Salt, StorageSlot, Transaction}; use fuel_vm::prelude::*; use std::path::PathBuf; use sway_core::TreeType; @@ -33,10 +33,12 @@ pub async fn deploy(command: DeployCommand) -> Result { silent_mode, output_directory, minify_json_abi, + minify_json_storage_slots, locked, url, build_profile, release, + time_phases, } = command; let build_command = BuildCommand { @@ -50,9 +52,11 @@ pub async fn deploy(command: DeployCommand) -> Result { silent_mode, output_directory, minify_json_abi, + minify_json_storage_slots, locked, build_profile, release, + time_phases, }; let compiled = forc_build::build(build_command)?; @@ -60,6 +64,7 @@ pub async fn deploy(command: DeployCommand) -> Result { compiled.bytecode, Vec::::new(), Vec::::new(), + compiled.storage_slots, ); let node_url = match &manifest.network { @@ -87,9 +92,10 @@ fn create_contract_tx( compiled_contract: Vec, inputs: Vec, outputs: Vec, + storage_slots: Vec, ) -> (Transaction, fuel_tx::ContractId) { let gas_price = 0; - let gas_limit = fuel_tx::default_parameters::MAX_GAS_PER_TX; + let gas_limit = fuel_tx::ConsensusParameters::default().max_gas_per_tx; let byte_price = 0; let maturity = 0; let bytecode_witness_index = 0; @@ -97,11 +103,16 @@ fn create_contract_tx( let salt = Salt::new([0; 32]); let static_contracts = vec![]; - let storage_slots = vec![]; let contract = Contract::from(compiled_contract); let root = contract.root(); - let state_root = Contract::default_state_root(); + + // The VM currently requires that storage slots are sorted but this shouldn't be neessary. + // Downstream tooling should do the sorting themselves. + // Ref: https://github.com/FuelLabs/fuel-tx/issues/153 + let mut storage_slots = storage_slots; + storage_slots.sort(); + let state_root = Contract::initial_state_root(storage_slots.iter()); let id = contract.id(&salt, &root, &state_root); info!("Contract id: 0x{}", hex::encode(id)); let outputs = [ diff --git a/forc/src/ops/forc_run.rs b/forc/src/ops/forc_run.rs index 413c95f63b0..2e770a3fa8f 100644 --- a/forc/src/ops/forc_run.rs +++ b/forc/src/ops/forc_run.rs @@ -37,9 +37,11 @@ pub async fn run(command: RunCommand) -> Result> { silent_mode: command.silent_mode, output_directory: command.output_directory, minify_json_abi: command.minify_json_abi, + minify_json_storage_slots: command.minify_json_storage_slots, locked: command.locked, build_profile: None, release: false, + time_phases: command.time_phases, }; let compiled = forc_build::build(build_command)?; diff --git a/forc/src/ops/mod.rs b/forc/src/ops/mod.rs index 1d5acc42f28..2f5f87527db 100644 --- a/forc/src/ops/mod.rs +++ b/forc/src/ops/mod.rs @@ -1,5 +1,6 @@ pub mod forc_abi_json; pub mod forc_build; +pub mod forc_check; pub mod forc_clean; pub mod forc_deploy; pub mod forc_init; diff --git a/forc/src/utils/defaults.rs b/forc/src/utils/defaults.rs index 8d5e28ad684..a6f4161ccbe 100644 --- a/forc/src/utils/defaults.rs +++ b/forc/src/utils/defaults.rs @@ -30,8 +30,7 @@ edition = "2021" license = "Apache-2.0" [dependencies] -fuels = "0.15" -fuels-abigen-macro = "0.15" +fuels = {{ version = "0.16", features = ["fuel-core-lib"] }} tokio = {{ version = "1.12", features = ["rt", "macros"] }} [[test]] @@ -93,7 +92,6 @@ pub(crate) fn default_test_program(project_name: &str) -> String { format!( "{}{}{}{}{}", r#"use fuels::{prelude::*, tx::ContractId}; -use fuels_abigen_macro::abigen; // Load abi from json abigen!(MyContract, "out/debug/"#, diff --git a/forc/src/utils/parameters.rs b/forc/src/utils/parameters.rs index 03f268c7536..25da3deadf9 100644 --- a/forc/src/utils/parameters.rs +++ b/forc/src/utils/parameters.rs @@ -8,7 +8,7 @@ pub(crate) struct TxParameters { impl TxParameters { pub const DEFAULT: Self = Self { byte_price: 0, - gas_limit: fuel_tx::default_parameters::MAX_GAS_PER_TX, + gas_limit: fuel_tx::ConsensusParameters::DEFAULT.max_gas_per_tx, gas_price: 0, }; diff --git a/scripts/highlightjs/sway.js b/scripts/highlightjs/sway.js index d089bd81b9f..2158faec472 100644 --- a/scripts/highlightjs/sway.js +++ b/scripts/highlightjs/sway.js @@ -43,6 +43,7 @@ export default function(hljs) { "script", "Self", "self", + "storage", "str", "struct", "trait", diff --git a/sway-core/Cargo.toml b/sway-core/Cargo.toml index 12c4a0eedd3..a9e5bbc8e23 100644 --- a/sway-core/Cargo.toml +++ b/sway-core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sway-core" -version = "0.16.1" +version = "0.16.2" authors = ["Fuel Labs "] edition = "2021" homepage = "https://fuel.network/" @@ -15,8 +15,9 @@ dirs = "3.0" either = "1.6" fuel-asm = "0.5" fuel-crypto = "0.5" +fuel-tx = "0.13" fuel-types = "0.5" -fuel-vm = "0.11" +fuel-vm = "0.12" fuels-types = "0.12" hex = { version = "0.4", optional = true } im = "15.0" @@ -28,10 +29,10 @@ regex = "1" serde = { version = "1.0", features = ["derive"] } sha2 = "0.9" smallvec = "1.7" -sway-ir = { version = "0.16.1", path = "../sway-ir" } -sway-parse = { version = "0.16.1", path = "../sway-parse" } -sway-types = { version = "0.16.1", path = "../sway-types" } -sway-utils = { version = "0.16.1", path = "../sway-utils" } +sway-ir = { version = "0.16.2", path = "../sway-ir" } +sway-parse = { version = "0.16.2", path = "../sway-parse" } +sway-types = { version = "0.16.2", path = "../sway-types" } +sway-utils = { version = "0.16.2", path = "../sway-utils" } thiserror = "1.0" tracing = "0.1" uint = "0.9" diff --git a/sway-core/src/constants.rs b/sway-core/src/constants.rs index e80e434b366..6b1d315e485 100644 --- a/sway-core/src/constants.rs +++ b/sway-core/src/constants.rs @@ -7,9 +7,6 @@ pub const LANGUAGE_NAME: &str = "Sway"; /// The size, in bytes, of a single word in the FuelVM. pub const VM_WORD_SIZE: u64 = 8; -// Keywords -pub const INVALID_NAMES: &[&str] = &["storage"]; - pub const CONTRACT_CALL_GAS_PARAMETER_NAME: &str = "gas"; pub const CONTRACT_CALL_COINS_PARAMETER_NAME: &str = "coins"; diff --git a/sway-core/src/control_flow_analysis/dead_code_analysis.rs b/sway-core/src/control_flow_analysis/dead_code_analysis.rs index 548f4aae180..c1e4b8fca80 100644 --- a/sway-core/src/control_flow_analysis/dead_code_analysis.rs +++ b/sway-core/src/control_flow_analysis/dead_code_analysis.rs @@ -16,29 +16,33 @@ use crate::{ type_engine::{resolve_type, TypeInfo}, CompileError, CompileWarning, Ident, TreeType, Warning, }; +use std::collections::BTreeSet; use sway_types::{span::Span, Spanned}; use crate::semantic_analysis::TypedStorageDeclaration; -use petgraph::algo::has_path_connecting; use petgraph::prelude::NodeIndex; +use petgraph::visit::Dfs; impl ControlFlowGraph { pub(crate) fn find_dead_code(&self) -> Vec { - // dead code is code that has no path to the entry point - let mut dead_nodes = vec![]; - for destination in self.graph.node_indices() { - let mut is_connected = false; - for entry in &self.entry_points { - if has_path_connecting(&self.graph, *entry, destination, None) { - is_connected = true; - break; - } - } - if !is_connected { - dead_nodes.push(destination); + // Dead code is code that has no path to the entry point. + // Collect all connected nodes by traversing from the entries. + // The dead nodes are those we did not collect. + let mut connected = BTreeSet::new(); + let mut dfs = Dfs::empty(&self.graph); + for &entry in &self.entry_points { + dfs.move_to(entry); + while let Some(node) = dfs.next(&self.graph) { + connected.insert(node); } } + let dead_nodes: Vec<_> = self + .graph + .node_indices() + .filter(|n| !connected.contains(n)) + .collect(); + let dead_enum_variant_warnings = dead_nodes .iter() .filter_map(|x| match &self.graph[*x] { @@ -1029,44 +1033,32 @@ fn connect_expression( } fn connect_intrinsic_function( - kind: &TypedIntrinsicFunctionKind, + TypedIntrinsicFunctionKind { + kind, arguments, .. + }: &TypedIntrinsicFunctionKind, graph: &mut ControlFlowGraph, leaves: &[NodeIndex], exit_node: Option, tree_type: &TreeType, ) -> Result, CompileError> { - let result = match kind { - TypedIntrinsicFunctionKind::SizeOfVal { exp } => connect_expression( + let node = graph.add_node(kind.to_string().into()); + for leaf in leaves { + graph.add_edge(*leaf, node, "".into()); + } + let mut result = vec![node]; + let _ = arguments.iter().try_fold(&mut result, |accum, exp| { + let mut res = connect_expression( &(*exp).expression, graph, leaves, exit_node, - "size_of", + "intrinsic", tree_type, exp.span.clone(), - )?, - TypedIntrinsicFunctionKind::SizeOfType { .. } => { - let node = graph.add_node("size of type".into()); - for leaf in leaves { - graph.add_edge(*leaf, node, "".into()); - } - vec![node] - } - TypedIntrinsicFunctionKind::IsRefType { .. } => { - let node = graph.add_node("is ref type".into()); - for leaf in leaves { - graph.add_edge(*leaf, node, "".into()); - } - vec![node] - } - TypedIntrinsicFunctionKind::GetStorageKey => { - let node = graph.add_node("Get storage key".into()); - for leaf in leaves { - graph.add_edge(*leaf, node, "".into()); - } - vec![node] - } - }; + )?; + accum.append(&mut res); + Ok::<_, CompileError>(accum) + })?; Ok(result) } diff --git a/sway-core/src/convert_parse_tree.rs b/sway-core/src/convert_parse_tree.rs index 6eec94e9008..d7874f2ee3b 100644 --- a/sway-core/src/convert_parse_tree.rs +++ b/sway-core/src/convert_parse_tree.rs @@ -1,3 +1,5 @@ +use crate::type_engine::{TraitConstraint, TypeArgument, TypeParameter}; + use { crate::{ constants::{ @@ -8,12 +10,11 @@ use { AbiDeclaration, AsmExpression, AsmOp, AsmRegister, AsmRegisterDeclaration, AstNode, AstNodeContent, CallPath, CodeBlock, ConstantDeclaration, Declaration, EnumDeclaration, EnumVariant, Expression, FunctionDeclaration, FunctionParameter, ImplSelf, ImplTrait, - ImportType, IncludeStatement, IntrinsicFunctionKind, LazyOp, Literal, MatchBranch, - MethodName, ParseTree, Purity, Reassignment, ReassignmentTarget, ReturnStatement, - Scrutinee, StorageDeclaration, StorageField, StructDeclaration, StructExpressionField, - StructField, StructScrutineeField, Supertrait, TraitConstraint, TraitDeclaration, TraitFn, - TreeType, TypeArgument, TypeInfo, TypeParameter, UseStatement, VariableDeclaration, - Visibility, WhileLoop, + ImportType, IncludeStatement, LazyOp, Literal, MatchBranch, MethodName, ParseTree, Purity, + Reassignment, ReassignmentTarget, ReturnStatement, Scrutinee, StorageDeclaration, + StorageField, StructDeclaration, StructExpressionField, StructField, StructScrutineeField, + Supertrait, TraitDeclaration, TraitFn, TreeType, TypeInfo, UseStatement, + VariableDeclaration, Visibility, WhileLoop, }, std::{ collections::HashMap, @@ -101,16 +102,6 @@ pub enum ConvertParseTreeError { GenericsNotSupportedHere { span: Span }, #[error("fully qualified paths are not supported here")] FullyQualifiedPathsNotSupportedHere { span: Span }, - #[error("__size_of does not take arguments")] - SizeOfTooManyArgs { span: Span }, - #[error("__size_of requires exactly one generic argument")] - SizeOfOneGenericArg { span: Span }, - #[error("__is_reference_type does not take arguments")] - IsReferenceTypeTooManyArgs { span: Span }, - #[error("__is_reference_type requires exactly one generic argument")] - IsReferenceTypeOneGenericArg { span: Span }, - #[error("__size_of_val requires exactly one argument")] - SizeOfValOneArg { span: Span }, #[error("tuple index out of range")] TupleIndexOutOfRange { span: Span }, #[error("shift-left expressions are not implemented")] @@ -198,11 +189,6 @@ impl Spanned for ConvertParseTreeError { ConvertParseTreeError::FunctionArbitraryExpression { span } => span.clone(), ConvertParseTreeError::GenericsNotSupportedHere { span } => span.clone(), ConvertParseTreeError::FullyQualifiedPathsNotSupportedHere { span } => span.clone(), - ConvertParseTreeError::SizeOfTooManyArgs { span } => span.clone(), - ConvertParseTreeError::SizeOfOneGenericArg { span } => span.clone(), - ConvertParseTreeError::IsReferenceTypeTooManyArgs { span } => span.clone(), - ConvertParseTreeError::IsReferenceTypeOneGenericArg { span } => span.clone(), - ConvertParseTreeError::SizeOfValOneArg { span } => span.clone(), ConvertParseTreeError::TupleIndexOutOfRange { span } => span.clone(), ConvertParseTreeError::ShlNotImplemented { span } => span.clone(), ConvertParseTreeError::ShrNotImplemented { span } => span.clone(), @@ -1441,118 +1427,38 @@ fn expr_to_expression(ec: &mut ErrorContext, expr: Expr) -> Result { - if call_path.prefixes.is_empty() - && !call_path.is_absolute - && Intrinsic::try_from_str(call_path.suffix.as_str()) - == Some(Intrinsic::SizeOf) - { - if !arguments.is_empty() { - let error = ConvertParseTreeError::SizeOfTooManyArgs { span }; - return Err(ec.error(error)); - } - let ty = match { - generics_opt.and_then(|(_double_colon_token, generic_args)| { - iter_to_array(generic_args.parameters.into_inner()) - }) - } { - Some([ty]) => ty, - None => { - let error = ConvertParseTreeError::SizeOfOneGenericArg { span }; - return Err(ec.error(error)); - } - }; - let type_span = ty.span(); - let type_name = ty_to_type_info(ec, ty)?; - Expression::IntrinsicFunction { - kind: IntrinsicFunctionKind::SizeOfType { - type_name, - type_span, - }, - span, - } - } else if call_path.prefixes.is_empty() - && !call_path.is_absolute - && Intrinsic::try_from_str(call_path.suffix.as_str()) - == Some(Intrinsic::GetStorageKey) - { - if !arguments.is_empty() { - let error = ConvertParseTreeError::GetStorageKeyTooManyArgs { span }; - return Err(ec.error(error)); - } - if generics_opt.is_some() { - let error = ConvertParseTreeError::GenericsNotSupportedHere { span }; - return Err(ec.error(error)); - } - Expression::IntrinsicFunction { - kind: IntrinsicFunctionKind::GetStorageKey, - span, - } - } else if call_path.prefixes.is_empty() - && !call_path.is_absolute - && Intrinsic::try_from_str(call_path.suffix.as_str()) - == Some(Intrinsic::IsReferenceType) - { - if !arguments.is_empty() { - let error = ConvertParseTreeError::IsReferenceTypeTooManyArgs { span }; - return Err(ec.error(error)); - } - let ty = match { - generics_opt.and_then(|(_double_colon_token, generic_args)| { - iter_to_array(generic_args.parameters.into_inner()) - }) - } { - Some([ty]) => ty, - None => { - let error = - ConvertParseTreeError::IsReferenceTypeOneGenericArg { span }; - return Err(ec.error(error)); - } - }; - let type_span = ty.span(); - let type_name = ty_to_type_info(ec, ty)?; - Expression::IntrinsicFunction { - kind: IntrinsicFunctionKind::IsRefType { - type_name, - type_span, - }, - span, - } - } else if call_path.prefixes.is_empty() - && !call_path.is_absolute - && Intrinsic::try_from_str(call_path.suffix.as_str()) - == Some(Intrinsic::SizeOfVal) - { - let exp = match <[_; 1]>::try_from(arguments) { - Ok([exp]) => Box::new(exp), - Err(..) => { - let error = ConvertParseTreeError::SizeOfValOneArg { span }; - return Err(ec.error(error)); - } - }; - Expression::IntrinsicFunction { - kind: IntrinsicFunctionKind::SizeOfVal { exp }, - span, + let type_arguments = match generics_opt { + Some((_double_colon_token, generic_args)) => { + generic_args_to_type_arguments(ec, generic_args)? } - } else { - let type_arguments = match generics_opt { - Some((_double_colon_token, generic_args)) => { - generic_args_to_type_arguments(ec, generic_args)? - } - None => Vec::new(), - }; - if call_path.prefixes.is_empty() { - Expression::FunctionApplication { - name: call_path, + None => Vec::new(), + }; + match Intrinsic::try_from_str(call_path.suffix.as_str()) { + Some(intrinsic) + if call_path.prefixes.is_empty() && !call_path.is_absolute => + { + Expression::IntrinsicFunction { + kind: intrinsic, arguments, type_arguments, span, } - } else { - Expression::DelineatedPath { - call_path, - args: arguments, - type_arguments, - span, + } + _ => { + if call_path.prefixes.is_empty() { + Expression::FunctionApplication { + name: call_path, + arguments, + type_arguments, + span, + } + } else { + Expression::DelineatedPath { + call_path, + args: arguments, + type_arguments, + span, + } } } } @@ -1866,7 +1772,10 @@ fn storage_field_to_storage_field( let storage_field = StorageField { name: storage_field.name, type_info: ty_to_type_info(ec, storage_field.ty)?, - //initializer: expr_to_expression(storage_field.expr), + initializer: storage_field + .initializer + .map(|initializer| expr_to_expression(ec, initializer.1)) + .transpose()?, }; Ok(storage_field) } @@ -2704,10 +2613,10 @@ fn asm_register_declaration_to_asm_register_declaration( ) -> Result { Ok(AsmRegisterDeclaration { name: asm_register_declaration.register, - initializer: match asm_register_declaration.value_opt { - None => None, - Some((_colon_token, expr)) => Some(expr_to_expression(ec, *expr)?), - }, + initializer: asm_register_declaration + .value_opt + .map(|(_colon_token, expr)| expr_to_expression(ec, *expr)) + .transpose()?, }) } @@ -2759,17 +2668,42 @@ fn pattern_to_scrutinee( span, } } - Pattern::Struct { path, fields } => Scrutinee::StructScrutinee { - struct_name: path_expr_to_ident(ec, path)?, - fields: { - fields - .into_inner() - .into_iter() - .map(|field| pattern_struct_field_to_struct_scrutinee_field(ec, field)) - .collect::>()? - }, - span, - }, + Pattern::Struct { path, fields } => { + let mut errors = Vec::new(); + let fields = fields.into_inner(); + + // Make sure each struct field is declared once + let mut names_of_fields = std::collections::HashSet::new(); + fields.clone().into_iter().for_each(|v| { + if let PatternStructField::Field { + field_name, + pattern_opt: _, + } = v + { + if !names_of_fields.insert(field_name.clone()) { + errors.push(ConvertParseTreeError::DuplicateStructField { + name: field_name.clone(), + span: field_name.span(), + }); + } + } + }); + + if let Some(errors) = ec.errors(errors) { + return Err(errors); + } + + let scrutinee_fields = fields + .into_iter() + .map(|field| pattern_struct_field_to_struct_scrutinee_field(ec, field)) + .collect::>()?; + + Scrutinee::StructScrutinee { + struct_name: path_expr_to_ident(ec, path)?, + fields: { scrutinee_fields }, + span, + } + } Pattern::Tuple(pat_tuple) => Scrutinee::Tuple { elems: { pat_tuple @@ -2841,15 +2775,25 @@ fn pattern_struct_field_to_struct_scrutinee_field( pattern_struct_field: PatternStructField, ) -> Result { let span = pattern_struct_field.span(); - let struct_scrutinee_field = StructScrutineeField { - field: pattern_struct_field.field_name, - scrutinee: match pattern_struct_field.pattern_opt { - Some((_colon_token, pattern)) => Some(pattern_to_scrutinee(ec, *pattern)?), - None => None, - }, - span, - }; - Ok(struct_scrutinee_field) + match pattern_struct_field { + PatternStructField::Rest { token } => { + let struct_scrutinee_field = StructScrutineeField::Rest { span: token.span() }; + Ok(struct_scrutinee_field) + } + PatternStructField::Field { + field_name, + pattern_opt, + } => { + let struct_scrutinee_field = StructScrutineeField::Field { + field: field_name, + scrutinee: pattern_opt + .map(|(_colon_token, pattern)| pattern_to_scrutinee(ec, *pattern)) + .transpose()?, + span, + }; + Ok(struct_scrutinee_field) + } + } } fn assignable_to_expression( diff --git a/sway-core/src/error.rs b/sway-core/src/error.rs index ef8da7a4414..ba8a071cee5 100644 --- a/sway-core/src/error.rs +++ b/sway-core/src/error.rs @@ -883,6 +883,13 @@ pub enum CompileError { missing_patterns: String, span: Span, }, + #[error("Pattern does not mention {}: {}", + if missing_fields.len() == 1 { "field" } else { "fields" }, + missing_fields.join(", "))] + MatchStructPatternMissingFields { + missing_fields: Vec, + span: Span, + }, #[error( "Storage attribute access mismatch. Try giving the surrounding function more access by \ adding \"#[{STORAGE_PURITY_ATTRIBUTE_NAME}({attrs})]\" to the function declaration." @@ -988,6 +995,20 @@ pub enum CompileError { NonConstantDeclValue { span: Span }, #[error("Declaring storage in a {program_kind} is not allowed.")] StorageDeclarationInNonContract { program_kind: String, span: Span }, + #[error("Unsupported argument type to intrinsic \"{name}\".")] + IntrinsicUnsupportedArgType { name: String, span: Span }, + #[error("Call to \"{name}\" expects {expected} arguments")] + IntrinsicIncorrectNumArgs { + name: String, + expected: u64, + span: Span, + }, + #[error("Call to \"{name}\" expects {expected} type arguments")] + IntrinsicIncorrectNumTArgs { + name: String, + expected: u64, + span: Span, + }, } impl std::convert::From for CompileError { @@ -1107,6 +1128,7 @@ impl Spanned for CompileError { StarImportShadowsOtherSymbol { name } => name.span(), MatchWrongType { span, .. } => span.clone(), MatchExpressionNonExhaustive { span, .. } => span.clone(), + MatchStructPatternMissingFields { span, .. } => span.clone(), NotAnEnum { span, .. } => span.clone(), StorageAccessMismatch { span, .. } => span.clone(), TraitDeclPureImplImpure { span, .. } => span.clone(), @@ -1143,6 +1165,9 @@ impl Spanned for CompileError { TupleIndexOutOfBounds { span, .. } => span.clone(), NonConstantDeclValue { span } => span.clone(), StorageDeclarationInNonContract { span, .. } => span.clone(), + IntrinsicUnsupportedArgType { span, .. } => span.clone(), + IntrinsicIncorrectNumArgs { span, .. } => span.clone(), + IntrinsicIncorrectNumTArgs { span, .. } => span.clone(), } } } diff --git a/sway-core/src/ir_generation.rs b/sway-core/src/ir_generation.rs index a54be627950..b8254f1b612 100644 --- a/sway-core/src/ir_generation.rs +++ b/sway-core/src/ir_generation.rs @@ -1,9 +1,10 @@ mod compile; -mod const_eval; +pub mod const_eval; mod convert; mod function; mod lexical_map; mod purity; +pub mod storage; mod types; use crate::{ @@ -17,7 +18,7 @@ use sway_types::span::Span; pub(crate) use purity::PurityChecker; pub(crate) fn compile_program(program: TypedProgram) -> Result { - let TypedProgram { kind, root } = program; + let TypedProgram { kind, root, .. } = program; let mut ctx = Context::default(); match kind { diff --git a/sway-core/src/ir_generation/compile.rs b/sway-core/src/ir_generation/compile.rs index bd8598449a2..8affe521be5 100644 --- a/sway-core/src/ir_generation/compile.rs +++ b/sway-core/src/ir_generation/compile.rs @@ -1,7 +1,7 @@ use crate::{ error::CompileError, parse_tree::{Purity, Visibility}, - semantic_analysis::{ast_node::*, *}, + semantic_analysis::{ast_node::*, namespace}, }; use super::{ @@ -63,14 +63,6 @@ fn compile_constants( None } } - - TypedDeclaration::VariableDeclaration(TypedVariableDeclaration { - name, - body, - const_decl_origin, - .. - }) if *const_decl_origin => Some((name, body)), - _otherwise => None, }; @@ -230,6 +222,8 @@ fn compile_fn_with_args( ret_val = Constant::get_unit(context, None); } + let already_returns = compiler.current_block.is_terminated_by_ret(context); + // Another special case: if the last expression in a function is a return then we don't want to // add another implicit return instruction here, as `ret_val` will be unit regardless of the // function return type actually is. This new RET will be going into an unreachable block @@ -239,9 +233,10 @@ fn compile_fn_with_args( // To tell if this is the case we can check that the current block is empty and has no // predecessors (and isn't the entry block which has none by definition), implying the most // recent instruction was a RET. - if compiler.current_block.num_instructions(context) > 0 - || compiler.current_block == compiler.function.get_entry_block(context) - || compiler.current_block.num_predecessors(context) > 0 + if !already_returns + && (compiler.current_block.num_instructions(context) > 0 + || compiler.current_block == compiler.function.get_entry_block(context) + || compiler.current_block.num_predecessors(context) > 0) { if ret_type.eq(context, &Type::Unit) { ret_val = Constant::get_unit(context, None); diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index 81f704dfdeb..97b9eeb834d 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -6,7 +6,7 @@ use crate::{ }, }; -use super::types::*; +use super::{convert::convert_literal_to_constant, types::*}; use sway_ir::{ constant::{Constant, ConstantValue}, @@ -26,6 +26,20 @@ pub(super) fn compile_constant_expression( const_expr: &TypedExpression, ) -> Result { let span_id_idx = MetadataIndex::from_span(context, &const_expr.span); + + let constant_evaluated = compile_constant_expression_to_constant(context, module, const_expr)?; + Ok(Value::new_constant( + context, + constant_evaluated, + span_id_idx, + )) +} + +pub(crate) fn compile_constant_expression_to_constant( + context: &mut Context, + module: Module, + const_expr: &TypedExpression, +) -> Result { let err = match &const_expr.expression { // Special case functions because the span in `const_expr` is to the inlined function // definition, rather than the actual call site. @@ -39,8 +53,8 @@ pub(super) fn compile_constant_expression( }), }; let mut known_consts = MappedStack::::new(); - const_eval_typed_expr(context, module, &mut known_consts, const_expr) - .map_or(err, |c| Ok(Value::new_constant(context, c, span_id_idx))) + + const_eval_typed_expr(context, module, &mut known_consts, const_expr).map_or(err, Ok) } // A HashMap that can hold multiple values and @@ -97,7 +111,7 @@ fn const_eval_typed_expr( expr: &TypedExpression, ) -> Option { match &expr.expression { - TypedExpressionVariant::Literal(l) => Some(super::convert::convert_literal_to_constant(l)), + TypedExpressionVariant::Literal(l) => Some(convert_literal_to_constant(l)), TypedExpressionVariant::FunctionApplication { arguments, function_body, @@ -148,7 +162,7 @@ fn const_eval_typed_expr( TypedExpressionVariant::StructExpression { fields, .. } => { let (field_typs, field_vals): (Vec<_>, Vec<_>) = fields .iter() - .filter_map(|TypedStructExpressionField { name: _, value }| { + .filter_map(|TypedStructExpressionField { name: _, value, .. }| { const_eval_typed_expr(context, module, known_consts, value) .map(|cv| (value.return_type, cv)) }) diff --git a/sway-core/src/ir_generation/convert.rs b/sway-core/src/ir_generation/convert.rs index e79ec305e3a..f29b981f90c 100644 --- a/sway-core/src/ir_generation/convert.rs +++ b/sway-core/src/ir_generation/convert.rs @@ -140,20 +140,3 @@ fn convert_resolved_type( TypeInfo::Storage { .. } => reject_type!("Storage"), }) } - -use uint::construct_uint; - -#[allow( -// These two warnings are generated by the `construct_uint!()` macro below. - clippy::assign_op_pattern, - clippy::ptr_offset_with_cast -)] -pub(super) fn add_to_b256(x: fuel_types::Bytes32, y: u64) -> fuel_types::Bytes32 { - construct_uint! { - struct U256(4); - } - let x = U256::from(*x); - let y = U256::from(y); - let res: [u8; 32] = (x + y).into(); - fuel_types::Bytes32::from(res) -} diff --git a/sway-core/src/ir_generation/function.rs b/sway-core/src/ir_generation/function.rs index dd39a964c07..5a49b8c7947 100644 --- a/sway-core/src/ir_generation/function.rs +++ b/sway-core/src/ir_generation/function.rs @@ -1,16 +1,21 @@ +use super::{ + compile::compile_function, + convert::*, + lexical_map::LexicalMap, + storage::{add_to_b256, get_storage_key}, + types::*, +}; use crate::{ asm_generation::from_ir::ir_type_size_in_bytes, constants, error::CompileError, + ir_generation::const_eval::compile_constant_expression, parse_tree::{AsmOp, AsmRegister, LazyOp, Literal, Purity, Visibility}, semantic_analysis::*, type_engine::{insert_type, resolve_type, TypeId, TypeInfo}, }; - -use super::{compile::compile_function, convert::*, lexical_map::LexicalMap, types::*}; - -use fuel_crypto::Hasher; -use sway_ir::*; +use sway_ir::{Context, *}; +use sway_parse::intrinsics::Intrinsic; use sway_types::{ ident::Ident, span::{Span, Spanned}, @@ -315,14 +320,22 @@ impl FnCompiler { fn compile_intrinsic_function( &mut self, context: &mut Context, - kind: TypedIntrinsicFunctionKind, + TypedIntrinsicFunctionKind { + kind, + arguments, + type_arguments, + span: _, + }: TypedIntrinsicFunctionKind, span: Span, ) -> Result { + // We safely index into arguments and type_arguments arrays below + // because the type-checker ensures that the arguments are all there. match kind { - TypedIntrinsicFunctionKind::SizeOfVal { exp } => { + Intrinsic::SizeOfVal => { + let exp = arguments[0].clone(); // Compile the expression in case of side-effects but ignore its value. let ir_type = convert_resolved_typeid(context, &exp.return_type, &exp.span)?; - self.compile_expression(context, *exp)?; + self.compile_expression(context, exp)?; Ok(Constant::get_uint( context, 64, @@ -330,8 +343,9 @@ impl FnCompiler { None, )) } - TypedIntrinsicFunctionKind::SizeOfType { type_id, type_span } => { - let ir_type = convert_resolved_typeid(context, &type_id, &type_span)?; + Intrinsic::SizeOfType => { + let targ = type_arguments[0].clone(); + let ir_type = convert_resolved_typeid(context, &targ.type_id, &targ.span)?; Ok(Constant::get_uint( context, 64, @@ -339,17 +353,30 @@ impl FnCompiler { None, )) } - TypedIntrinsicFunctionKind::IsRefType { type_id, type_span } => { - let ir_type = convert_resolved_typeid(context, &type_id, &type_span)?; + Intrinsic::IsReferenceType => { + let targ = type_arguments[0].clone(); + let ir_type = convert_resolved_typeid(context, &targ.type_id, &targ.span)?; Ok(Constant::get_bool(context, !ir_type.is_copy_type(), None)) } - TypedIntrinsicFunctionKind::GetStorageKey => { + Intrinsic::GetStorageKey => { let span_md_idx = MetadataIndex::from_span(context, &span); Ok(self .current_block .ins(context) .get_storage_key(span_md_idx, None)) } + Intrinsic::Eq => { + let lhs = arguments[0].clone(); + let rhs = arguments[1].clone(); + let lhs_value = self.compile_expression(context, lhs)?; + let rhs_value = self.compile_expression(context, rhs)?; + Ok(self.current_block.ins(context).cmp( + Predicate::Equal, + lhs_value, + rhs_value, + None, + )) + } } } @@ -369,9 +396,6 @@ impl FnCompiler { self.current_block .ins(context) .ret(ret_value, ret_ty, span_md_idx); - // RET is a terminator so we must create a new block here. If anything is added to - // it then it'll almost certainly be dead code. - self.current_block = self.function.create_block(context, None); Ok(Constant::get_unit(context, span_md_idx)) } } @@ -724,14 +748,15 @@ impl FnCompiler { if codeblock.contents.is_empty() { Some(insert_type(TypeInfo::Tuple(Vec::new()))) } else { - codeblock - .contents - .iter() - .find_map(|node| match &node.content { - TypedAstNodeContent::ReturnStatement(trs) => Some(trs.expr.return_type), - TypedAstNodeContent::ImplicitReturnExpression(te) => Some(te.return_type), - _otherwise => None, - }) + codeblock.contents.iter().find_map(|node| { + match node.gather_return_statements().first() { + Some(TypedReturnStatement { expr }) => Some(expr.return_type), + None => match &node.content { + TypedAstNodeContent::ImplicitReturnExpression(te) => Some(te.return_type), + _otherwise => None, + }, + } + }) } } @@ -765,6 +790,7 @@ impl FnCompiler { self.current_block = true_block_begin; let true_value = self.compile_expression(context, ast_then)?; let true_block_end = self.current_block; + let then_returns = true_block_end.is_terminated_by_ret(context); let false_block_begin = self.function.create_block(context, None); self.current_block = false_block_begin; @@ -773,6 +799,7 @@ impl FnCompiler { Some(expr) => self.compile_expression(context, *expr)?, }; let false_block_end = self.current_block; + let else_returns = false_block_end.is_terminated_by_ret(context); entry_block.ins(context).conditional_branch( cond_value, @@ -782,16 +809,28 @@ impl FnCompiler { cond_span_md_idx, ); + if then_returns && else_returns { + return Ok(Constant::get_unit(context, None)); + } + let merge_block = self.function.create_block(context, None); - true_block_end - .ins(context) - .branch(merge_block, Some(true_value), None); - false_block_end - .ins(context) - .branch(merge_block, Some(false_value), None); + if !then_returns { + true_block_end + .ins(context) + .branch(merge_block, Some(true_value), None); + } + if !else_returns { + false_block_end + .ins(context) + .branch(merge_block, Some(false_value), None); + } self.current_block = merge_block; - Ok(merge_block.get_phi(context)) + if !then_returns || !else_returns { + Ok(merge_block.get_phi(context)) + } else { + Ok(Constant::get_unit(context, None)) + } } fn compile_unsafe_downcast( @@ -982,29 +1021,33 @@ impl FnCompiler { // This is local to the function, so we add it to the locals, rather than the module // globals like other const decls. let TypedConstantDeclaration { name, value, .. } = ast_const_decl; + let const_expr_val = compile_constant_expression(context, self.module, &value)?; + let local_name = self.lexical_map.insert(name.as_str().to_owned()); + let return_type = convert_resolved_typeid(context, &value.return_type, &value.span)?; - if let TypedExpressionVariant::Literal(literal) = &value.expression { - let initialiser = convert_literal_to_constant(literal); - let return_type = convert_resolved_typeid(context, &value.return_type, &value.span)?; - let name = name.as_str().to_owned(); - self.function - .new_local_ptr(context, name.clone(), return_type, false, Some(initialiser)) - .map_err(|ir_error| { - CompileError::InternalOwned(ir_error.to_string(), Span::dummy()) - })?; - - // We still insert this into the symbol table, as itself... can they be shadowed? - // (Hrmm, name resolution in the variable expression code could be smarter about var - // decls vs const decls, for now they're essentially the same...) - self.lexical_map.insert(name); + // We compile consts the same as vars are compiled. This is because ASM generation + // cannot handle + // 1. initializing aggregates + // 2. get_ptr() + // into the data section. + let ptr = self + .function + .new_local_ptr(context, local_name, return_type, false, None) + .map_err(|ir_error| CompileError::InternalOwned(ir_error.to_string(), Span::dummy()))?; - Ok(Constant::get_unit(context, span_md_idx)) - } else { - Err(CompileError::Internal( - "Unsupported constant declaration type, expecting a literal.", - name.span(), - )) + // We can have empty aggregates, especially arrays, which shouldn't be initialised, but + // otherwise use a store. + let ptr_ty = *ptr.get_type(context); + if ir_type_size_in_bytes(context, &ptr_ty) > 0 { + let ptr_val = self + .current_block + .ins(context) + .get_ptr(ptr, ptr_ty, 0, span_md_idx); + self.current_block + .ins(context) + .store(ptr_val, const_expr_val, span_md_idx); } + Ok(const_expr_val) } fn compile_reassignment( @@ -1597,16 +1640,7 @@ impl FnCompiler { Ok(struct_val) } _ => { - // Calculate the storage location hash for the given field - let mut storage_slot_to_hash = format!( - "{}{}", - sway_utils::constants::STORAGE_DOMAIN_SEPARATOR, - ix.to_usize() - ); - for ix in &indices { - storage_slot_to_hash = format!("{}_{}", storage_slot_to_hash, ix); - } - let hashed_storage_slot = Hasher::hash(storage_slot_to_hash); + let storage_key = get_storage_key(ix, &indices); // New name for the key let mut key_name = format!("{}{}", "key_for_", ix.to_usize()); @@ -1626,7 +1660,7 @@ impl FnCompiler { // Const value for the key from the hash let const_key = convert_literal_to_value( context, - &Literal::B256(hashed_storage_slot.into()), + &Literal::B256(storage_key.into()), span_md_idx, ); @@ -1672,7 +1706,7 @@ impl FnCompiler { &indices, &mut key_ptr_val, &key_ptr, - &hashed_storage_slot, + &storage_key, r#type, rhs, span_md_idx, @@ -1792,7 +1826,7 @@ impl FnCompiler { indices: &[u64], key_ptr_val: &mut Value, key_ptr: &Pointer, - hashed_storage_slot: &fuel_types::Bytes32, + storage_key: &fuel_types::Bytes32, r#type: &Type, rhs: &Option, span_md_idx: Option, @@ -1850,7 +1884,7 @@ impl FnCompiler { // Const value for the key from the initial hash + array_index let const_key = convert_literal_to_value( context, - &Literal::B256(*add_to_b256(*hashed_storage_slot, array_index)), + &Literal::B256(*add_to_b256(*storage_key, array_index)), span_md_idx, ); diff --git a/sway-core/src/ir_generation/storage.rs b/sway-core/src/ir_generation/storage.rs new file mode 100644 index 00000000000..31621995bc6 --- /dev/null +++ b/sway-core/src/ir_generation/storage.rs @@ -0,0 +1,234 @@ +use crate::asm_generation::from_ir::ir_type_size_in_bytes; +use fuel_crypto::Hasher; +use fuel_tx::StorageSlot; +use fuel_types::{Bytes32, Bytes8}; +use sway_ir::{ + constant::{Constant, ConstantValue}, + context::Context, + irtype::{AggregateContent, Type}, +}; +use sway_types::state::StateIndex; + +/// Hands out storage keys using a state index and a list of subfield indices. +/// Basically returns sha256("storage____..") +/// +pub(super) fn get_storage_key(ix: &StateIndex, indices: &[T]) -> Bytes32 +where + T: std::fmt::Display, +{ + Hasher::hash(indices.iter().fold( + format!( + "{}{}", + sway_utils::constants::STORAGE_DOMAIN_SEPARATOR, + ix.to_usize() + ), + |acc, i| format!("{}_{}", acc, i), + )) +} + +use uint::construct_uint; + +#[allow( +// These two warnings are generated by the `construct_uint!()` macro below. + clippy::assign_op_pattern, + clippy::ptr_offset_with_cast +)] +pub(super) fn add_to_b256(x: fuel_types::Bytes32, y: u64) -> fuel_types::Bytes32 { + construct_uint! { + struct U256(4); + } + let x = U256::from(*x); + let y = U256::from(y); + let res: [u8; 32] = (x + y).into(); + fuel_types::Bytes32::from(res) +} + +/// Given a constant value `constant`, a type `ty`, a state index, and a vector of subfield +/// indices, serialize the constant into a vector of storage slots. The keys (slots) are +/// generated using the state index and the subfield indices which are recursively built. The +/// values are generated such that each subfield gets its own storage slot except for enums and +/// strings which are spread over successive storage slots (use `serialize_to_words` in this case). +/// +/// This behavior matches the behavior of how storage slots are assigned for storage reads and +/// writes (i.e. how `state_read_*` and `state_write_*` instructions are generated). +/// +pub fn serialize_to_storage_slots( + constant: &Constant, + context: &Context, + ix: &StateIndex, + ty: &Type, + indices: &[usize], +) -> Vec { + match (&ty, &constant.value) { + (_, ConstantValue::Undef) => vec![], + (Type::Unit, ConstantValue::Unit) => vec![StorageSlot::new( + get_storage_key(ix, indices), + Bytes32::new([0; 32]), + )], + (Type::Bool, ConstantValue::Bool(b)) => { + vec![StorageSlot::new( + get_storage_key(ix, indices), + Bytes32::new( + [0; 7] + .iter() + .cloned() + .chain([if *b { 0x01 } else { 0x00 }].iter().cloned()) + .chain([0; 24].iter().cloned()) + .collect::>() + .try_into() + .unwrap(), + ), + )] + } + (Type::Uint(_), ConstantValue::Uint(n)) => { + vec![StorageSlot::new( + get_storage_key(ix, indices), + Bytes32::new( + n.to_be_bytes() + .iter() + .cloned() + .chain([0; 24].iter().cloned()) + .collect::>() + .try_into() + .unwrap(), + ), + )] + } + (Type::B256, ConstantValue::B256(b)) => { + vec![StorageSlot::new( + get_storage_key(ix, indices), + Bytes32::new(*b), + )] + } + (Type::Array(_), ConstantValue::Array(_a)) => { + unimplemented!("Arrays in storage have not been implemented yet.") + } + (Type::Struct(aggregate), ConstantValue::Struct(vec)) => { + match &context.aggregates[aggregate.0] { + AggregateContent::FieldTypes(field_tys) => vec + .iter() + .zip(field_tys.iter()) + .enumerate() + .flat_map(|(i, (f, ty))| { + serialize_to_storage_slots( + f, + context, + ix, + ty, + &indices + .iter() + .cloned() + .chain(vec![i].iter().cloned()) + .collect::>(), + ) + }) + .collect(), + _ => unreachable!("Wrong content for struct."), + } + } + (Type::Union(_), _) | (Type::String(_), _) => { + // Serialize the constant data in words and add zero words until the number of words + // is a multiple of 4. This is useful because each storage slot is 4 words. + let mut packed = serialize_to_words(constant, context, ty); + packed.extend(vec![ + Bytes8::new([0; 8]); + ((packed.len() + 3) / 4) * 4 - packed.len() + ]); + + assert!(packed.len() % 4 == 0); + + // Return a list of `StorageSlot`s + // First get the keys then get the values + (0..(ir_type_size_in_bytes(context, ty) + 31) / 32) + .into_iter() + .map(|i| add_to_b256(get_storage_key(ix, indices), i)) + .zip((0..packed.len() / 4).into_iter().map(|i| { + Bytes32::new( + Vec::from_iter((0..4).into_iter().flat_map(|j| *packed[4 * i + j])) + .try_into() + .unwrap(), + ) + })) + .map(|(k, r)| StorageSlot::new(k, r)) + .collect() + } + _ => vec![], + } +} + +/// Given a constant value `constant` and a type `ty`, serialize the constant into a vector of +/// words and add left padding up to size of `ty`. +/// +pub fn serialize_to_words(constant: &Constant, context: &Context, ty: &Type) -> Vec { + match (&ty, &constant.value) { + (_, ConstantValue::Undef) => vec![], + (Type::Unit, ConstantValue::Unit) => vec![Bytes8::new([0; 8])], + (Type::Bool, ConstantValue::Bool(b)) => { + vec![Bytes8::new( + [0; 7] + .iter() + .cloned() + .chain([if *b { 0x01 } else { 0x00 }].iter().cloned()) + .collect::>() + .try_into() + .unwrap(), + )] + } + (Type::Uint(_), ConstantValue::Uint(n)) => { + vec![Bytes8::new(n.to_be_bytes())] + } + (Type::B256, ConstantValue::B256(b)) => Vec::from_iter( + (0..4) + .into_iter() + .map(|i| Bytes8::new(b[8 * i..8 * i + 8].try_into().unwrap())), + ), + (Type::String(_), ConstantValue::String(s)) => { + // Turn the serialized words (Bytes8) into seriliazed storage slots (Bytes32) + // Pad to word alignment + let mut s = s.clone(); + s.extend(vec![0; ((s.len() + 3) / 4) * 4 - s.len()]); + + assert!(s.len() % 8 == 0); + + // Group into words + Vec::from_iter((0..s.len() / 8).into_iter().map(|i| { + Bytes8::new( + Vec::from_iter((0..8).into_iter().map(|j| s[8 * i + j])) + .try_into() + .unwrap(), + ) + })) + } + (Type::Array(_), ConstantValue::Array(_)) => { + unimplemented!("Arrays in storage have not been implemented yet.") + } + (Type::Struct(aggregate), ConstantValue::Struct(vec)) => { + match &context.aggregates[aggregate.0] { + AggregateContent::FieldTypes(field_tys) => vec + .iter() + .zip(field_tys.iter()) + .flat_map(|(f, ty)| serialize_to_words(f, context, ty)) + .collect(), + _ => unreachable!("Wrong content for struct."), + } + } + (Type::Union(_), _) => { + let value_size_in_words = ir_type_size_in_bytes(context, ty) / 8; + let constant_size_in_words = ir_type_size_in_bytes(context, &constant.ty) / 8; + assert!(value_size_in_words >= constant_size_in_words); + + // Add enough left padding to satisfy the actual size of the union + let padding_size_in_words = value_size_in_words - constant_size_in_words; + vec![Bytes8::new([0; 8]); padding_size_in_words as usize] + .iter() + .cloned() + .chain( + serialize_to_words(constant, context, &constant.ty) + .iter() + .cloned(), + ) + .collect() + } + _ => vec![], + } +} diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index b965cf771fa..ac12974c364 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -255,8 +255,25 @@ pub fn compile_to_ast( return CompileAstResult::Failure { errors, warnings }; } + // Check that all storage initializers can be evaluated at compile time. + let CompileResult { + value: typed_program_with_storage_slots_result, + warnings: new_warnings, + errors: new_errors, + } = typed_program.get_typed_program_with_initialized_storage_slots(); + warnings.extend(new_warnings); + errors.extend(new_errors); + let typed_program_with_storage_slots = match typed_program_with_storage_slots_result { + Some(typed_program_with_storage_slots) => typed_program_with_storage_slots, + None => { + errors = dedup_unsorted(errors); + warnings = dedup_unsorted(warnings); + return CompileAstResult::Failure { errors, warnings }; + } + }; + CompileAstResult::Success { - typed_program: Box::new(typed_program), + typed_program: Box::new(typed_program_with_storage_slots), warnings, } } diff --git a/sway-core/src/parse_tree/declaration.rs b/sway-core/src/parse_tree/declaration.rs index e4bf3ab1cdc..b3dca0f4b61 100644 --- a/sway-core/src/parse_tree/declaration.rs +++ b/sway-core/src/parse_tree/declaration.rs @@ -7,8 +7,6 @@ mod reassignment; mod storage; mod r#struct; mod r#trait; -mod type_argument; -mod type_parameter; mod variable; pub(crate) use abi::*; @@ -20,8 +18,6 @@ pub use r#struct::*; pub use r#trait::*; pub(crate) use reassignment::*; pub use storage::*; -pub(crate) use type_argument::*; -pub(crate) use type_parameter::*; pub use variable::*; #[derive(Debug, Clone)] diff --git a/sway-core/src/parse_tree/declaration/enum.rs b/sway-core/src/parse_tree/declaration/enum.rs index d7d4be0cdf5..5dea55b894b 100644 --- a/sway-core/src/parse_tree/declaration/enum.rs +++ b/sway-core/src/parse_tree/declaration/enum.rs @@ -1,7 +1,4 @@ -use crate::{ - parse_tree::{declaration::TypeParameter, Visibility}, - type_engine::*, -}; +use crate::{parse_tree::Visibility, type_engine::*}; use sway_types::{ident::Ident, span::Span}; diff --git a/sway-core/src/parse_tree/declaration/impl_trait.rs b/sway-core/src/parse_tree/declaration/impl_trait.rs index 010177e347a..14fb70d84c2 100644 --- a/sway-core/src/parse_tree/declaration/impl_trait.rs +++ b/sway-core/src/parse_tree/declaration/impl_trait.rs @@ -1,5 +1,8 @@ -use super::{FunctionDeclaration, TypeParameter}; -use crate::{parse_tree::CallPath, type_engine::TypeInfo}; +use super::FunctionDeclaration; +use crate::{ + parse_tree::CallPath, + type_engine::{TypeInfo, TypeParameter}, +}; use sway_types::span::Span; diff --git a/sway-core/src/parse_tree/declaration/storage.rs b/sway-core/src/parse_tree/declaration/storage.rs index c452e33825e..a70b280e237 100644 --- a/sway-core/src/parse_tree/declaration/storage.rs +++ b/sway-core/src/parse_tree/declaration/storage.rs @@ -1,4 +1,4 @@ -use crate::type_engine::*; +use crate::{parse_tree::Expression, type_engine::*}; use sway_types::{ident::Ident, span::Span}; @@ -18,4 +18,5 @@ pub struct StorageDeclaration { pub struct StorageField { pub name: Ident, pub type_info: TypeInfo, + pub initializer: Option, } diff --git a/sway-core/src/parse_tree/declaration/struct.rs b/sway-core/src/parse_tree/declaration/struct.rs index 1faea405c96..73e39183054 100644 --- a/sway-core/src/parse_tree/declaration/struct.rs +++ b/sway-core/src/parse_tree/declaration/struct.rs @@ -1,6 +1,6 @@ use crate::{ - parse_tree::{declaration::TypeParameter, Visibility}, - type_engine::TypeInfo, + parse_tree::Visibility, + type_engine::{TypeInfo, TypeParameter}, }; use sway_types::{ident::Ident, span::Span}; diff --git a/sway-core/src/parse_tree/expression/intrinsic_function.rs b/sway-core/src/parse_tree/expression/intrinsic_function.rs deleted file mode 100644 index a6568ad8d0f..00000000000 --- a/sway-core/src/parse_tree/expression/intrinsic_function.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::{type_engine::TypeInfo, Expression}; -use sway_types::Span; - -#[derive(Debug, Clone)] -pub enum IntrinsicFunctionKind { - SizeOfVal { - exp: Box, - }, - SizeOfType { - type_name: TypeInfo, - type_span: Span, - }, - IsRefType { - type_name: TypeInfo, - type_span: Span, - }, - GetStorageKey, -} diff --git a/sway-core/src/parse_tree/expression/mod.rs b/sway-core/src/parse_tree/expression/mod.rs index fe099fc83cb..e2b9c3600e1 100644 --- a/sway-core/src/parse_tree/expression/mod.rs +++ b/sway-core/src/parse_tree/expression/mod.rs @@ -1,19 +1,19 @@ use crate::{ parse_tree::{CallPath, Literal}, - CodeBlock, TypeArgument, + type_engine::TypeArgument, + CodeBlock, }; use sway_types::{ident::Ident, Span, Spanned}; mod asm; -mod intrinsic_function; mod match_branch; mod method_name; mod scrutinee; pub(crate) use asm::*; -pub use intrinsic_function::*; pub(crate) use match_branch::MatchBranch; pub use method_name::MethodName; pub use scrutinee::*; +use sway_parse::intrinsics::Intrinsic; /// Represents a parsed, but not yet type checked, [Expression](https://en.wikipedia.org/wiki/Expression_(computer_science)). #[derive(Debug, Clone)] @@ -138,7 +138,9 @@ pub enum Expression { span: Span, }, IntrinsicFunction { - kind: IntrinsicFunctionKind, + kind: Intrinsic, + arguments: Vec, + type_arguments: Vec, span: Span, }, } diff --git a/sway-core/src/parse_tree/expression/scrutinee.rs b/sway-core/src/parse_tree/expression/scrutinee.rs index 620ae2bba4a..cb75b74fe54 100644 --- a/sway-core/src/parse_tree/expression/scrutinee.rs +++ b/sway-core/src/parse_tree/expression/scrutinee.rs @@ -36,10 +36,16 @@ pub enum Scrutinee { } #[derive(Debug, Clone)] -pub struct StructScrutineeField { - pub field: Ident, - pub scrutinee: Option, - pub span: Span, +#[allow(clippy::large_enum_variant)] +pub enum StructScrutineeField { + Rest { + span: Span, + }, + Field { + field: Ident, + scrutinee: Option, + span: Span, + }, } impl Spanned for Scrutinee { @@ -122,9 +128,12 @@ impl Scrutinee { }]; let fields = fields .iter() - .flat_map(|StructScrutineeField { scrutinee, .. }| match scrutinee { - Some(scrutinee) => scrutinee.gather_approximate_typeinfo_dependencies(), - None => vec![], + .flat_map(|f| match f { + StructScrutineeField::Field { + scrutinee: Some(scrutinee), + .. + } => scrutinee.gather_approximate_typeinfo_dependencies(), + _ => vec![], }) .collect::>(); vec![name, fields].concat() diff --git a/sway-core/src/semantic_analysis.rs b/sway-core/src/semantic_analysis.rs index 2594feda433..53802ba85e2 100644 --- a/sway-core/src/semantic_analysis.rs +++ b/sway-core/src/semantic_analysis.rs @@ -4,10 +4,10 @@ mod module; pub mod namespace; mod node_dependencies; mod program; -pub(crate) mod type_check_arguments; +mod type_check_context; pub(crate) use ast_node::*; pub use ast_node::{TypedConstantDeclaration, TypedDeclaration, TypedFunctionDeclaration}; pub use module::{TypedModule, TypedSubmodule}; pub use namespace::Namespace; pub use program::{TypedProgram, TypedProgramKind}; -pub use type_check_arguments::*; +pub(crate) use type_check_context::TypeCheckContext; diff --git a/sway-core/src/semantic_analysis/ast_node/code_block.rs b/sway-core/src/semantic_analysis/ast_node/code_block.rs index 34a02f5c18f..bcc8caf34c8 100644 --- a/sway-core/src/semantic_analysis/ast_node/code_block.rs +++ b/sway-core/src/semantic_analysis/ast_node/code_block.rs @@ -1,5 +1,4 @@ use super::*; -use crate::semantic_analysis::{ast_node::Mode, TypeCheckArguments}; use crate::CodeBlock; #[derive(Clone, Debug, Eq, PartialEq)] @@ -23,41 +22,24 @@ impl DeterministicallyAborts for TypedCodeBlock { impl TypedCodeBlock { pub(crate) fn type_check( - arguments: TypeCheckArguments<'_, CodeBlock>, + mut ctx: TypeCheckContext, + code_block: CodeBlock, ) -> CompileResult<(Self, TypeId)> { let mut warnings = Vec::new(); let mut errors = Vec::new(); - let TypeCheckArguments { - checkee: other, - namespace, - return_type_annotation: type_annotation, - help_text, - self_type, - opts, - .. - } = arguments; - // Create a temp namespace for checking within the code block scope. - let mut code_block_namespace = namespace.clone(); - let evaluated_contents = other + let mut code_block_namespace = ctx.namespace.clone(); + let evaluated_contents = code_block .contents .iter() .filter_map(|node| { - TypedAstNode::type_check(TypeCheckArguments { - checkee: node.clone(), - namespace: &mut code_block_namespace, - return_type_annotation: type_annotation, - help_text, - self_type, - mode: Mode::NonAbi, - opts, - }) - .ok(&mut warnings, &mut errors) + let ctx = ctx.by_ref().scoped(&mut code_block_namespace); + TypedAstNode::type_check(ctx, node.clone()).ok(&mut warnings, &mut errors) }) .collect::>(); - let implicit_return_span = other + let implicit_return_span = code_block .contents .iter() .find_map(|x| match &x.content { @@ -76,32 +58,22 @@ impl TypedCodeBlock { .. }), .. - } => Some(*return_type), + } if !x.deterministically_aborts() => Some(*return_type), _ => None, }); if let Some(return_type) = return_type { - let (mut new_warnings, new_errors) = unify_with_self( - return_type, - type_annotation, - self_type, - &implicit_return_span.unwrap_or_else(|| other.whole_block_span.clone()), - help_text, - ); + let span = implicit_return_span.unwrap_or_else(|| code_block.whole_block_span.clone()); + let (mut new_warnings, new_errors) = ctx.unify_with_self(return_type, &span); warnings.append(&mut new_warnings); errors.append(&mut new_errors.into_iter().map(|x| x.into()).collect()); // The annotation will result in a cast, so set the return type accordingly. } - ok( - ( - TypedCodeBlock { - contents: evaluated_contents, - }, - return_type.unwrap_or_else(|| insert_type(TypeInfo::Tuple(Vec::new()))), - ), - warnings, - errors, - ) + let typed_code_block = TypedCodeBlock { + contents: evaluated_contents, + }; + let type_id = return_type.unwrap_or_else(|| insert_type(TypeInfo::Tuple(Vec::new()))); + ok((typed_code_block, type_id), warnings, errors) } } diff --git a/sway-core/src/semantic_analysis/ast_node/declaration.rs b/sway-core/src/semantic_analysis/ast_node/declaration.rs index 87344b68dc9..80ba1a40d35 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration.rs @@ -2,7 +2,6 @@ mod abi; mod r#enum; mod function; mod impl_trait; -mod monomorphize; mod storage; mod r#struct; mod r#trait; @@ -11,7 +10,6 @@ mod variable; pub use abi::*; pub use function::*; pub use impl_trait::*; -pub(crate) use monomorphize::*; pub use r#enum::*; pub use r#struct::*; pub use r#trait::*; @@ -178,6 +176,14 @@ impl UnresolvedTypeCheck for TypedDeclaration { .flat_map(UnresolvedTypeCheck::check_for_unresolved_types) .collect(), ); + body.append( + &mut decl + .parameters + .iter() + .map(|x| &x.type_id) + .flat_map(UnresolvedTypeCheck::check_for_unresolved_types) + .collect(), + ); body } ConstantDeclaration(TypedConstantDeclaration { value, .. }) => { @@ -381,6 +387,7 @@ impl TypedDeclaration { ) } TypedDeclaration::StructDeclaration(decl) => decl.create_type_id(), + TypedDeclaration::EnumDeclaration(decl) => decl.create_type_id(), TypedDeclaration::Reassignment(TypedReassignment { rhs, .. }) => rhs.return_type, TypedDeclaration::StorageDeclaration(decl) => insert_type(TypeInfo::Storage { fields: decl.fields_as_typed_struct_fields(), diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/abi.rs b/sway-core/src/semantic_analysis/ast_node/declaration/abi.rs index ce85974b14f..3a21f315060 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/abi.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/abi.rs @@ -3,9 +3,12 @@ use sway_types::{Ident, Span}; use crate::{ error::{err, ok}, - semantic_analysis::ast_node::{type_check_interface_surface, type_check_trait_methods}, + semantic_analysis::{ + ast_node::{type_check_interface_surface, type_check_trait_methods}, + TypeCheckContext, + }, type_engine::{insert_type, AbiName, TypeId}, - AbiDeclaration, CompileResult, FunctionDeclaration, Namespace, TypeInfo, + AbiDeclaration, CompileResult, FunctionDeclaration, TypeInfo, }; use super::{CreateTypeId, TypedTraitFn}; @@ -40,10 +43,9 @@ impl CreateTypeId for TypedAbiDeclaration { impl TypedAbiDeclaration { pub(crate) fn type_check( + ctx: TypeCheckContext, abi_decl: AbiDeclaration, - namespace: &mut Namespace, - self_type: TypeId, - ) -> CompileResult { + ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; @@ -60,7 +62,7 @@ impl TypedAbiDeclaration { // so we don't support the case of calling a contract's own interface // from itself. This is by design. let interface_surface = check!( - type_check_interface_surface(interface_surface, namespace), + type_check_interface_surface(interface_surface, ctx.namespace), return err(warnings, errors), warnings, errors @@ -68,7 +70,7 @@ impl TypedAbiDeclaration { // type check these for errors but don't actually use them yet -- the real // ones will be type checked with proper symbols when the ABI is implemented let _methods = check!( - type_check_trait_methods(methods.clone(), namespace, self_type,), + type_check_trait_methods(ctx, methods.clone()), vec![], warnings, errors diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/enum.rs b/sway-core/src/semantic_analysis/ast_node/declaration/enum.rs index fc0895b8459..c1351efc472 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/enum.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/enum.rs @@ -1,10 +1,10 @@ use crate::{ error::*, - namespace::*, parse_tree::*, semantic_analysis::*, type_engine::{ - insert_type, look_up_type_id, CopyTypes, CreateTypeId, ReplaceSelfType, TypeId, TypeMapping, + insert_type, look_up_type_id, CopyTypes, CreateTypeId, EnforceTypeArguments, + MonomorphizeHelper, ReplaceSelfType, TypeId, TypeMapping, TypeParameter, }, types::{JsonAbiString, ToJsonAbi}, TypeInfo, @@ -62,8 +62,6 @@ impl Spanned for TypedEnumDeclaration { } impl MonomorphizeHelper for TypedEnumDeclaration { - type Output = TypedEnumDeclaration; - fn type_parameters(&self) -> &[TypeParameter] { &self.type_parameters } @@ -71,18 +69,10 @@ impl MonomorphizeHelper for TypedEnumDeclaration { fn name(&self) -> &Ident { &self.name } - - fn monomorphize_inner(self, type_mapping: &TypeMapping, namespace: &mut Items) -> Self::Output { - monomorphize_inner(self, type_mapping, namespace) - } } impl TypedEnumDeclaration { - pub fn type_check( - decl: EnumDeclaration, - namespace: &mut Namespace, - self_type: TypeId, - ) -> CompileResult { + pub fn type_check(ctx: TypeCheckContext, decl: EnumDeclaration) -> CompileResult { let mut errors = vec![]; let mut warnings = vec![]; @@ -95,14 +85,15 @@ impl TypedEnumDeclaration { } = decl; // create a namespace for the decl, used to create a scope for generics - let mut namespace = namespace.clone(); + let mut decl_namespace = ctx.namespace.clone(); + let mut ctx = ctx.scoped(&mut decl_namespace); // type check the type parameters // insert them into the namespace let mut new_type_parameters = vec![]; for type_parameter in type_parameters.into_iter() { new_type_parameters.push(check!( - TypeParameter::type_check(type_parameter, &mut namespace), + TypeParameter::type_check(ctx.by_ref(), type_parameter), return err(warnings, errors), warnings, errors @@ -113,12 +104,7 @@ impl TypedEnumDeclaration { let mut variants_buf = vec![]; for variant in variants { variants_buf.push(check!( - TypedEnumVariant::type_check( - variant.clone(), - &mut namespace, - self_type, - variant.span, - ), + TypedEnumVariant::type_check(ctx.by_ref(), variant.clone()), continue, warnings, errors @@ -216,18 +202,15 @@ impl ReplaceSelfType for TypedEnumVariant { impl TypedEnumVariant { pub(crate) fn type_check( + mut ctx: TypeCheckContext, variant: EnumVariant, - namespace: &mut Namespace, - self_type: TypeId, - span: Span, - ) -> CompileResult { + ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; let enum_variant_type = check!( - namespace.resolve_type_with_self( - variant.type_info.clone(), - self_type, - &span, + ctx.resolve_type_with_self( + insert_type(variant.type_info), + &variant.span, EnforceTypeArguments::Yes ), insert_type(TypeInfo::ErrorRecovery), diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/function.rs b/sway-core/src/semantic_analysis/ast_node/declaration/function.rs index 9fd29ec44f6..3a6aeeef018 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/function.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/function.rs @@ -1,9 +1,7 @@ mod function_parameter; pub use function_parameter::*; -use crate::{ - error::*, namespace::*, parse_tree::*, semantic_analysis::*, style::*, type_engine::*, types::*, -}; +use crate::{error::*, parse_tree::*, semantic_analysis::*, style::*, type_engine::*, types::*}; use fuels_types::{Function, Property}; use sha2::{Digest, Sha256}; use sway_types::{Ident, Span, Spanned}; @@ -74,8 +72,6 @@ impl Spanned for TypedFunctionDeclaration { } impl MonomorphizeHelper for TypedFunctionDeclaration { - type Output = TypedFunctionDeclaration; - fn type_parameters(&self) -> &[TypeParameter] { &self.type_parameters } @@ -83,16 +79,6 @@ impl MonomorphizeHelper for TypedFunctionDeclaration { fn name(&self) -> &Ident { &self.name } - - fn monomorphize_inner( - self, - type_mapping: &TypeMapping, - _namespace: &mut Items, - ) -> Self::Output { - let mut new_decl = self; - new_decl.copy_types(type_mapping); - new_decl - } } impl ToJsonAbi for TypedFunctionDeclaration { @@ -121,19 +107,10 @@ impl ToJsonAbi for TypedFunctionDeclaration { } impl TypedFunctionDeclaration { - pub fn type_check( - arguments: TypeCheckArguments<'_, FunctionDeclaration>, - ) -> CompileResult { + pub fn type_check(ctx: TypeCheckContext, fn_decl: FunctionDeclaration) -> CompileResult { let mut warnings = Vec::new(); let mut errors = Vec::new(); - let TypeCheckArguments { - checkee: fn_decl, - namespace, - self_type, - mode, - mut opts, - .. - } = arguments; + let FunctionDeclaration { name, body, @@ -147,17 +124,18 @@ impl TypedFunctionDeclaration { .. } = fn_decl; is_snake_case(&name).ok(&mut warnings, &mut errors); - opts.purity = purity; // create a namespace for the function - let mut namespace = namespace.clone(); + let mut fn_namespace = ctx.namespace.clone(); + + let mut ctx = ctx.scoped(&mut fn_namespace).with_purity(purity); // type check the type parameters // insert them into the namespace let mut new_type_parameters = vec![]; for type_parameter in type_parameters.into_iter() { new_type_parameters.push(check!( - TypeParameter::type_check(type_parameter, &mut namespace), + TypeParameter::type_check(ctx.by_ref(), type_parameter), return err(warnings, errors), warnings, errors @@ -169,7 +147,7 @@ impl TypedFunctionDeclaration { let mut new_parameters = vec![]; for parameter in parameters.into_iter() { new_parameters.push(check!( - TypedFunctionParameter::type_check(parameter, &mut namespace, self_type), + TypedFunctionParameter::type_check(ctx.by_ref(), parameter), continue, warnings, errors @@ -178,9 +156,8 @@ impl TypedFunctionDeclaration { // type check the return type let return_type = check!( - namespace.resolve_type_with_self( - return_type, - self_type, + ctx.resolve_type_with_self( + insert_type(return_type), &return_type_span, EnforceTypeArguments::Yes ), @@ -193,24 +170,21 @@ impl TypedFunctionDeclaration { // // If there are no implicit block returns, then we do not want to type check them, so we // stifle the errors. If there _are_ implicit block returns, we want to type_check them. - let (body, _implicit_block_return) = check!( - TypedCodeBlock::type_check(TypeCheckArguments { - checkee: body, - namespace: &mut namespace, - return_type_annotation: return_type, - help_text: - "Function body's return type does not match up with its return type annotation.", - self_type, - mode: Mode::NonAbi, - opts, - }), - ( - TypedCodeBlock { contents: vec![] }, - insert_type(TypeInfo::ErrorRecovery) - ), - warnings, - errors - ); + let (body, _implicit_block_return) = { + let ctx = ctx + .by_ref() + .with_help_text("Function body's return type does not match up with its return type annotation.") + .with_type_annotation(return_type); + check!( + TypedCodeBlock::type_check(ctx, body), + ( + TypedCodeBlock { contents: vec![] }, + insert_type(TypeInfo::ErrorRecovery) + ), + warnings, + errors + ) + }; // gather the return statements let return_statements: Vec<&TypedExpression> = body @@ -222,13 +196,11 @@ impl TypedFunctionDeclaration { // unify the types of the return statements with the function return type for stmt in return_statements { - let (mut new_warnings, new_errors) = unify_with_self( - stmt.return_type, - return_type, - self_type, - &stmt.span, - "Return statement must return the declared function return type.", - ); + let (mut new_warnings, new_errors) = ctx + .by_ref() + .with_type_annotation(return_type) + .with_help_text("Return statement must return the declared function return type.") + .unify_with_self(stmt.return_type, &stmt.span); warnings.append(&mut new_warnings); errors.append(&mut new_errors.into_iter().map(|x| x.into()).collect()); } @@ -243,7 +215,7 @@ impl TypedFunctionDeclaration { return_type_span, visibility, // if this is for a contract, then it is a contract call - is_contract_call: mode == Mode::ImplAbiFn, + is_contract_call: ctx.mode() == Mode::ImplAbiFn, purity, }; diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/function/function_parameter.rs b/sway-core/src/semantic_analysis/ast_node/declaration/function/function_parameter.rs index 4b940621782..7d917d0552e 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/function/function_parameter.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/function/function_parameter.rs @@ -1,11 +1,11 @@ use crate::{ error::ok, semantic_analysis::{ - EnforceTypeArguments, IsConstant, TypedExpression, TypedExpressionVariant, + IsConstant, TypeCheckContext, TypedExpression, TypedExpressionVariant, TypedVariableDeclaration, VariableMutability, }, type_engine::*, - CompileResult, FunctionParameter, Ident, Namespace, TypedDeclaration, + CompileResult, FunctionParameter, Ident, TypedDeclaration, }; use sway_types::{span::Span, Spanned}; @@ -37,16 +37,14 @@ impl CopyTypes for TypedFunctionParameter { impl TypedFunctionParameter { pub(crate) fn type_check( + mut ctx: TypeCheckContext, parameter: FunctionParameter, - namespace: &mut Namespace, - self_type: TypeId, - ) -> CompileResult { + ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; let type_id = check!( - namespace.resolve_type_with_self( - look_up_type_id(parameter.type_id), - self_type, + ctx.resolve_type_with_self( + parameter.type_id, ¶meter.type_span, EnforceTypeArguments::Yes ), @@ -54,7 +52,7 @@ impl TypedFunctionParameter { warnings, errors, ); - namespace.insert_symbol( + ctx.namespace.insert_symbol( parameter.name.clone(), TypedDeclaration::VariableDeclaration(TypedVariableDeclaration { name: parameter.name.clone(), @@ -69,7 +67,6 @@ impl TypedFunctionParameter { } else { VariableMutability::Immutable }, - const_decl_origin: false, type_ascription: type_id, }), ); diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs b/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs index 325255ebe41..c663935e884 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs @@ -4,12 +4,13 @@ use sway_types::{Ident, Span, Spanned}; use crate::{ error::{err, ok}, - semantic_analysis::{Mode, TCOpts, TypeCheckArguments}, + semantic_analysis::{Mode, TypeCheckContext}, type_engine::{ - insert_type, look_up_type_id, resolve_type, unify_with_self, CopyTypes, TypeId, TypeMapping, + insert_type, look_up_type_id, resolve_type, unify_with_self, CopyTypes, TypeId, + TypeMapping, TypeParameter, }, - CallPath, CompileError, CompileResult, FunctionDeclaration, ImplSelf, ImplTrait, Namespace, - Purity, TypeInfo, TypeParameter, TypedDeclaration, TypedFunctionDeclaration, + CallPath, CompileError, CompileResult, FunctionDeclaration, ImplSelf, ImplTrait, Purity, + TypeInfo, TypedDeclaration, TypedFunctionDeclaration, }; use super::TypedTraitFn; @@ -32,10 +33,9 @@ impl CopyTypes for TypedImplTrait { impl TypedImplTrait { pub(crate) fn type_check_impl_trait( + ctx: TypeCheckContext, impl_trait: ImplTrait, - namespace: &mut Namespace, - opts: TCOpts, - ) -> CompileResult<(TypedImplTrait, TypeId)> { + ) -> CompileResult<(Self, TypeId)> { let mut errors = vec![]; let mut warnings = vec![]; @@ -49,7 +49,8 @@ impl TypedImplTrait { } = impl_trait; // create a namespace for the impl - let mut namespace = namespace.clone(); + let mut impl_namespace = ctx.namespace.clone(); + let mut ctx = ctx.scoped(&mut impl_namespace); // type check the type parameters // insert them into the namespace @@ -57,7 +58,7 @@ impl TypedImplTrait { let mut new_type_parameters = vec![]; for type_parameter in type_parameters.into_iter() { new_type_parameters.push(check!( - TypeParameter::type_check(type_parameter, &mut namespace), + TypeParameter::type_check(ctx.by_ref(), type_parameter), return err(warnings, errors), warnings, errors @@ -66,7 +67,10 @@ impl TypedImplTrait { // type check the type that we are implementing for let implementing_for_type_id = check!( - namespace.resolve_type_without_self(type_implementing_for), + ctx.namespace.resolve_type_without_self( + insert_type(type_implementing_for), + &type_implementing_for_span + ), return err(warnings, errors), warnings, errors @@ -84,7 +88,11 @@ impl TypedImplTrait { errors ); - let impl_trait = match namespace + // Update the context with the new `self` type. + let ctx = ctx.with_self_type(implementing_for_type_id); + + let impl_trait = match ctx + .namespace .resolve_call_path(&trait_name) .ok(&mut warnings, &mut errors) .cloned() @@ -92,16 +100,13 @@ impl TypedImplTrait { Some(TypedDeclaration::TraitDeclaration(tr)) => { let functions_buf = check!( type_check_trait_implementation( + ctx, &tr.interface_surface, &tr.methods, &functions, &trait_name, - &mut namespace, - implementing_for_type_id, &type_implementing_for_span, &block_span, - Mode::NonAbi, - opts, ), return err(warnings, errors), warnings, @@ -136,18 +141,17 @@ impl TypedImplTrait { }); } + let ctx = ctx.with_mode(Mode::ImplAbiFn); + let functions_buf = check!( type_check_trait_implementation( + ctx, &abi.interface_surface, &abi.methods, &functions, &trait_name, - &mut namespace, - implementing_for_type_id, &type_implementing_for_span, &block_span, - Mode::ImplAbiFn, - opts, ), return err(warnings, errors), warnings, @@ -173,10 +177,9 @@ impl TypedImplTrait { } pub(crate) fn type_check_impl_self( + ctx: TypeCheckContext, impl_self: ImplSelf, - namespace: &mut Namespace, - opts: TCOpts, - ) -> CompileResult { + ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; @@ -189,7 +192,8 @@ impl TypedImplTrait { } = impl_self; // create the namespace for the impl - let mut namespace = namespace.clone(); + let mut impl_namespace = ctx.namespace.clone(); + let mut ctx = ctx.scoped(&mut impl_namespace); // create the trait name let trait_name = CallPath { @@ -206,7 +210,7 @@ impl TypedImplTrait { let mut new_type_parameters = vec![]; for type_parameter in type_parameters.into_iter() { new_type_parameters.push(check!( - TypeParameter::type_check(type_parameter, &mut namespace), + TypeParameter::type_check(ctx.by_ref(), type_parameter), return err(warnings, errors), warnings, errors @@ -215,7 +219,10 @@ impl TypedImplTrait { // type check the type that we are implementing for let implementing_for_type_id = check!( - namespace.resolve_type_without_self(type_implementing_for), + ctx.namespace.resolve_type_without_self( + insert_type(type_implementing_for), + &type_implementing_for_span + ), return err(warnings, errors), warnings, errors @@ -233,19 +240,16 @@ impl TypedImplTrait { errors ); + let mut ctx = ctx + .with_self_type(implementing_for_type_id) + .with_help_text("") + .with_type_annotation(insert_type(TypeInfo::Unknown)); + // type check the methods inside of the impl block let mut methods = vec![]; for fn_decl in functions.into_iter() { methods.push(check!( - TypedFunctionDeclaration::type_check(TypeCheckArguments { - checkee: fn_decl, - namespace: &mut namespace, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text: "", - self_type: implementing_for_type_id, - mode: Mode::NonAbi, - opts, - }), + TypedFunctionDeclaration::type_check(ctx.by_ref(), fn_decl), continue, warnings, errors @@ -264,16 +268,13 @@ impl TypedImplTrait { #[allow(clippy::too_many_arguments)] fn type_check_trait_implementation( + mut ctx: TypeCheckContext, trait_interface_surface: &[TypedTraitFn], trait_methods: &[FunctionDeclaration], functions: &[FunctionDeclaration], trait_name: &CallPath, - namespace: &mut Namespace, - self_type: TypeId, self_type_span: &Span, block_span: &Span, - mode: Mode, - opts: TCOpts, ) -> CompileResult> { let mut errors = vec![]; let mut warnings = vec![]; @@ -289,17 +290,14 @@ fn type_check_trait_implementation( .map(|decl| (&decl.name, decl)) .collect(); for fn_decl in functions { + let mut ctx = ctx + .by_ref() + .with_help_text("") + .with_type_annotation(insert_type(TypeInfo::Unknown)); + // type check the function declaration let fn_decl = check!( - TypedFunctionDeclaration::type_check(TypeCheckArguments { - checkee: fn_decl.clone(), - namespace, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text: Default::default(), - self_type, - mode, - opts, - }), + TypedFunctionDeclaration::type_check(ctx.by_ref(), fn_decl.clone()), continue, warnings, errors @@ -353,9 +351,9 @@ fn type_check_trait_implementation( let (mut new_warnings, new_errors) = unify_with_self( fn_decl_param_type, fn_signature_param_type, - self_type, + ctx.self_type(), &fn_signature_param.type_span, - "", + ctx.help_text(), ); warnings.append(&mut new_warnings); if !new_errors.is_empty() { @@ -393,9 +391,9 @@ fn type_check_trait_implementation( let (mut new_warnings, new_errors) = unify_with_self( fn_decl.return_type, fn_signature.return_type, - self_type, + ctx.self_type(), &fn_decl.return_type_span, - "", + ctx.help_text(), ); warnings.append(&mut new_warnings); if !new_errors.is_empty() { @@ -412,7 +410,8 @@ fn type_check_trait_implementation( // This name space is temporary! It is used only so that the below methods // can reference functions from the interface - let mut impl_trait_namespace = namespace.clone(); + let mut impl_trait_namespace = ctx.namespace.clone(); + let ctx = ctx.scoped(&mut impl_trait_namespace); // A trait impl needs access to everything that the trait methods have access to, which is // basically everything in the path where the trait is declared. @@ -420,42 +419,39 @@ fn type_check_trait_implementation( // in the symbols map and the path stored in the CallPath. let trait_path = [ &trait_name.prefixes[..], - impl_trait_namespace.get_canonical_path(&trait_name.suffix), + ctx.namespace.get_canonical_path(&trait_name.suffix), ] .concat(); - impl_trait_namespace.star_import(&trait_path); + ctx.namespace.star_import(&trait_path); - impl_trait_namespace.insert_trait_implementation( + let self_type_id = insert_type(match resolve_type(ctx.self_type(), self_type_span) { + Ok(o) => o, + Err(e) => { + errors.push(e.into()); + return err(warnings, errors); + } + }); + ctx.namespace.insert_trait_implementation( CallPath { prefixes: vec![], suffix: trait_name.suffix.clone(), is_absolute: false, }, - insert_type(match resolve_type(self_type, self_type_span) { - Ok(o) => o, - Err(e) => { - errors.push(e.into()); - return err(warnings, errors); - } - }), + self_type_id, functions_buf.clone(), ); + let mut ctx = ctx + .with_help_text("") + .with_type_annotation(insert_type(TypeInfo::Unknown)); + // type check the methods now that the interface // they depends upon has been implemented // use a local namespace which has the above interface inserted // into it as a trait implementation for this for method in trait_methods { let method = check!( - TypedFunctionDeclaration::type_check(TypeCheckArguments { - checkee: method.clone(), - namespace: &mut impl_trait_namespace, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text: Default::default(), - self_type, - mode, - opts, - }), + TypedFunctionDeclaration::type_check(ctx.by_ref(), method.clone()), continue, warnings, errors diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/monomorphize.rs b/sway-core/src/semantic_analysis/ast_node/declaration/monomorphize.rs deleted file mode 100644 index d38e781c2d1..00000000000 --- a/sway-core/src/semantic_analysis/ast_node/declaration/monomorphize.rs +++ /dev/null @@ -1,203 +0,0 @@ -use sway_types::{Ident, Span, Spanned}; - -use crate::{ - error::*, namespace::*, type_engine::*, CompileError, CompileResult, TypeArgument, TypeInfo, - TypeParameter, -}; - -use super::CreateTypeId; - -/// This type is used to denote if, during monomorphization, the compiler -/// should enforce that type arguments be provided. An example of that -/// might be this: -/// -/// ```ignore -/// struct Point { -/// x: u64, -/// y: u64 -/// } -/// -/// fn add(p1: Point, p2: Point) -> Point { -/// Point { -/// x: p1.x + p2.x, -/// y: p1.y + p2.y -/// } -/// } -/// ``` -/// -/// `EnforeTypeArguments` would require that the type annotations -/// for `p1` and `p2` contain `<...>`. This is to avoid ambiguous definitions: -/// -/// ```ignore -/// fn add(p1: Point, p2: Point) -> Point { -/// Point { -/// x: p1.x + p2.x, -/// y: p1.y + p2.y -/// } -/// } -/// ``` -#[derive(Clone, Copy)] -pub(crate) enum EnforceTypeArguments { - Yes, - No, -} - -pub(crate) trait Monomorphize { - type Output; - - fn monomorphize( - self, - type_arguments: Vec, - enforce_type_arguments: EnforceTypeArguments, - self_type: Option, - call_site_span: Option<&Span>, - namespace: &mut Root, - module_path: &Path, - ) -> CompileResult; -} - -impl Monomorphize for T -where - T: MonomorphizeHelper + Spanned, -{ - type Output = T; - - fn monomorphize( - self, - type_arguments: Vec, - enforce_type_arguments: EnforceTypeArguments, - self_type: Option, - call_site_span: Option<&Span>, - namespace: &mut Root, - module_path: &Path, - ) -> CompileResult { - let mut warnings = vec![]; - let mut errors = vec![]; - match (self.type_parameters().is_empty(), type_arguments.is_empty()) { - (true, true) => ok(self, warnings, errors), - (false, true) => { - if let EnforceTypeArguments::Yes = enforce_type_arguments { - let name_span = self.name().span(); - errors.push(CompileError::NeedsTypeArguments { - name: self.name().clone(), - span: call_site_span.unwrap_or(&name_span).clone(), - }); - return err(warnings, errors); - } - let type_mapping = insert_type_parameters(self.type_parameters()); - let module = check!( - namespace.check_submodule_mut(module_path), - return err(warnings, errors), - warnings, - errors - ); - let new_decl = self.monomorphize_inner(&type_mapping, module); - ok(new_decl, warnings, errors) - } - (true, false) => { - let type_arguments_span = type_arguments - .iter() - .map(|x| x.span.clone()) - .reduce(Span::join) - .unwrap_or_else(|| self.span()); - errors.push(CompileError::DoesNotTakeTypeArguments { - name: self.name().clone(), - span: type_arguments_span, - }); - err(warnings, errors) - } - (false, false) => { - let mut type_arguments = type_arguments; - for type_argument in type_arguments.iter_mut() { - let type_id = match self_type { - Some(self_type) => namespace.resolve_type_with_self( - look_up_type_id(type_argument.type_id), - self_type, - &type_argument.span, - enforce_type_arguments, - module_path, - ), - None => namespace.resolve_type_without_self( - look_up_type_id(type_argument.type_id), - module_path, - ), - }; - type_argument.type_id = check!( - type_id, - insert_type(TypeInfo::ErrorRecovery), - warnings, - errors - ); - } - let type_arguments_span = type_arguments - .iter() - .map(|x| x.span.clone()) - .reduce(Span::join) - .unwrap_or_else(|| self.span()); - if self.type_parameters().len() != type_arguments.len() { - errors.push(CompileError::IncorrectNumberOfTypeArguments { - given: type_arguments.len(), - expected: self.type_parameters().len(), - span: type_arguments_span, - }); - return err(warnings, errors); - } - let type_mapping = insert_type_parameters(self.type_parameters()); - for ((_, interim_type), type_argument) in - type_mapping.iter().zip(type_arguments.iter()) - { - match self_type { - Some(self_type) => { - let (mut new_warnings, new_errors) = unify_with_self( - *interim_type, - type_argument.type_id, - self_type, - &type_argument.span, - "Type argument is not assignable to generic type parameter.", - ); - warnings.append(&mut new_warnings); - errors.append(&mut new_errors.into_iter().map(|x| x.into()).collect()); - } - None => { - let (mut new_warnings, new_errors) = unify( - *interim_type, - type_argument.type_id, - &type_argument.span, - "Type argument is not assignable to generic type parameter.", - ); - warnings.append(&mut new_warnings); - errors.append(&mut new_errors.into_iter().map(|x| x.into()).collect()); - } - } - } - let module = check!( - namespace.check_submodule_mut(module_path), - return err(warnings, errors), - warnings, - errors - ); - let new_decl = self.monomorphize_inner(&type_mapping, module); - ok(new_decl, warnings, errors) - } - } - } -} - -pub(crate) trait MonomorphizeHelper { - type Output; - - fn type_parameters(&self) -> &[TypeParameter]; - fn name(&self) -> &Ident; - fn monomorphize_inner(self, type_mapping: &TypeMapping, namespace: &mut Items) -> Self::Output; -} - -pub(crate) fn monomorphize_inner(decl: T, type_mapping: &TypeMapping, namespace: &mut Items) -> T -where - T: CopyTypes + CreateTypeId, -{ - let old_type_id = decl.create_type_id(); - let mut new_decl = decl; - new_decl.copy_types(type_mapping); - namespace.copy_methods_to_type(old_type_id, new_decl.create_type_id(), type_mapping); - new_decl -} diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/storage.rs b/sway-core/src/semantic_analysis/ast_node/declaration/storage.rs index a410c787f95..6aa55ea6d34 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/storage.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/storage.rs @@ -1,16 +1,19 @@ -use crate::semantic_analysis::{ - TypeCheckedStorageAccess, TypeCheckedStorageAccessDescriptor, TypedStructField, -}; -use crate::type_engine::look_up_type_id; use crate::{ error::*, - type_engine::{TypeId, TypeInfo}, + ir_generation::{ + const_eval::compile_constant_expression_to_constant, storage::serialize_to_storage_slots, + }, + semantic_analysis::{ + TypeCheckedStorageAccess, TypeCheckedStorageAccessDescriptor, TypedExpression, + TypedStructField, + }, + type_engine::{look_up_type_id, TypeId, TypeInfo}, Ident, }; -use sway_types::Spanned; -use sway_types::{state::StateIndex, Span}; - use derivative::Derivative; +use fuel_tx::StorageSlot; +use sway_ir::{Context, Kind, Module}; +use sway_types::{state::StateIndex, Span, Spanned}; #[derive(Clone, Debug, Derivative)] #[derivative(PartialEq, Eq)] @@ -134,6 +137,7 @@ impl TypedStorageDeclaration { ref name, type_id: ref r#type, ref span, + .. }| TypedStructField { name: name.clone(), type_id: *r#type, @@ -142,12 +146,30 @@ impl TypedStorageDeclaration { ) .collect() } + + pub(crate) fn get_initialized_storage_slots(&self) -> CompileResult> { + let mut errors = vec![]; + let storage_slots = self + .fields + .iter() + .enumerate() + .map(|(i, f)| f.get_initialized_storage_slots(&StateIndex::new(i))) + .filter_map(|s| s.map_err(|e| errors.push(e)).ok()) + .flatten() + .collect::>(); + + match errors.is_empty() { + true => ok(storage_slots, vec![], vec![]), + false => err(vec![], errors), + } + } } #[derive(Clone, Debug, Eq)] pub struct TypedStorageField { pub name: Ident, pub type_id: TypeId, + pub initializer: Option, pub(crate) span: Span, } @@ -156,16 +178,41 @@ pub struct TypedStorageField { // https://doc.rust-lang.org/std/collections/struct.HashMap.html impl PartialEq for TypedStorageField { fn eq(&self, other: &Self) -> bool { - self.name == other.name && look_up_type_id(self.type_id) == look_up_type_id(other.type_id) + self.name == other.name + && look_up_type_id(self.type_id) == look_up_type_id(other.type_id) + && self.initializer == other.initializer } } impl TypedStorageField { - pub fn new(name: Ident, r#type: TypeId, span: Span) -> Self { + pub fn new( + name: Ident, + r#type: TypeId, + initializer: Option, + span: Span, + ) -> Self { TypedStorageField { name, type_id: r#type, + initializer, span, } } + + pub fn get_initialized_storage_slots( + &self, + ix: &StateIndex, + ) -> Result, CompileError> { + let mut context = Context::default(); + let module = Module::new(&mut context, Kind::Contract); + match &self.initializer { + None => Ok(vec![]), + Some(initializer) => compile_constant_expression_to_constant( + &mut context, + module, + initializer, + ) + .map(|constant| serialize_to_storage_slots(&constant, &context, ix, &constant.ty, &[])), + } + } } diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/struct.rs b/sway-core/src/semantic_analysis/ast_node/declaration/struct.rs index 4306f5ffc88..0e56fd372f9 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/struct.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/struct.rs @@ -1,6 +1,4 @@ -use crate::{ - error::*, namespace::*, parse_tree::*, semantic_analysis::*, type_engine::*, types::*, -}; +use crate::{error::*, parse_tree::*, semantic_analysis::*, type_engine::*, types::*}; use fuels_types::Property; use std::hash::{Hash, Hasher}; use sway_types::{Ident, Span, Spanned}; @@ -54,8 +52,6 @@ impl Spanned for TypedStructDeclaration { } impl MonomorphizeHelper for TypedStructDeclaration { - type Output = TypedStructDeclaration; - fn type_parameters(&self) -> &[TypeParameter] { &self.type_parameters } @@ -63,18 +59,13 @@ impl MonomorphizeHelper for TypedStructDeclaration { fn name(&self) -> &Ident { &self.name } - - fn monomorphize_inner(self, type_mapping: &TypeMapping, namespace: &mut Items) -> Self::Output { - monomorphize_inner(self, type_mapping, namespace) - } } impl TypedStructDeclaration { pub(crate) fn type_check( + ctx: TypeCheckContext, decl: StructDeclaration, - namespace: &mut Namespace, - self_type: TypeId, - ) -> CompileResult { + ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; @@ -87,14 +78,15 @@ impl TypedStructDeclaration { } = decl; // create a namespace for the decl, used to create a scope for generics - let mut namespace = namespace.clone(); + let mut decl_namespace = ctx.namespace.clone(); + let mut ctx = ctx.scoped(&mut decl_namespace); // type check the type parameters // insert them into the namespace let mut new_type_parameters = vec![]; for type_parameter in type_parameters.into_iter() { new_type_parameters.push(check!( - TypeParameter::type_check(type_parameter, &mut namespace), + TypeParameter::type_check(ctx.by_ref(), type_parameter), return err(warnings, errors), warnings, errors @@ -105,7 +97,7 @@ impl TypedStructDeclaration { let mut new_fields = vec![]; for field in fields.into_iter() { new_fields.push(check!( - TypedStructField::type_check(field, &mut namespace, self_type), + TypedStructField::type_check(ctx.by_ref(), field), return err(warnings, errors), warnings, errors @@ -201,17 +193,12 @@ impl ReplaceSelfType for TypedStructField { } impl TypedStructField { - pub(crate) fn type_check( - field: StructField, - namespace: &mut Namespace, - self_type: TypeId, - ) -> CompileResult { + pub(crate) fn type_check(mut ctx: TypeCheckContext, field: StructField) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; let r#type = check!( - namespace.resolve_type_with_self( - field.type_info, - self_type, + ctx.resolve_type_with_self( + insert_type(field.type_info), &field.type_span, EnforceTypeArguments::Yes ), diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/trait.rs b/sway-core/src/semantic_analysis/ast_node/declaration/trait.rs index c0e1a9d1e31..64936e9d860 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/trait.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/trait.rs @@ -5,10 +5,10 @@ use crate::{ error::{err, ok}, semantic_analysis::{ ast_node::{type_check_interface_surface, type_check_trait_methods}, - Mode, TypedCodeBlock, + Mode, TypeCheckContext, TypedCodeBlock, }, style::is_upper_camel_case, - type_engine::{insert_type, look_up_type_id, CopyTypes, TypeMapping}, + type_engine::{insert_type, CopyTypes, TypeMapping}, CallPath, CompileError, CompileResult, FunctionDeclaration, FunctionParameter, Namespace, Supertrait, TraitDeclaration, TypeInfo, TypedDeclaration, TypedFunctionDeclaration, Visibility, }; @@ -41,9 +41,9 @@ impl CopyTypes for TypedTraitDeclaration { impl TypedTraitDeclaration { pub(crate) fn type_check( + ctx: TypeCheckContext, trait_decl: TraitDeclaration, - namespace: &mut Namespace, - ) -> CompileResult { + ) -> CompileResult { let mut warnings = Vec::new(); let mut errors = Vec::new(); @@ -51,18 +51,19 @@ impl TypedTraitDeclaration { // type check the interface surface let interface_surface = check!( - type_check_interface_surface(trait_decl.interface_surface.to_vec(), namespace), + type_check_interface_surface(trait_decl.interface_surface.to_vec(), ctx.namespace), return err(warnings, errors), warnings, errors ); // A temporary namespace for checking within the trait's scope. - let mut namespace = namespace.clone(); + let mut trait_namespace = ctx.namespace.clone(); + let ctx = ctx.scoped(&mut trait_namespace); // Recursively handle supertraits: make their interfaces and methods available to this trait check!( - handle_supertraits(&trait_decl.supertraits, &mut namespace), + handle_supertraits(&trait_decl.supertraits, ctx.namespace), return err(warnings, errors), warnings, errors @@ -70,7 +71,7 @@ impl TypedTraitDeclaration { // insert placeholder functions representing the interface surface // to allow methods to use those functions - namespace.insert_trait_implementation( + ctx.namespace.insert_trait_implementation( CallPath { prefixes: vec![], suffix: trait_decl.name.clone(), @@ -83,12 +84,9 @@ impl TypedTraitDeclaration { .collect(), ); // check the methods for errors but throw them away and use vanilla [FunctionDeclaration]s + let ctx = ctx.with_self_type(insert_type(TypeInfo::SelfType)); let _methods = check!( - type_check_trait_methods( - trait_decl.methods.clone(), - &mut namespace, - insert_type(TypeInfo::SelfType), - ), + type_check_trait_methods(ctx, trait_decl.methods.clone()), vec![], warnings, errors @@ -205,7 +203,7 @@ fn convert_trait_methods_to_dummy_funcs( is_mutable: *is_mutable, type_id: check!( trait_namespace.resolve_type_with_self( - look_up_type_id(*type_id), + *type_id, insert_type(TypeInfo::SelfType), type_span, EnforceTypeArguments::Yes @@ -221,7 +219,7 @@ fn convert_trait_methods_to_dummy_funcs( span: name.span(), return_type: check!( trait_namespace.resolve_type_with_self( - return_type.clone(), + insert_type(return_type.clone()), insert_type(TypeInfo::SelfType), return_type_span, EnforceTypeArguments::Yes diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs b/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs index 43017159b81..8219279cb57 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/variable.rs @@ -1,4 +1,4 @@ -use crate::{constants::*, error::*, semantic_analysis::*, type_engine::*, Ident, Visibility}; +use crate::{semantic_analysis::*, type_engine::*, Ident, Visibility}; #[derive(Clone, Debug, PartialEq, Eq)] pub enum VariableMutability { @@ -52,7 +52,6 @@ pub struct TypedVariableDeclaration { pub body: TypedExpression, pub(crate) is_mutable: VariableMutability, pub type_ascription: TypeId, - pub(crate) const_decl_origin: bool, } // NOTE: Hash and PartialEq must uphold the invariant: @@ -64,7 +63,6 @@ impl PartialEq for TypedVariableDeclaration { && self.body == other.body && self.is_mutable == other.is_mutable && look_up_type_id(self.type_ascription) == look_up_type_id(other.type_ascription) - && self.const_decl_origin == other.const_decl_origin } } @@ -75,21 +73,3 @@ impl CopyTypes for TypedVariableDeclaration { self.body.copy_types(type_mapping) } } - -// there are probably more names we should check here, this is the only one that will result in an -// actual issue right now, though -pub fn check_if_name_is_invalid(name: &Ident) -> CompileResult<()> { - INVALID_NAMES - .iter() - .find_map(|x| { - if *x == name.as_str() { - Some(err( - vec![], - [CompileError::InvalidVariableName { name: name.clone() }].to_vec(), - )) - } else { - None - } - }) - .unwrap_or_else(|| ok((), vec![], vec![])) -} diff --git a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs index ffa34ff0d7a..1d363ab136a 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs @@ -1,181 +1,243 @@ use std::fmt; +use itertools::Itertools; +use sway_parse::intrinsics::Intrinsic; use sway_types::Span; use crate::{ error::{err, ok}, - semantic_analysis::{EnforceTypeArguments, Mode, TCOpts, TypeCheckArguments}, + semantic_analysis::TypeCheckContext, type_engine::*, types::DeterministicallyAborts, - CompileError, CompileResult, IntrinsicFunctionKind, Namespace, + CompileError, CompileResult, Expression, }; use super::TypedExpression; -#[derive(Debug, Clone)] -pub enum TypedIntrinsicFunctionKind { - SizeOfVal { exp: Box }, - SizeOfType { type_id: TypeId, type_span: Span }, - IsRefType { type_id: TypeId, type_span: Span }, - GetStorageKey, -} - -// NOTE: Hash and PartialEq must uphold the invariant: -// k1 == k2 -> hash(k1) == hash(k2) -// https://doc.rust-lang.org/std/collections/struct.HashMap.html -impl PartialEq for TypedIntrinsicFunctionKind { - fn eq(&self, other: &Self) -> bool { - use TypedIntrinsicFunctionKind::*; - match (self, other) { - (SizeOfVal { exp: l_exp }, SizeOfVal { exp: r_exp }) => *l_exp == *r_exp, - ( - SizeOfType { - type_id: l_type_id, .. - }, - SizeOfType { - type_id: r_type_id, .. - }, - ) => look_up_type_id(*l_type_id) == look_up_type_id(*r_type_id), - ( - IsRefType { - type_id: l_type_id, .. - }, - IsRefType { - type_id: r_type_id, .. - }, - ) => look_up_type_id(*l_type_id) == look_up_type_id(*r_type_id), - (GetStorageKey, GetStorageKey) => true, - _ => false, - } - } +#[derive(Debug, Clone, PartialEq)] +pub struct TypedIntrinsicFunctionKind { + pub kind: Intrinsic, + pub arguments: Vec, + pub type_arguments: Vec, + pub span: Span, } impl CopyTypes for TypedIntrinsicFunctionKind { fn copy_types(&mut self, type_mapping: &TypeMapping) { - use TypedIntrinsicFunctionKind::*; - match self { - SizeOfVal { exp } => { - exp.copy_types(type_mapping); - } - SizeOfType { type_id, type_span } => { - type_id.update_type(type_mapping, type_span); - } - IsRefType { type_id, type_span } => { - type_id.update_type(type_mapping, type_span); - } - GetStorageKey => {} + for arg in &mut self.arguments { + arg.copy_types(type_mapping); + } + for targ in &mut self.type_arguments { + targ.type_id.update_type(type_mapping, &targ.span); } } } impl fmt::Display for TypedIntrinsicFunctionKind { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use TypedIntrinsicFunctionKind::*; - let s = match self { - SizeOfVal { exp } => format!("size_of_val({})", exp), - SizeOfType { type_id, .. } => format!("size_of({})", look_up_type_id(*type_id)), - IsRefType { type_id, .. } => format!("is_ref_type({})", look_up_type_id(*type_id)), - GetStorageKey => "get_storage_key".to_string(), - }; - write!(f, "{}", s) + let targs = self + .type_arguments + .iter() + .map(|targ| look_up_type_id(targ.type_id)) + .join(", "); + let args = self.arguments.iter().map(|e| format!("{}", e)).join(", "); + + write!(f, "{}::<{}>::({})", self.kind, targs, args) } } impl DeterministicallyAborts for TypedIntrinsicFunctionKind { fn deterministically_aborts(&self) -> bool { - use TypedIntrinsicFunctionKind::*; - match self { - SizeOfVal { exp } => exp.deterministically_aborts(), - SizeOfType { .. } | GetStorageKey | IsRefType { .. } => false, - } + self.arguments.iter().any(|x| x.deterministically_aborts()) } } impl UnresolvedTypeCheck for TypedIntrinsicFunctionKind { fn check_for_unresolved_types(&self) -> Vec { - use TypedIntrinsicFunctionKind::*; - match self { - SizeOfVal { exp } => exp.check_for_unresolved_types(), - SizeOfType { type_id, .. } => type_id.check_for_unresolved_types(), - IsRefType { type_id, .. } => type_id.check_for_unresolved_types(), - GetStorageKey => vec![], - } + self.type_arguments + .iter() + .flat_map(|targ| targ.type_id.check_for_unresolved_types()) + .chain( + self.arguments + .iter() + .flat_map(UnresolvedTypeCheck::check_for_unresolved_types), + ) + .collect() } } impl TypedIntrinsicFunctionKind { pub(crate) fn type_check( - kind: IntrinsicFunctionKind, - self_type: TypeId, - namespace: &mut Namespace, - opts: TCOpts, - ) -> CompileResult<(TypedIntrinsicFunctionKind, TypeId)> { + mut ctx: TypeCheckContext, + kind: Intrinsic, + type_arguments: Vec, + arguments: Vec, + span: Span, + ) -> CompileResult<(Self, TypeId)> { let mut warnings = vec![]; let mut errors = vec![]; let (intrinsic_function, return_type) = match kind { - IntrinsicFunctionKind::SizeOfVal { exp } => { + Intrinsic::SizeOfVal => { + if arguments.len() != 1 { + errors.push(CompileError::IntrinsicIncorrectNumArgs { + name: kind.to_string(), + expected: 1, + span, + }); + return err(warnings, errors); + } + let ctx = ctx + .with_help_text("") + .with_type_annotation(insert_type(TypeInfo::Unknown)); let exp = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: *exp, - namespace, - self_type, - mode: Mode::NonAbi, - opts, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text: Default::default(), - }), + TypedExpression::type_check(ctx, arguments[0].clone()), return err(warnings, errors), warnings, errors ); - let intrinsic_function = - TypedIntrinsicFunctionKind::SizeOfVal { exp: Box::new(exp) }; + let intrinsic_function = TypedIntrinsicFunctionKind { + kind, + arguments: vec![exp], + type_arguments: vec![], + span, + }; let return_type = insert_type(TypeInfo::UnsignedInteger(IntegerBits::SixtyFour)); (intrinsic_function, return_type) } - IntrinsicFunctionKind::SizeOfType { - type_name, - type_span, - } => { + Intrinsic::SizeOfType => { + if !arguments.is_empty() { + errors.push(CompileError::IntrinsicIncorrectNumArgs { + name: kind.to_string(), + expected: 0, + span, + }); + return err(warnings, errors); + } + if type_arguments.len() != 1 { + errors.push(CompileError::IntrinsicIncorrectNumTArgs { + name: kind.to_string(), + expected: 1, + span, + }); + return err(warnings, errors); + } + let targ = type_arguments[0].clone(); let type_id = check!( - namespace.resolve_type_with_self( - type_name, - self_type, - &type_span, + ctx.resolve_type_with_self( + insert_type(resolve_type(targ.type_id, &targ.span).unwrap()), + &targ.span, EnforceTypeArguments::Yes ), insert_type(TypeInfo::ErrorRecovery), warnings, errors, ); - let intrinsic_function = - TypedIntrinsicFunctionKind::SizeOfType { type_id, type_span }; + let intrinsic_function = TypedIntrinsicFunctionKind { + kind, + arguments: vec![], + type_arguments: vec![TypeArgument { + type_id, + span: targ.span, + }], + span, + }; let return_type = insert_type(TypeInfo::UnsignedInteger(IntegerBits::SixtyFour)); (intrinsic_function, return_type) } - IntrinsicFunctionKind::IsRefType { - type_name, - type_span, - } => { + Intrinsic::IsReferenceType => { + if type_arguments.len() != 1 { + errors.push(CompileError::IntrinsicIncorrectNumTArgs { + name: kind.to_string(), + expected: 1, + span, + }); + return err(warnings, errors); + } + let targ = type_arguments[0].clone(); let type_id = check!( - namespace.resolve_type_with_self( - type_name, - self_type, - &type_span, + ctx.resolve_type_with_self( + insert_type(resolve_type(targ.type_id, &targ.span).unwrap()), + &targ.span, EnforceTypeArguments::Yes ), insert_type(TypeInfo::ErrorRecovery), warnings, errors, ); - let intrinsic_function = - TypedIntrinsicFunctionKind::IsRefType { type_id, type_span }; + let intrinsic_function = TypedIntrinsicFunctionKind { + kind, + arguments: vec![], + type_arguments: vec![TypeArgument { + type_id, + span: targ.span, + }], + span, + }; (intrinsic_function, insert_type(TypeInfo::Boolean)) } - IntrinsicFunctionKind::GetStorageKey => ( - TypedIntrinsicFunctionKind::GetStorageKey, + Intrinsic::GetStorageKey => ( + TypedIntrinsicFunctionKind { + kind, + arguments: vec![], + type_arguments: vec![], + span, + }, insert_type(TypeInfo::B256), ), + Intrinsic::Eq => { + if arguments.len() != 2 { + errors.push(CompileError::IntrinsicIncorrectNumArgs { + name: kind.to_string(), + expected: 2, + span, + }); + return err(warnings, errors); + } + let mut ctx = ctx + .by_ref() + .with_type_annotation(insert_type(TypeInfo::Unknown)); + + let lhs = arguments[0].clone(); + let lhs = check!( + TypedExpression::type_check(ctx.by_ref(), lhs), + return err(warnings, errors), + warnings, + errors + ); + + // Check for supported argument types + let arg_ty = resolve_type(lhs.return_type, &lhs.span).unwrap(); + let is_valid_arg_ty = matches!(arg_ty, TypeInfo::UnsignedInteger(_)) + || matches!(arg_ty, TypeInfo::Boolean); + if !is_valid_arg_ty { + errors.push(CompileError::IntrinsicUnsupportedArgType { + name: kind.to_string(), + span: lhs.span, + }); + return err(warnings, errors); + } + + let rhs = arguments[1].clone(); + let ctx = ctx + .by_ref() + .with_help_text("Incorrect argument type") + .with_type_annotation(lhs.return_type); + let rhs = check!( + TypedExpression::type_check(ctx, rhs), + return err(warnings, errors), + warnings, + errors + ); + ( + TypedIntrinsicFunctionKind { + kind, + arguments: vec![lhs, rhs], + type_arguments: vec![], + span, + }, + insert_type(TypeInfo::Boolean), + ) + } }; ok((intrinsic_function, return_type), warnings, errors) } diff --git a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/analysis/pattern.rs b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/analysis/pattern.rs index b8ef31573db..26acf90ea09 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/analysis/pattern.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/analysis/pattern.rs @@ -140,20 +140,22 @@ impl Pattern { .. } => { let mut new_fields = vec![]; - for StructScrutineeField { - field, scrutinee, .. - } in fields.into_iter() - { - let f = match scrutinee { - Some(scrutinee) => check!( - Pattern::from_scrutinee(scrutinee), - return err(warnings, errors), - warnings, - errors - ), - None => Pattern::Wildcard, - }; - new_fields.push((field.as_str().to_string(), f)); + for field in fields.into_iter() { + if let StructScrutineeField::Field { + field, scrutinee, .. + } = field + { + let f = match scrutinee { + Some(scrutinee) => check!( + Pattern::from_scrutinee(scrutinee), + return err(warnings, errors), + warnings, + errors + ), + None => Pattern::Wildcard, + }; + new_fields.push((field.as_str().to_string(), f)); + } } Pattern::Struct(StructPattern { struct_name: struct_name.to_string(), diff --git a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_branch.rs b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_branch.rs index dd634a5614a..c0862dc9590 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_branch.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_branch.rs @@ -4,10 +4,10 @@ use crate::{ error::{err, ok}, semantic_analysis::{ ast_node::expression::match_expression::typed::typed_scrutinee::TypedScrutinee, IsConstant, - TypeCheckArguments, TypedAstNode, TypedAstNodeContent, TypedCodeBlock, TypedExpression, + TypeCheckContext, TypedAstNode, TypedAstNodeContent, TypedCodeBlock, TypedExpression, TypedExpressionVariant, TypedVariableDeclaration, VariableMutability, }, - type_engine::{insert_type, unify_with_self}, + type_engine::insert_type, types::DeterministicallyAborts, CompileResult, MatchBranch, TypeInfo, TypedDeclaration, }; @@ -24,21 +24,13 @@ pub(crate) struct TypedMatchBranch { impl TypedMatchBranch { pub(crate) fn type_check( - arguments: TypeCheckArguments<'_, (&TypedExpression, MatchBranch)>, + mut ctx: TypeCheckContext, + typed_value: &TypedExpression, + branch: MatchBranch, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; - let TypeCheckArguments { - checkee: (typed_value, branch), - namespace, - return_type_annotation, - self_type, - opts, - help_text, - mode, - } = arguments; - let MatchBranch { scrutinee, result, @@ -47,7 +39,7 @@ impl TypedMatchBranch { // type check the scrutinee let typed_scrutinee = check!( - TypedScrutinee::type_check(scrutinee, namespace, self_type), + TypedScrutinee::type_check(ctx.by_ref(), scrutinee), return err(warnings, errors), warnings, errors @@ -55,14 +47,15 @@ impl TypedMatchBranch { // calculate the requirements map and the declarations map let (match_req_map, match_decl_map) = check!( - matcher(typed_value, typed_scrutinee, namespace), + matcher(typed_value, typed_scrutinee, ctx.namespace), return err(warnings, errors), warnings, errors ); // create a new namespace for this branch - let mut namespace = namespace.clone(); + let mut namespace = ctx.namespace.clone(); + let mut ctx = ctx.scoped(&mut namespace); // for every item in the declarations map, create a variable declaration, // insert it into the branch namespace, and add it to a block of code statements @@ -75,9 +68,8 @@ impl TypedMatchBranch { body: right_decl, is_mutable: VariableMutability::Immutable, type_ascription, - const_decl_origin: false, }); - namespace.insert_symbol(left_decl, var_decl.clone()); + ctx.namespace.insert_symbol(left_decl, var_decl.clone()); code_block_contents.push(TypedAstNode { content: TypedAstNodeContent::Declaration(var_decl), span, @@ -85,30 +77,22 @@ impl TypedMatchBranch { } // type check the branch result - let typed_result = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: result, - namespace: &mut namespace, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text, - self_type, - mode, - opts, - }), - return err(warnings, errors), - warnings, - errors - ); + let typed_result = { + let ctx = ctx + .by_ref() + .with_type_annotation(insert_type(TypeInfo::Unknown)); + check!( + TypedExpression::type_check(ctx, result), + return err(warnings, errors), + warnings, + errors + ) + }; // unify the return type from the typed result with the type annotation if !typed_result.deterministically_aborts() { - let (mut new_warnings, new_errors) = unify_with_self( - typed_result.return_type, - return_type_annotation, - self_type, - &typed_result.span, - help_text, - ); + let (mut new_warnings, new_errors) = + ctx.unify_with_self(typed_result.return_type, &typed_result.span); warnings.append(&mut new_warnings); errors.append(&mut new_errors.into_iter().map(|x| x.into()).collect()); } diff --git a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_expression.rs index 431f7ae4735..74cc4a1ba17 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_expression.rs @@ -6,8 +6,7 @@ use crate::{ ast_node::expression::typed_expression::{ instantiate_if_expression, instantiate_lazy_operator, }, - namespace::Namespace, - IsConstant, TypeCheckArguments, TypedExpression, TypedExpressionVariant, + IsConstant, TypeCheckContext, TypedExpression, TypedExpressionVariant, }, type_engine::{insert_type, TypeId}, CompileError, CompileResult, LazyOp, Literal, MatchBranch, TypeInfo, @@ -25,35 +24,21 @@ pub(crate) struct TypedMatchExpression { impl TypedMatchExpression { pub(crate) fn type_check( - arguments: TypeCheckArguments<'_, (TypedExpression, Vec)>, + ctx: TypeCheckContext, + typed_value: TypedExpression, + branches: Vec, span: Span, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; - let TypeCheckArguments { - checkee: (typed_value, branches), - namespace, - return_type_annotation, - self_type, - opts, - mode, - .. - } = arguments; - // type check all of the branches let mut typed_branches = vec![]; + let mut ctx = + ctx.with_help_text("all branches of a match statement must return the same type"); for branch in branches.into_iter() { let typed_branch = check!( - TypedMatchBranch::type_check(TypeCheckArguments { - checkee: (&typed_value, branch), - namespace, - return_type_annotation, - help_text: "all branches of a match statement must return the same type", - self_type, - mode, - opts, - }), + TypedMatchBranch::type_check(ctx.by_ref(), &typed_value, branch), continue, warnings, errors @@ -67,7 +52,7 @@ impl TypedMatchExpression { let exp = TypedMatchExpression { branches: typed_branches, - return_type_id: return_type_annotation, + return_type_id: ctx.type_annotation(), span, }; ok(exp, warnings, errors) @@ -75,8 +60,7 @@ impl TypedMatchExpression { pub(crate) fn convert_to_typed_if_expression( self, - namespace: &mut Namespace, - self_type: TypeId, + mut ctx: TypeCheckContext, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; @@ -95,13 +79,9 @@ impl TypedMatchExpression { let mut conditional: Option = None; for (left_req, right_req) in conditions.into_iter().rev() { let joined_span = Span::join(left_req.span.clone(), right_req.span.clone()); + let args = vec![left_req, right_req]; let new_condition = check!( - TypedExpression::core_ops_eq( - vec![left_req, right_req], - joined_span, - namespace, - self_type - ), + TypedExpression::core_ops_eq(ctx.by_ref(), args, joined_span), continue, warnings, errors @@ -135,7 +115,7 @@ impl TypedMatchExpression { Some(result), // TODO: this is a really bad hack and we should not do this result_span, self.return_type_id, // TODO: figure out if this argument matters or not - self_type + ctx.self_type() ), continue, warnings, @@ -156,7 +136,7 @@ impl TypedMatchExpression { Some(prev_if_exp), result_span, self.return_type_id, - self_type + ctx.self_type() ), continue, warnings, @@ -171,7 +151,7 @@ impl TypedMatchExpression { Some(prev_if_exp), result_span, self.return_type_id, - self_type + ctx.self_type() ), continue, warnings, diff --git a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_scrutinee.rs b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_scrutinee.rs index 62489f31118..cddbaa66b5c 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_scrutinee.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_scrutinee.rs @@ -1,14 +1,10 @@ use sway_types::{Ident, Span, Spanned}; -use crate::semantic_analysis::declaration::EnforceTypeArguments; -use crate::semantic_analysis::namespace::Namespace; -use crate::semantic_analysis::TypedEnumVariant; -use crate::type_engine::CreateTypeId; -use crate::CompileError; use crate::{ error::{err, ok}, - type_engine::{insert_type, TypeId}, - CompileResult, Literal, Scrutinee, TypeArgument, TypeInfo, + semantic_analysis::{TypeCheckContext, TypedEnumVariant}, + type_engine::{insert_type, CreateTypeId, EnforceTypeArguments, TypeArgument, TypeId}, + CompileError, CompileResult, Literal, Scrutinee, StructScrutineeField, TypeInfo, }; #[derive(Debug, Clone)] @@ -41,9 +37,8 @@ pub(crate) struct TypedStructScrutineeField { impl TypedScrutinee { pub(crate) fn type_check( + mut ctx: TypeCheckContext, scrutinee: Scrutinee, - namespace: &mut Namespace, - self_type: TypeId, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; @@ -70,25 +65,24 @@ impl TypedScrutinee { } => { // find the struct definition from the name let unknown_decl = check!( - namespace.resolve_symbol(&struct_name).cloned(), + ctx.namespace.resolve_symbol(&struct_name).cloned(), return err(warnings, errors), warnings, errors ); - let struct_decl = check!( + let mut struct_decl = check!( unknown_decl.expect_struct().cloned(), return err(warnings, errors), warnings, errors ); // monomorphize the struct definition - let struct_decl = check!( - namespace.monomorphize( - struct_decl, + check!( + ctx.monomorphize( + &mut struct_decl, vec!(), EnforceTypeArguments::No, - Some(self_type), - None + &struct_name.span() ), return err(warnings, errors), warnings, @@ -96,29 +90,55 @@ impl TypedScrutinee { ); // type check the fields let mut typed_fields = vec![]; + let mut rest_pattern = None; for field in fields.into_iter() { - // ensure that the struct definition has this field - let _ = check!( - struct_decl.expect_field(&field.field), - return err(warnings, errors), - warnings, - errors - ); - // type check the nested scrutinee - let typed_scrutinee = match field.scrutinee { - None => None, - Some(scrutinee) => Some(check!( - TypedScrutinee::type_check(scrutinee, namespace, self_type), - return err(warnings, errors), - warnings, - errors - )), - }; - typed_fields.push(TypedStructScrutineeField { - field: field.field, - scrutinee: typed_scrutinee, - span: field.span, + match field { + StructScrutineeField::Rest { .. } => rest_pattern = Some(field), + StructScrutineeField::Field { + field, + scrutinee, + span, + } => { + // ensure that the struct definition has this field + let _ = check!( + struct_decl.expect_field(&field), + return err(warnings, errors), + warnings, + errors + ); + // type check the nested scrutinee + let typed_scrutinee = match scrutinee { + None => None, + Some(scrutinee) => Some(check!( + TypedScrutinee::type_check(ctx.by_ref(), scrutinee), + return err(warnings, errors), + warnings, + errors + )), + }; + typed_fields.push(TypedStructScrutineeField { + field, + scrutinee: typed_scrutinee, + span, + }); + } + } + } + // ensure that the pattern uses all fields of the struct unless the rest pattern is present + if (struct_decl.fields.len() != typed_fields.len()) && rest_pattern.is_none() { + let missing_fields = struct_decl + .fields + .iter() + .filter(|f| !typed_fields.iter().any(|tf| f.name == tf.field)) + .map(|f| f.name.to_string()) + .collect::>(); + + errors.push(CompileError::MatchStructPatternMissingFields { + span, + missing_fields, }); + + return err(warnings, errors); } TypedScrutinee { variant: TypedScrutineeVariant::StructScrutinee(typed_fields), @@ -144,25 +164,24 @@ impl TypedScrutinee { let variant_name = call_path.suffix; // find the enum definition from the name let unknown_decl = check!( - namespace.resolve_symbol(enum_name).cloned(), + ctx.namespace.resolve_symbol(enum_name).cloned(), return err(warnings, errors), warnings, errors ); - let enum_decl = check!( + let mut enum_decl = check!( unknown_decl.expect_enum().cloned(), return err(warnings, errors), warnings, errors ); // monomorphize the enum definition - let enum_decl = check!( - namespace.monomorphize( - enum_decl, + check!( + ctx.monomorphize( + &mut enum_decl, vec!(), EnforceTypeArguments::No, - Some(self_type), - None + &enum_name.span() ), return err(warnings, errors), warnings, @@ -178,7 +197,7 @@ impl TypedScrutinee { ); // type check the nested scrutinee let typed_value = check!( - TypedScrutinee::type_check(*value, namespace, self_type), + TypedScrutinee::type_check(ctx, *value), return err(warnings, errors), warnings, errors @@ -196,7 +215,7 @@ impl TypedScrutinee { let mut typed_elems = vec![]; for elem in elems.into_iter() { typed_elems.push(check!( - TypedScrutinee::type_check(elem, namespace, self_type), + TypedScrutinee::type_check(ctx.by_ref(), elem), continue, warnings, errors diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index 3becc8e00c2..71873e87c5d 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -16,6 +16,7 @@ use crate::{ error::*, parse_tree::*, semantic_analysis::*, type_engine::*, types::DeterministicallyAborts, }; +use sway_parse::intrinsics::Intrinsic; use sway_types::{Ident, Span, Spanned}; use std::{ @@ -65,128 +66,151 @@ impl fmt::Display for TypedExpression { impl UnresolvedTypeCheck for TypedExpression { fn check_for_unresolved_types(&self) -> Vec { use TypedExpressionVariant::*; + let mut res = self.return_type.check_for_unresolved_types(); match &self.expression { FunctionApplication { arguments, function_body, .. } => { - let mut args = arguments - .iter() - .map(|x| &x.1) - .flat_map(UnresolvedTypeCheck::check_for_unresolved_types) - .collect::>(); - args.append( + res.append( + &mut arguments + .iter() + .map(|x| &x.1) + .flat_map(UnresolvedTypeCheck::check_for_unresolved_types) + .collect::>(), + ); + res.append( &mut function_body .contents .iter() .flat_map(UnresolvedTypeCheck::check_for_unresolved_types) .collect(), ); - args } - // expressions don't ever have return types themselves, they're stored in - // `TypedExpression::return_type`. Variable expressions are just names of variables. - VariableExpression { .. } => vec![], - Tuple { fields } => fields - .iter() - .flat_map(|x| x.check_for_unresolved_types()) - .collect(), - AsmExpression { registers, .. } => registers - .iter() - .filter_map(|x| x.initializer.as_ref()) - .flat_map(UnresolvedTypeCheck::check_for_unresolved_types) - .collect::>(), - StructExpression { fields, .. } => fields - .iter() - .flat_map(|x| x.value.check_for_unresolved_types()) - .collect(), - LazyOperator { lhs, rhs, .. } => lhs - .check_for_unresolved_types() - .into_iter() - .chain(rhs.check_for_unresolved_types().into_iter()) - .collect(), - Array { contents } => contents - .iter() - .flat_map(|x| x.check_for_unresolved_types()) - .collect(), - ArrayIndex { prefix, .. } => prefix.check_for_unresolved_types(), - CodeBlock(block) => block - .contents - .iter() - .flat_map(UnresolvedTypeCheck::check_for_unresolved_types) - .collect(), + Tuple { fields } => { + res.append( + &mut fields + .iter() + .flat_map(|x| x.check_for_unresolved_types()) + .collect(), + ); + } + AsmExpression { registers, .. } => { + res.append( + &mut registers + .iter() + .filter_map(|x| x.initializer.as_ref()) + .flat_map(UnresolvedTypeCheck::check_for_unresolved_types) + .collect::>(), + ); + } + StructExpression { fields, .. } => { + res.append( + &mut fields + .iter() + .flat_map(|x| x.value.check_for_unresolved_types()) + .collect(), + ); + } + LazyOperator { lhs, rhs, .. } => { + res.append(&mut lhs.check_for_unresolved_types()); + res.append(&mut rhs.check_for_unresolved_types()); + } + Array { contents } => { + res.append( + &mut contents + .iter() + .flat_map(|x| x.check_for_unresolved_types()) + .collect(), + ); + } + ArrayIndex { prefix, index } => { + res.append(&mut prefix.check_for_unresolved_types()); + res.append(&mut index.check_for_unresolved_types()); + } + CodeBlock(block) => { + res.append( + &mut block + .contents + .iter() + .flat_map(UnresolvedTypeCheck::check_for_unresolved_types) + .collect(), + ); + } IfExp { condition, then, r#else, } => { - let mut buf = condition - .check_for_unresolved_types() - .into_iter() - .chain(then.check_for_unresolved_types().into_iter()) - .collect::>(); + res.append(&mut condition.check_for_unresolved_types()); + res.append(&mut then.check_for_unresolved_types()); if let Some(r#else) = r#else { - buf.append(&mut r#else.check_for_unresolved_types()); + res.append(&mut r#else.check_for_unresolved_types()); } - buf } StructFieldAccess { prefix, resolved_type_of_parent, .. - } => prefix - .check_for_unresolved_types() - .into_iter() - .chain( - resolved_type_of_parent - .check_for_unresolved_types() - .into_iter(), - ) - .collect(), + } => { + res.append(&mut prefix.check_for_unresolved_types()); + res.append(&mut resolved_type_of_parent.check_for_unresolved_types()); + } TupleElemAccess { prefix, resolved_type_of_parent, .. - } => prefix - .check_for_unresolved_types() - .into_iter() - .chain( - resolved_type_of_parent - .check_for_unresolved_types() - .into_iter(), - ) - .collect(), + } => { + res.append(&mut prefix.check_for_unresolved_types()); + res.append(&mut resolved_type_of_parent.check_for_unresolved_types()); + } EnumInstantiation { enum_decl, contents, .. } => { - let mut buf = if let Some(contents) = contents { - contents.check_for_unresolved_types().into_iter().collect() - } else { - vec![] - }; - buf.append( + if let Some(contents) = contents { + res.append(&mut contents.check_for_unresolved_types().into_iter().collect()); + } + res.append( &mut enum_decl .variants .iter() .flat_map(|x| x.type_id.check_for_unresolved_types()) .collect(), ); - buf + res.append( + &mut enum_decl + .type_parameters + .iter() + .flat_map(|x| x.type_id.check_for_unresolved_types()) + .collect(), + ); + } + AbiCast { address, .. } => { + res.append(&mut address.check_for_unresolved_types()); + } + IntrinsicFunction(kind) => { + res.append(&mut kind.check_for_unresolved_types()); + } + EnumTag { exp } => { + res.append(&mut exp.check_for_unresolved_types()); + } + UnsafeDowncast { exp, variant } => { + res.append(&mut exp.check_for_unresolved_types()); + res.append(&mut variant.type_id.check_for_unresolved_types()); } - AbiCast { address, .. } => address.check_for_unresolved_types(), // storage access can never be generic - StorageAccess { .. } | Literal(_) | AbiName(_) | FunctionParameter => vec![], - IntrinsicFunction(kind) => kind.check_for_unresolved_types(), - EnumTag { exp } => exp.check_for_unresolved_types(), - UnsafeDowncast { exp, variant } => exp - .check_for_unresolved_types() - .into_iter() - .chain(variant.type_id.check_for_unresolved_types().into_iter()) - .collect(), + // variable expressions don't ever have return types themselves, they're stored in + // `TypedExpression::return_type`. Variable expressions are just names of variables. + VariableExpression { .. } + | StorageAccess { .. } + | Literal(_) + | AbiName(_) + | FunctionParameter => {} } + res } } @@ -273,10 +297,9 @@ pub(crate) fn error_recovery_expr(span: Span) -> TypedExpression { #[allow(clippy::too_many_arguments)] impl TypedExpression { pub(crate) fn core_ops_eq( + ctx: TypeCheckContext, arguments: Vec, span: Span, - namespace: &mut Namespace, - self_type: TypeId, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; @@ -297,14 +320,7 @@ impl TypedExpression { }; let arguments = VecDeque::from(arguments); let method = check!( - resolve_method_name( - &method_name, - arguments.clone(), - vec![], - span.clone(), - namespace, - self_type, - ), + resolve_method_name(ctx, &method_name, arguments.clone(), vec![], span.clone()), return err(warnings, errors), warnings, errors @@ -370,21 +386,12 @@ impl TypedExpression { } } - pub(crate) fn type_check(arguments: TypeCheckArguments<'_, Expression>) -> CompileResult { - let TypeCheckArguments { - checkee: other, - namespace, - return_type_annotation: type_annotation, - help_text, - self_type, - opts, - .. - } = arguments; - let expr_span = other.span(); - let res = match other { + pub(crate) fn type_check(mut ctx: TypeCheckContext, expr: Expression) -> CompileResult { + let expr_span = expr.span(); + let res = match expr { Expression::Literal { value: lit, span } => Self::type_check_literal(lit, span), Expression::VariableExpression { name, span, .. } => { - Self::type_check_variable_expression(name, span, namespace) + Self::type_check_variable_expression(ctx.namespace, name, span) } Expression::FunctionApplication { name, @@ -393,38 +400,21 @@ impl TypedExpression { type_arguments, .. } => Self::type_check_function_application( - TypeCheckArguments { - checkee: (name, arguments, type_arguments), - namespace, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text, - self_type, - mode: Mode::NonAbi, - opts, - }, - span, - ), - Expression::LazyOperator { op, lhs, rhs, span } => Self::type_check_lazy_operator( - TypeCheckArguments { - checkee: (op, *lhs, *rhs), - return_type_annotation: insert_type(TypeInfo::Boolean), - namespace, - help_text, - self_type, - mode: Mode::NonAbi, - opts, - }, - span, - ), - Expression::CodeBlock { contents, span, .. } => Self::type_check_code_block( - contents, + ctx.by_ref(), + name, + arguments, + type_arguments, span, - namespace, - type_annotation, - help_text, - self_type, - opts, ), + Expression::LazyOperator { op, lhs, rhs, span } => { + let ctx = ctx + .by_ref() + .with_type_annotation(insert_type(TypeInfo::Boolean)); + Self::type_check_lazy_operator(ctx, op, *lhs, *rhs, span) + } + Expression::CodeBlock { contents, span, .. } => { + Self::type_check_code_block(ctx.by_ref(), contents, span) + } // TODO if _condition_ is constant, evaluate it and compile this to an // expression with only one branch Expression::IfExp { @@ -433,15 +423,10 @@ impl TypedExpression { r#else, span, } => Self::type_check_if_expression( - TypeCheckArguments { - checkee: (*condition, *then, r#else.map(|x| *x)), - return_type_annotation: type_annotation, - namespace, - self_type, - mode: Mode::NonAbi, - help_text: Default::default(), - opts, - }, + ctx.by_ref().with_help_text(""), + *condition, + *then, + r#else.map(|e| *e), span, ), Expression::MatchExp { @@ -449,19 +434,13 @@ impl TypedExpression { branches, span, } => Self::type_check_match_expression( - TypeCheckArguments { - checkee: (*value, branches), - return_type_annotation: type_annotation, - namespace, - self_type, - mode: Mode::NonAbi, - help_text: Default::default(), - opts, - }, + ctx.by_ref().with_help_text(""), + *value, + branches, span, ), Expression::AsmExpression { asm, span, .. } => { - Self::type_check_asm_expression(asm, span, namespace, self_type, opts) + Self::type_check_asm_expression(ctx.by_ref(), asm, span) } Expression::StructExpression { span, @@ -469,26 +448,17 @@ impl TypedExpression { struct_name, fields, } => Self::type_check_struct_expression( - span, + ctx.by_ref(), struct_name, type_arguments, fields, - namespace, - self_type, - opts, + span, ), Expression::SubfieldExpression { prefix, span, field_to_access, - } => Self::type_check_subfield_expression( - *prefix, - span, - field_to_access, - namespace, - self_type, - opts, - ), + } => Self::type_check_subfield_expression(ctx.by_ref(), *prefix, span, field_to_access), Expression::MethodApplication { method_name, contract_call_params, @@ -496,79 +466,72 @@ impl TypedExpression { type_arguments, span, } => type_check_method_application( + ctx.by_ref(), method_name, contract_call_params, arguments, type_arguments, span, - namespace, - self_type, - opts, ), Expression::Tuple { fields, span } => { - Self::type_check_tuple(fields, span, namespace, type_annotation, self_type, opts) + Self::type_check_tuple(ctx.by_ref(), fields, span) } Expression::TupleIndex { prefix, index, index_span, span, - } => Self::type_check_tuple_index( - *prefix, index, index_span, span, namespace, self_type, opts, - ), + } => Self::type_check_tuple_index(ctx.by_ref(), *prefix, index, index_span, span), Expression::DelineatedPath { call_path, span, args, type_arguments, } => Self::type_check_delineated_path( + ctx.by_ref(), call_path, span, args, type_arguments, - namespace, - self_type, - opts, ), Expression::AbiCast { abi_name, address, span, - } => Self::type_check_abi_cast(abi_name, address, span, namespace, self_type, opts), + } => Self::type_check_abi_cast(ctx.by_ref(), abi_name, address, span), Expression::Array { contents, span } => { - Self::type_check_array(contents, span, namespace, self_type, opts) + Self::type_check_array(ctx.by_ref(), contents, span) } Expression::ArrayIndex { prefix, index, span, - } => Self::type_check_array_index( - TypeCheckArguments { - checkee: (*prefix, *index), - namespace, - self_type, - opts, - return_type_annotation: insert_type(TypeInfo::Unknown), - mode: Default::default(), - help_text: Default::default(), - }, + } => { + let ctx = ctx + .by_ref() + .with_type_annotation(insert_type(TypeInfo::Unknown)) + .with_help_text(""); + Self::type_check_array_index(ctx, *prefix, *index, span) + } + Expression::StorageAccess { field_names, .. } => { + let ctx = ctx + .by_ref() + .with_type_annotation(insert_type(TypeInfo::Unknown)) + .with_help_text(""); + Self::type_check_storage_load(ctx, field_names, &expr_span) + } + Expression::IntrinsicFunction { + kind, + type_arguments, + arguments, + span, + } => Self::type_check_intrinsic_function( + ctx.by_ref(), + kind, + type_arguments, + arguments, span, ), - Expression::StorageAccess { field_names, .. } => Self::type_check_storage_load( - TypeCheckArguments { - checkee: field_names, - namespace, - self_type, - opts, - return_type_annotation: insert_type(TypeInfo::Unknown), - mode: Default::default(), - help_text: Default::default(), - }, - &expr_span, - ), - Expression::IntrinsicFunction { kind, span } => { - Self::type_check_intrinsic_function(kind, self_type, namespace, opts, span) - } }; let mut typed_expression = match res.value { Some(r) => r, @@ -580,22 +543,16 @@ impl TypedExpression { // if one of the expressions deterministically aborts, we don't want to type check it. if !typed_expression.deterministically_aborts() { // if the return type cannot be cast into the annotation type then it is a type error - let (mut new_warnings, new_errors) = unify_with_self( - typed_expression.return_type, - type_annotation, - self_type, - &expr_span, - help_text, - ); + let (mut new_warnings, new_errors) = + ctx.unify_with_self(typed_expression.return_type, &expr_span); warnings.append(&mut new_warnings); errors.append(&mut new_errors.into_iter().map(|x| x.into()).collect()); } // The annotation may result in a cast, which is handled in the type engine. typed_expression.return_type = check!( - namespace.resolve_type_with_self( - look_up_type_id(typed_expression.return_type), - self_type, + ctx.resolve_type_with_self( + typed_expression.return_type, &expr_span, EnforceTypeArguments::No ), @@ -652,9 +609,9 @@ impl TypedExpression { } pub(crate) fn type_check_variable_expression( + namespace: &Namespace, name: Ident, span: Span, - namespace: &Namespace, ) -> CompileResult { let mut errors = vec![]; let exp = match namespace.resolve_symbol(&name).value { @@ -703,20 +660,16 @@ impl TypedExpression { #[allow(clippy::type_complexity)] fn type_check_function_application( - arguments: TypeCheckArguments<'_, (CallPath, Vec, Vec)>, + ctx: TypeCheckContext, + name: CallPath, + arguments: Vec, + type_arguments: Vec, _span: Span, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; - let TypeCheckArguments { - checkee: (name, arguments, type_arguments), - namespace, - self_type, - opts, - .. - } = arguments; let unknown_decl = check!( - namespace.resolve_call_path(&name).cloned(), + ctx.namespace.resolve_call_path(&name).cloned(), return err(warnings, errors), warnings, errors @@ -728,86 +681,52 @@ impl TypedExpression { errors ); instantiate_function_application( + ctx, function_decl.clone(), name, type_arguments, arguments, - namespace, - self_type, - opts, ) } fn type_check_lazy_operator( - arguments: TypeCheckArguments<'_, (LazyOp, Expression, Expression)>, + ctx: TypeCheckContext, + op: LazyOp, + lhs: Expression, + rhs: Expression, span: Span, ) -> CompileResult { - let TypeCheckArguments { - checkee: (op, lhs, rhs), - namespace, - self_type, - return_type_annotation, - opts, - .. - } = arguments; - let mut warnings = vec![]; let mut errors = vec![]; + let mut ctx = ctx.with_help_text(""); let typed_lhs = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: lhs.clone(), - help_text: Default::default(), - mode: Mode::NonAbi, - opts, - self_type, - namespace, - return_type_annotation, - }), + TypedExpression::type_check(ctx.by_ref(), lhs.clone()), error_recovery_expr(lhs.span()), warnings, errors ); let typed_rhs = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: rhs.clone(), - namespace, - return_type_annotation, - help_text: Default::default(), - self_type, - mode: Mode::NonAbi, - opts, - }), + TypedExpression::type_check(ctx.by_ref(), rhs.clone()), error_recovery_expr(rhs.span()), warnings, errors ); - let exp = instantiate_lazy_operator(op, typed_lhs, typed_rhs, return_type_annotation, span); + let type_annotation = ctx.type_annotation(); + let exp = instantiate_lazy_operator(op, typed_lhs, typed_rhs, type_annotation, span); ok(exp, warnings, errors) } fn type_check_code_block( + mut ctx: TypeCheckContext, contents: CodeBlock, span: Span, - namespace: &mut Namespace, - type_annotation: TypeId, - help_text: &'static str, - self_type: TypeId, - opts: TCOpts, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; let (typed_block, block_return_type) = check!( - TypedCodeBlock::type_check(TypeCheckArguments { - checkee: contents, - namespace, - return_type_annotation: type_annotation, - help_text, - self_type, - mode: Mode::NonAbi, - opts, - }), + TypedCodeBlock::type_check(ctx.by_ref(), contents), ( TypedCodeBlock { contents: vec![] }, crate::type_engine::insert_type(TypeInfo::Tuple(Vec::new())) @@ -816,13 +735,7 @@ impl TypedExpression { errors ); - let (mut new_warnings, new_errors) = unify_with_self( - block_return_type, - type_annotation, - self_type, - &span, - help_text, - ); + let (mut new_warnings, new_errors) = ctx.unify_with_self(block_return_type, &span); warnings.append(&mut new_warnings); errors.append(&mut new_errors.into_iter().map(|x| x.into()).collect()); let exp = TypedExpression { @@ -839,65 +752,59 @@ impl TypedExpression { #[allow(clippy::type_complexity)] fn type_check_if_expression( - arguments: TypeCheckArguments<'_, (Expression, Expression, Option)>, + mut ctx: TypeCheckContext, + condition: Expression, + then: Expression, + r#else: Option, span: Span, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; - let TypeCheckArguments { - checkee: (condition, then, r#else), - namespace, - return_type_annotation: type_annotation, - self_type, - opts, - .. - } = arguments; - let condition = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: condition.clone(), - namespace, - return_type_annotation: insert_type(TypeInfo::Boolean), - help_text: "The condition of an if expression must be a boolean expression.", - self_type, - mode: Mode::NonAbi, - opts, - }), - error_recovery_expr(condition.span()), - warnings, - errors - ); - let then = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: then.clone(), - namespace, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text: Default::default(), - self_type, - mode: Mode::NonAbi, - opts, - }), - error_recovery_expr(then.span()), - warnings, - errors - ); + let condition = { + let ctx = ctx + .by_ref() + .with_help_text("The condition of an if expression must be a boolean expression.") + .with_type_annotation(insert_type(TypeInfo::Boolean)); + check!( + TypedExpression::type_check(ctx, condition.clone()), + error_recovery_expr(condition.span()), + warnings, + errors + ) + }; + let then = { + let ctx = ctx + .by_ref() + .with_help_text("") + .with_type_annotation(insert_type(TypeInfo::Unknown)); + check!( + TypedExpression::type_check(ctx, then.clone()), + error_recovery_expr(then.span()), + warnings, + errors + ) + }; let r#else = r#else.map(|expr| { + let ctx = ctx + .by_ref() + .with_help_text("") + .with_type_annotation(insert_type(TypeInfo::Unknown)); check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: expr.clone(), - namespace, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text: Default::default(), - self_type, - mode: Mode::NonAbi, - opts, - }), + TypedExpression::type_check(ctx, expr.clone()), error_recovery_expr(expr.span()), warnings, errors ) }); let exp = check!( - instantiate_if_expression(condition, then, r#else, span, type_annotation, self_type), + instantiate_if_expression( + condition, + then, + r#else, + span, + ctx.type_annotation(), + ctx.self_type(), + ), return err(warnings, errors), warnings, errors @@ -906,35 +813,27 @@ impl TypedExpression { } fn type_check_match_expression( - arguments: TypeCheckArguments<'_, (Expression, Vec)>, + mut ctx: TypeCheckContext, + value: Expression, + branches: Vec, span: Span, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; - let TypeCheckArguments { - checkee: (value, branches), - namespace, - return_type_annotation, - self_type, - opts, - .. - } = arguments; // type check the value - let typed_value = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: value.clone(), - namespace, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text: Default::default(), - self_type, - mode: Mode::NonAbi, - opts, - }), - error_recovery_expr(value.span()), - warnings, - errors - ); + let typed_value = { + let ctx = ctx + .by_ref() + .with_help_text("") + .with_type_annotation(insert_type(TypeInfo::Unknown)); + check!( + TypedExpression::type_check(ctx, value.clone()), + error_recovery_expr(value.span()), + warnings, + errors + ) + }; let type_id = typed_value.return_type; let _ = check!( @@ -950,23 +849,15 @@ impl TypedExpression { .collect::>(); // type check the match expression and create a TypedMatchExpression object - let typed_match_expression = check!( - TypedMatchExpression::type_check( - TypeCheckArguments { - checkee: (typed_value, branches), - namespace, - return_type_annotation, - help_text: Default::default(), - self_type, - mode: Mode::NonAbi, - opts, - }, - span.clone() - ), - return err(warnings, errors), - warnings, - errors - ); + let typed_match_expression = { + let ctx = ctx.by_ref().with_help_text(""); + check!( + TypedMatchExpression::type_check(ctx, typed_value, branches, span.clone()), + return err(warnings, errors), + warnings, + errors + ) + }; // check to see if the match expression is exhaustive and if all match arms are reachable let (witness_report, arms_reachability) = check!( @@ -993,7 +884,7 @@ impl TypedExpression { // desugar the typed match expression to a typed if expression let typed_if_exp = check!( - typed_match_expression.convert_to_typed_if_expression(namespace, self_type), + typed_match_expression.convert_to_typed_if_expression(ctx), return err(warnings, errors), warnings, errors @@ -1004,11 +895,9 @@ impl TypedExpression { #[allow(clippy::too_many_arguments)] fn type_check_asm_expression( + mut ctx: TypeCheckContext, asm: AsmExpression, span: Span, - namespace: &mut Namespace, - self_type: TypeId, - opts: TCOpts, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; @@ -1018,9 +907,8 @@ impl TypedExpression { .map(|x| x.1) .unwrap_or_else(|| asm.whole_block_span.clone()); let return_type = check!( - namespace.resolve_type_with_self( - asm.return_type.clone(), - self_type, + ctx.resolve_type_with_self( + insert_type(asm.return_type.clone()), &asm_span, EnforceTypeArguments::No ), @@ -1036,16 +924,12 @@ impl TypedExpression { |AsmRegisterDeclaration { name, initializer }| TypedAsmRegisterDeclaration { name, initializer: initializer.map(|initializer| { + let ctx = ctx + .by_ref() + .with_help_text("") + .with_type_annotation(insert_type(TypeInfo::Unknown)); check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: initializer.clone(), - namespace, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text: Default::default(), - self_type, - mode: Mode::NonAbi, - opts, - }), + TypedExpression::type_check(ctx, initializer.clone()), error_recovery_expr(initializer.span()), warnings, errors @@ -1074,21 +958,19 @@ impl TypedExpression { #[allow(clippy::too_many_arguments)] fn type_check_struct_expression( - span: Span, + mut ctx: TypeCheckContext, call_path: CallPath, type_arguments: Vec, fields: Vec, - namespace: &mut Namespace, - self_type: TypeId, - opts: TCOpts, + span: Span, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; // find the module that the symbol is in - let module_path = namespace.find_module_path(&call_path.prefixes); + let module_path = ctx.namespace.find_module_path(&call_path.prefixes); check!( - namespace.root().check_submodule(&module_path), + ctx.namespace.root().check_submodule(&module_path), return err(warnings, errors), warnings, errors @@ -1096,7 +978,7 @@ impl TypedExpression { // find the struct definition from the name let unknown_decl = check!( - namespace + ctx.namespace .root() .resolve_symbol(&module_path, &call_path.suffix) .cloned(), @@ -1104,22 +986,20 @@ impl TypedExpression { warnings, errors ); - let struct_decl = check!( - unknown_decl.expect_struct(), + let mut struct_decl = check!( + unknown_decl.expect_struct().cloned(), return err(warnings, errors), warnings, errors - ) - .clone(); + ); // monomorphize the struct definition - let mut struct_decl = check!( - namespace.monomorphize( - struct_decl, + check!( + ctx.monomorphize( + &mut struct_decl, type_arguments, EnforceTypeArguments::No, - Some(self_type), - None + &call_path.span() ), return err(warnings, errors), warnings, @@ -1151,17 +1031,14 @@ impl TypedExpression { } }; + let ctx = ctx + .by_ref() + .with_help_text( + "Struct field's type must match up with the type specified in its declaration.", + ) + .with_type_annotation(def_field.type_id); let typed_field = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: expr_field.value, - namespace, - return_type_annotation: def_field.type_id, - help_text: "Struct field's type must match up with the type specified in its \ - declaration.", - self_type, - mode: Mode::NonAbi, - opts, - }), + TypedExpression::type_check(ctx, expr_field.value), continue, warnings, errors @@ -1198,25 +1075,18 @@ impl TypedExpression { #[allow(clippy::too_many_arguments)] fn type_check_subfield_expression( + ctx: TypeCheckContext, prefix: Expression, span: Span, field_to_access: Ident, - namespace: &mut Namespace, - self_type: TypeId, - opts: TCOpts, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; + let ctx = ctx + .with_help_text("") + .with_type_annotation(insert_type(TypeInfo::Unknown)); let parent = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: prefix, - namespace, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text: Default::default(), - self_type, - mode: Mode::NonAbi, - opts, - }), + TypedExpression::type_check(ctx, prefix), return err(warnings, errors), warnings, errors @@ -1231,16 +1101,13 @@ impl TypedExpression { } fn type_check_tuple( + mut ctx: TypeCheckContext, fields: Vec, span: Span, - namespace: &mut Namespace, - type_annotation: TypeId, - self_type: TypeId, - opts: TCOpts, - ) -> CompileResult { + ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; - let field_type_opt = match look_up_type_id(type_annotation) { + let field_type_opt = match look_up_type_id(ctx.type_annotation()) { TypeInfo::Tuple(field_type_ids) if field_type_ids.len() == fields.len() => { Some(field_type_ids) } @@ -1255,16 +1122,12 @@ impl TypedExpression { .map(|field_type_ids| field_type_ids[i].clone()) .unwrap_or_default(); let field_span = field.span(); + let ctx = ctx + .by_ref() + .with_help_text("tuple field type does not match the expected type") + .with_type_annotation(field_type.type_id); let typed_field = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: field, - namespace, - return_type_annotation: field_type.type_id, - help_text: "tuple field type does not match the expected type", - self_type, - mode: Mode::NonAbi, - opts, - }), + TypedExpression::type_check(ctx, field), error_recovery_expr(field_span), warnings, errors @@ -1293,22 +1156,19 @@ impl TypedExpression { /// If there isn't any storage, then this is an error. If there is storage, find the corresponding /// field that has been specified and return that value. fn type_check_storage_load( - arguments: TypeCheckArguments<'_, Vec>, + ctx: TypeCheckContext, + checkee: Vec, span: &Span, - ) -> CompileResult { - let TypeCheckArguments { - checkee, namespace, .. - } = arguments; - + ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; - if !namespace.has_storage_declared() { + if !ctx.namespace.has_storage_declared() { errors.push(CompileError::NoDeclaredStorage { span: span.clone() }); return err(warnings, errors); } let storage_fields = check!( - namespace.get_storage_field_descriptors(), + ctx.namespace.get_storage_field_descriptors(), return err(warnings, errors), warnings, errors @@ -1316,7 +1176,7 @@ impl TypedExpression { // Do all namespace checking here! let (storage_access, return_type) = check!( - namespace.apply_storage_load(checkee, &storage_fields), + ctx.namespace.apply_storage_load(checkee, &storage_fields), return err(warnings, errors), warnings, errors @@ -1334,26 +1194,19 @@ impl TypedExpression { } fn type_check_tuple_index( + ctx: TypeCheckContext, prefix: Expression, index: usize, index_span: Span, span: Span, - namespace: &mut Namespace, - self_type: TypeId, - opts: TCOpts, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; + let ctx = ctx + .with_help_text("") + .with_type_annotation(insert_type(TypeInfo::Unknown)); let parent = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: prefix, - namespace, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text: Default::default(), - self_type, - mode: Mode::NonAbi, - opts, - }), + TypedExpression::type_check(ctx, prefix), return err(warnings, errors), warnings, errors @@ -1369,13 +1222,11 @@ impl TypedExpression { #[allow(clippy::too_many_arguments)] fn type_check_delineated_path( + ctx: TypeCheckContext, call_path: CallPath, span: Span, args: Vec, type_arguments: Vec, - namespace: &mut Namespace, - self_type: TypeId, - opts: TCOpts, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; @@ -1390,20 +1241,22 @@ impl TypedExpression { // First, check if this could be a module. We check first so that we can check for // ambiguity in the following enum check. - let is_module = namespace + let is_module = ctx + .namespace .check_submodule(&call_path.prefixes) .ok(&mut probe_warnings, &mut probe_errors) .is_some(); // Check if the call path refers to an enum in another module. let (enum_name, enum_mod_path) = call_path.prefixes.split_last().expect("empty call path"); - let abs_enum_mod_path: Vec<_> = namespace.find_module_path(enum_mod_path); - let exp = if let Some(enum_decl) = namespace + let abs_enum_mod_path: Vec<_> = ctx.namespace.find_module_path(enum_mod_path); + let exp = if let Some(enum_decl) = ctx + .namespace .check_submodule_mut(enum_mod_path) .ok(&mut warnings, &mut errors) .map(|_| ()) .and_then(|_| { - namespace + ctx.namespace .root() .resolve_symbol(&abs_enum_mod_path, enum_name) .value @@ -1416,28 +1269,21 @@ impl TypedExpression { return err(warnings, errors); } check!( - instantiate_enum( - enum_decl, - call_path.suffix, - args, - type_arguments, - namespace, - self_type, - opts, - ), + instantiate_enum(ctx, enum_decl, call_path.suffix, args, type_arguments), return err(warnings, errors), warnings, errors ) // Otherwise, our prefix should point to some module ending with an enum or function. - } else if namespace + } else if ctx + .namespace .check_submodule_mut(&call_path.prefixes) .ok(&mut probe_warnings, &mut probe_errors) .is_some() { let decl = check!( - namespace.resolve_call_path(&call_path).cloned(), + ctx.namespace.resolve_call_path(&call_path).cloned(), return err(warnings, errors), warnings, errors @@ -1445,15 +1291,7 @@ impl TypedExpression { match decl { TypedDeclaration::EnumDeclaration(enum_decl) => { check!( - instantiate_enum( - enum_decl, - call_path.suffix, - args, - type_arguments, - namespace, - self_type, - opts, - ), + instantiate_enum(ctx, enum_decl, call_path.suffix, args, type_arguments), return err(warnings, errors), warnings, errors @@ -1462,13 +1300,11 @@ impl TypedExpression { TypedDeclaration::FunctionDeclaration(func_decl) => { check!( instantiate_function_application( + ctx, func_decl, call_path, vec!(), // the type args in this position are guarenteed to be empty due to parsing args, - namespace, - self_type, - opts, ), return err(warnings, errors), warnings, @@ -1499,35 +1335,31 @@ impl TypedExpression { #[allow(clippy::too_many_arguments)] fn type_check_abi_cast( + mut ctx: TypeCheckContext, abi_name: CallPath, address: Box, span: Span, - namespace: &mut Namespace, - self_type: TypeId, - opts: TCOpts, - ) -> CompileResult { + ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; // TODO use lib-std's Address type instead of b256 // type check the address and make sure it is let err_span = address.span(); - let address_expr = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: *address, - namespace, - return_type_annotation: insert_type(TypeInfo::B256), - help_text: "An address that is being ABI cast must be of type b256", - self_type, - mode: Mode::NonAbi, - opts, - }), - error_recovery_expr(err_span), - warnings, - errors - ); + let address_expr = { + let ctx = ctx + .by_ref() + .with_help_text("An address that is being ABI cast must be of type b256") + .with_type_annotation(insert_type(TypeInfo::B256)); + check!( + TypedExpression::type_check(ctx, *address), + error_recovery_expr(err_span), + warnings, + errors + ) + }; // look up the call path and get the declaration it references let abi = check!( - namespace.resolve_call_path(&abi_name).cloned(), + ctx.namespace.resolve_call_path(&abi_name).cloned(), return err(warnings, errors), warnings, errors @@ -1553,7 +1385,7 @@ impl TypedExpression { // look up the call path and get the declaration it references AbiName::Known(abi_name) => { let unknown_decl = check!( - namespace.resolve_call_path(&abi_name).cloned(), + ctx.namespace.resolve_call_path(&abi_name).cloned(), return err(warnings, errors), warnings, errors @@ -1606,16 +1438,13 @@ impl TypedExpression { // they instead just use the CALL opcode and the return type let mut type_checked_fn_buf = Vec::with_capacity(abi.methods.len()); for method in &abi.methods { + let ctx = ctx + .by_ref() + .with_help_text("") + .with_type_annotation(insert_type(TypeInfo::Unknown)) + .with_mode(Mode::ImplAbiFn); type_checked_fn_buf.push(check!( - TypedFunctionDeclaration::type_check(TypeCheckArguments { - checkee: method.clone(), - namespace, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text: Default::default(), - self_type: insert_type(TypeInfo::Contract), - mode: Mode::ImplAbiFn, - opts, - }), + TypedFunctionDeclaration::type_check(ctx, method.clone()), return err(warnings, errors), warnings, errors @@ -1623,7 +1452,8 @@ impl TypedExpression { } functions_buf.append(&mut type_checked_fn_buf); - namespace.insert_trait_implementation(abi_name.clone(), return_type, functions_buf); + ctx.namespace + .insert_trait_implementation(abi_name.clone(), return_type, functions_buf); let exp = TypedExpression { expression: TypedExpressionVariant::AbiCast { abi_name, @@ -1637,14 +1467,11 @@ impl TypedExpression { ok(exp, warnings, errors) } - #[allow(clippy::too_many_arguments)] fn type_check_array( + mut ctx: TypeCheckContext, contents: Vec, span: Span, - namespace: &mut Namespace, - self_type: TypeId, - opts: TCOpts, - ) -> CompileResult { + ) -> CompileResult { if contents.is_empty() { return ok( TypedExpression { @@ -1666,16 +1493,12 @@ impl TypedExpression { .into_iter() .map(|expr| { let span = expr.span(); + let ctx = ctx + .by_ref() + .with_help_text("") + .with_type_annotation(insert_type(TypeInfo::Unknown)); check!( - Self::type_check(TypeCheckArguments { - checkee: expr, - namespace, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text: Default::default(), - self_type, - mode: Mode::NonAbi, - opts, - }), + Self::type_check(ctx, expr), error_recovery_expr(span), warnings, errors @@ -1685,13 +1508,10 @@ impl TypedExpression { let elem_type = typed_contents[0].return_type; for typed_elem in &typed_contents[1..] { - let (mut new_warnings, new_errors) = unify_with_self( - typed_elem.return_type, - elem_type, - self_type, - &typed_elem.span, - "", - ); + let (mut new_warnings, new_errors) = ctx + .by_ref() + .with_type_annotation(elem_type) + .unify_with_self(typed_elem.return_type, &typed_elem.span); let no_warnings = new_warnings.is_empty(); let no_errors = new_errors.is_empty(); warnings.append(&mut new_warnings); @@ -1719,48 +1539,35 @@ impl TypedExpression { } fn type_check_array_index( - arguments: TypeCheckArguments<'_, (Expression, Expression)>, + mut ctx: TypeCheckContext, + prefix: Expression, + index: Expression, span: Span, - ) -> CompileResult { - let TypeCheckArguments { - checkee: (prefix, index), - namespace, - self_type, - opts, - .. - } = arguments; + ) -> CompileResult { let mut warnings = Vec::new(); let mut errors = Vec::new(); - let prefix_te = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: prefix.clone(), - namespace, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text: Default::default(), - self_type, - mode: Mode::NonAbi, - opts, - }), - return err(warnings, errors), - warnings, - errors - ); + let prefix_te = { + let ctx = ctx + .by_ref() + .with_help_text("") + .with_type_annotation(insert_type(TypeInfo::Unknown)); + check!( + TypedExpression::type_check(ctx, prefix.clone()), + return err(warnings, errors), + warnings, + errors + ) + }; // If the return type is a static array then create a TypedArrayIndex. if let TypeInfo::Array(elem_type_id, _) = look_up_type_id(prefix_te.return_type) { + let type_info_u64 = TypeInfo::UnsignedInteger(IntegerBits::SixtyFour); + let ctx = ctx + .with_help_text("") + .with_type_annotation(insert_type(type_info_u64)); let index_te = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: index, - namespace, - return_type_annotation: insert_type(TypeInfo::UnsignedInteger( - IntegerBits::SixtyFour - )), - help_text: Default::default(), - self_type, - mode: Mode::NonAbi, - opts, - }), + TypedExpression::type_check(ctx, index), return err(warnings, errors), warnings, errors @@ -1792,29 +1599,33 @@ impl TypedExpression { }, }; type_check_method_application( + ctx, method_name, vec![], vec![prefix, index], vec![], span, - namespace, - self_type, - opts, ) } } fn type_check_intrinsic_function( - kind: IntrinsicFunctionKind, - self_type: TypeId, - namespace: &mut Namespace, - opts: TCOpts, + ctx: TypeCheckContext, + kind: Intrinsic, + type_arguments: Vec, + arguments: Vec, span: Span, - ) -> CompileResult { + ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; let (intrinsic_function, return_type) = check!( - TypedIntrinsicFunctionKind::type_check(kind, self_type, namespace, opts), + TypedIntrinsicFunctionKind::type_check( + ctx, + kind, + type_arguments, + arguments, + span.clone() + ), return err(warnings, errors), warnings, errors @@ -1920,17 +1731,8 @@ mod tests { fn do_type_check(expr: Expression, type_annotation: TypeId) -> CompileResult { let mut namespace = Namespace::init_root(namespace::Module::default()); - let self_type = insert_type(TypeInfo::Unknown); - - TypedExpression::type_check(TypeCheckArguments { - checkee: expr, - namespace: &mut namespace, - return_type_annotation: type_annotation, - help_text: Default::default(), - self_type, - mode: Mode::NonAbi, - opts: Default::default(), - }) + let ctx = TypeCheckContext::from_root(&mut namespace).with_type_annotation(type_annotation); + TypedExpression::type_check(ctx, expr) } fn do_type_check_for_boolx2(expr: Expression) -> CompileResult { diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/enum_instantiation.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/enum_instantiation.rs index 80adecaf40d..e91fef07608 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/enum_instantiation.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/enum_instantiation.rs @@ -6,25 +6,22 @@ use sway_types::{Ident, Spanned}; /// [TypedExpression]. #[allow(clippy::too_many_arguments)] pub(crate) fn instantiate_enum( - enum_decl: TypedEnumDeclaration, + mut ctx: TypeCheckContext, + mut enum_decl: TypedEnumDeclaration, enum_field_name: Ident, args: Vec, type_arguments: Vec, - namespace: &mut Namespace, - self_type: TypeId, - opts: TCOpts, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; // monomorphize the enum definition with the type arguments - let enum_decl = check!( - namespace.monomorphize( - enum_decl, + check!( + ctx.monomorphize( + &mut enum_decl, type_arguments, EnforceTypeArguments::No, - Some(self_type), - Some(&enum_field_name.span()) + &enum_field_name.span() ), return err(warnings, errors), warnings, @@ -61,16 +58,11 @@ pub(crate) fn instantiate_enum( errors, ), ([single_expr], _) => { + let ctx = ctx + .with_help_text("Enum instantiator must match its declared variant type.") + .with_type_annotation(enum_variant.type_id); let typed_expr = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: single_expr.clone(), - namespace, - return_type_annotation: enum_variant.type_id, - help_text: "Enum instantiator must match its declared variant type.", - self_type, - mode: Mode::NonAbi, - opts, - }), + TypedExpression::type_check(ctx, single_expr.clone()), return err(warnings, errors), warnings, errors diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs index 525d562a397..5a2adccf410 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/function_application.rs @@ -1,32 +1,28 @@ use crate::{ error::*, - semantic_analysis::{ast_node::*, TCOpts, TypeCheckArguments}, - type_engine::TypeId, + semantic_analysis::{ast_node::*, TypeCheckContext}, }; use std::collections::{hash_map::RandomState, HashMap, VecDeque}; use sway_types::{state::StateIndex, Spanned}; #[allow(clippy::too_many_arguments)] pub(crate) fn instantiate_function_application( - function_decl: TypedFunctionDeclaration, + mut ctx: TypeCheckContext, + mut function_decl: TypedFunctionDeclaration, call_path: CallPath, type_arguments: Vec, arguments: Vec, - namespace: &mut Namespace, - self_type: TypeId, - opts: TCOpts, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; // monomorphize the function declaration - let function_decl = check!( - namespace.monomorphize( - function_decl, + check!( + ctx.monomorphize( + &mut function_decl, type_arguments, EnforceTypeArguments::No, - Some(self_type), - Some(&call_path.span()) + &call_path.span() ), return err(warnings, errors), warnings, @@ -34,9 +30,9 @@ pub(crate) fn instantiate_function_application( ); // 'purity' is that of the callee, 'opts.purity' of the caller. - if !opts.purity.can_call(function_decl.purity) { + if !ctx.purity().can_call(function_decl.purity) { errors.push(CompileError::StorageAccessMismatch { - attrs: promote_purity(opts.purity, function_decl.purity).to_attribute_syntax(), + attrs: promote_purity(ctx.purity(), function_decl.purity).to_attribute_syntax(), span: call_path.span(), }); } @@ -48,18 +44,16 @@ pub(crate) fn instantiate_function_application( .into_iter() .zip(function_decl.parameters.iter()) .map(|(arg, param)| { + let ctx = ctx + .by_ref() + .with_help_text( + "The argument that has been provided to this function's type does \ + not match the declared type of the parameter in the function \ + declaration.", + ) + .with_type_annotation(param.type_id); let exp = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: arg.clone(), - namespace, - return_type_annotation: param.type_id, - help_text: "The argument that has been provided to this function's type does \ - not match the declared type of the parameter in the function \ - declaration.", - self_type, - mode: Mode::NonAbi, - opts, - }), + TypedExpression::type_check(ctx, arg.clone()), error_recovery_expr(arg.span()), warnings, errors diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/if_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/if_expression.rs index a146b571954..ac5670cc0fc 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/if_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/if_expression.rs @@ -37,11 +37,16 @@ pub(crate) fn instantiate_if_expression( let mut else_deterministically_aborts = false; let r#else = r#else.map(|r#else| { else_deterministically_aborts = r#else.deterministically_aborts(); + let ty_to_check = if then_deterministically_aborts { + type_annotation + } else { + then.return_type + }; if !else_deterministically_aborts { // if this does not deterministically_abort, check the block return type let (mut new_warnings, new_errors) = unify_with_self( r#else.return_type, - then.return_type, + ty_to_check, self_type, &r#else.span, "`else` branch must return expected type.", @@ -78,7 +83,11 @@ pub(crate) fn instantiate_if_expression( } } - let return_type = then.return_type; + let return_type = if !then_deterministically_aborts { + then.return_type + } else { + r#else_ret_ty + }; let exp = TypedExpression { expression: TypedExpressionVariant::IfExp { condition: Box::new(condition), diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs index 39313691d5b..1a03e347b60 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs @@ -12,30 +12,24 @@ use sway_types::{Ident, Spanned}; #[allow(clippy::too_many_arguments)] pub(crate) fn type_check_method_application( + mut ctx: TypeCheckContext, method_name: MethodName, contract_call_params: Vec, arguments: Vec, type_arguments: Vec, span: Span, - namespace: &mut Namespace, - self_type: TypeId, - opts: TCOpts, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; let mut args_buf = VecDeque::new(); let mut contract_call_params_map = HashMap::new(); for arg in &arguments { + let ctx = ctx + .by_ref() + .with_help_text("") + .with_type_annotation(insert_type(TypeInfo::Unknown)); args_buf.push_back(check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: arg.clone(), - namespace, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text: Default::default(), - self_type, - mode: Mode::NonAbi, - opts, - }), + TypedExpression::type_check(ctx, arg.clone()), error_recovery_expr(span.clone()), warnings, errors @@ -44,12 +38,11 @@ pub(crate) fn type_check_method_application( let method = check!( resolve_method_name( + ctx.by_ref(), &method_name, args_buf.clone(), type_arguments, span.clone(), - namespace, - self_type ), return err(warnings, errors), warnings, @@ -64,9 +57,9 @@ pub(crate) fn type_check_method_application( if !method.is_contract_call { // 'method.purity' is that of the callee, 'opts.purity' of the caller. - if !opts.purity.can_call(method.purity) { + if !ctx.purity().can_call(method.purity) { errors.push(CompileError::StorageAccessMismatch { - attrs: promote_purity(opts.purity, method.purity).to_attribute_syntax(), + attrs: promote_purity(ctx.purity(), method.purity).to_attribute_syntax(), span: method_name.easy_name().span(), }); } @@ -96,6 +89,18 @@ pub(crate) fn type_check_method_application( } for param in contract_call_params { + let type_annotation = match param.name.span().as_str() { + constants::CONTRACT_CALL_GAS_PARAMETER_NAME + | constants::CONTRACT_CALL_COINS_PARAMETER_NAME => { + insert_type(TypeInfo::UnsignedInteger(IntegerBits::SixtyFour)) + } + constants::CONTRACT_CALL_ASSET_ID_PARAMETER_NAME => insert_type(TypeInfo::B256), + _ => unreachable!(), + }; + let ctx = ctx + .by_ref() + .with_help_text("") + .with_type_annotation(type_annotation); match param.name.span().as_str() { constants::CONTRACT_CALL_GAS_PARAMETER_NAME | constants::CONTRACT_CALL_COINS_PARAMETER_NAME @@ -103,23 +108,7 @@ pub(crate) fn type_check_method_application( contract_call_params_map.insert( param.name.to_string(), check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: param.value, - namespace, - return_type_annotation: match param.name.span().as_str() { - constants::CONTRACT_CALL_GAS_PARAMETER_NAME - | constants::CONTRACT_CALL_COINS_PARAMETER_NAME => insert_type( - TypeInfo::UnsignedInteger(IntegerBits::SixtyFour) - ), - constants::CONTRACT_CALL_ASSET_ID_PARAMETER_NAME => - insert_type(TypeInfo::B256), - _ => unreachable!(), - }, - help_text: Default::default(), - self_type, - mode: Mode::NonAbi, - opts, - }), + TypedExpression::type_check(ctx, param.value), error_recovery_expr(span.clone()), warnings, errors @@ -139,9 +128,9 @@ pub(crate) fn type_check_method_application( // If this method was called with self being a `StorageAccess` (e.g. storage.map.insert(..)), // then record the index of that storage variable and pass it on. let mut self_state_idx = None; - if namespace.has_storage_declared() { + if ctx.namespace.has_storage_declared() { let storage_fields = check!( - namespace.get_storage_field_descriptors(), + ctx.namespace.get_storage_field_descriptors(), return err(warnings, errors), warnings, errors @@ -172,13 +161,11 @@ pub(crate) fn type_check_method_application( // type check all of the arguments against the parameters in the method declaration for (arg, param) in args_buf.iter().zip(method.parameters.iter()) { // if the return type cannot be cast into the annotation type then it is a type error - let (mut new_warnings, new_errors) = unify_with_self( - arg.return_type, - param.type_id, - self_type, - &arg.span, - "This argument's type is not castable to the declared parameter type.", - ); + let (mut new_warnings, new_errors) = ctx + .by_ref() + .with_help_text("This argument's type is not castable to the declared parameter type.") + .with_type_annotation(param.type_id) + .unify_with_self(arg.return_type, &arg.span); warnings.append(&mut new_warnings); if !new_errors.is_empty() { errors.push(CompileError::ArgumentParameterTypeMismatch { @@ -201,19 +188,26 @@ pub(crate) fn type_check_method_application( ) = (args_buf.get(0), method.parameters.get(0)) { let unknown_decl = check!( - namespace.resolve_symbol(name).cloned(), - return err(warnings, errors), - warnings, - errors - ); - let variable_decl = check!( - unknown_decl.expect_variable().cloned(), + ctx.namespace.resolve_symbol(name).cloned(), return err(warnings, errors), warnings, errors ); - if !variable_decl.is_mutable.is_mutable() && *is_mutable { + let is_decl_mutable = match unknown_decl { + TypedDeclaration::ConstantDeclaration(_) => false, + _ => { + let variable_decl = check!( + unknown_decl.expect_variable().cloned(), + return err(warnings, errors), + warnings, + errors + ); + variable_decl.is_mutable.is_mutable() + } + }; + + if !is_decl_mutable && *is_mutable { errors.push(CompileError::MethodRequiresMutableSelf { method_name: method_name.easy_name(), variable_name: name.clone(), @@ -330,12 +324,11 @@ pub(crate) fn type_check_method_application( } pub(crate) fn resolve_method_name( + ctx: TypeCheckContext, method_name: &MethodName, arguments: VecDeque, type_arguments: Vec, span: Span, - namespace: &mut Namespace, - self_type: TypeId, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; @@ -346,13 +339,12 @@ pub(crate) fn resolve_method_name( type_name_span, } => check!( find_method( + ctx, type_name, type_name_span, type_arguments, - namespace, &arguments, call_path, - self_type ), return err(warnings, errors), warnings, @@ -365,13 +357,12 @@ pub(crate) fn resolve_method_name( .unwrap_or_else(|| (TypeInfo::Unknown, span.clone())); check!( find_method( + ctx, &type_name, &type_name_span, type_arguments, - namespace, &arguments, call_path, - self_type ), return err(warnings, errors), warnings, @@ -383,9 +374,10 @@ pub(crate) fn resolve_method_name( .get(0) .map(|x| x.return_type) .unwrap_or_else(|| insert_type(TypeInfo::Unknown)); - let abs_path: Vec<_> = namespace.find_module_path(Some(method_name)); + let abs_path: Vec<_> = ctx.namespace.find_module_path(Some(method_name)); check!( - namespace.find_method_for_type(ty, &abs_path, self_type, &arguments), + ctx.namespace + .find_method_for_type(ty, &abs_path, ctx.self_type(), &arguments), return err(warnings, errors), warnings, errors @@ -396,13 +388,12 @@ pub(crate) fn resolve_method_name( } fn find_method( + ctx: TypeCheckContext, type_name: &TypeInfo, type_name_span: &Span, type_arguments: Vec, - namespace: &mut Namespace, arguments: &VecDeque, call_path: &CallPath, - self_type: TypeId, ) -> CompileResult { let warnings = vec![]; let mut errors = vec![]; @@ -443,7 +434,8 @@ fn find_method( let abs_path: Vec = if call_path.is_absolute { call_path.full_path().cloned().collect() } else { - namespace.find_module_path(call_path.full_path()) + ctx.namespace.find_module_path(call_path.full_path()) }; - namespace.find_method_for_type(insert_type(ty), &abs_path, self_type, arguments) + ctx.namespace + .find_method_for_type(insert_type(ty), &abs_path, ctx.self_type(), arguments) } diff --git a/sway-core/src/semantic_analysis/ast_node/mod.rs b/sway-core/src/semantic_analysis/ast_node/mod.rs index 50d521abe60..3632445aab5 100644 --- a/sway-core/src/semantic_analysis/ast_node/mod.rs +++ b/sway-core/src/semantic_analysis/ast_node/mod.rs @@ -214,28 +214,16 @@ impl TypedAstNode { } } - pub(crate) fn type_check( - arguments: TypeCheckArguments<'_, AstNode>, - ) -> CompileResult { - let TypeCheckArguments { - checkee: node, - namespace, - return_type_annotation, - help_text, - self_type, - opts, - .. - } = arguments; + pub(crate) fn type_check(mut ctx: TypeCheckContext, node: AstNode) -> CompileResult { let mut warnings = Vec::new(); let mut errors = Vec::new(); // A little utility used to check an ascribed type matches its associated expression. let mut type_check_ascribed_expr = - |namespace: &mut Namespace, type_ascription: TypeInfo, value| { + |mut ctx: TypeCheckContext, type_ascription: TypeInfo, expr| { let type_id = check!( - namespace.resolve_type_with_self( - type_ascription, - self_type, + ctx.resolve_type_with_self( + insert_type(type_ascription), &node.span, EnforceTypeArguments::No ), @@ -243,16 +231,11 @@ impl TypedAstNode { warnings, errors, ); - TypedExpression::type_check(TypeCheckArguments { - checkee: value, - namespace, - return_type_annotation: type_id, - help_text: "This declaration's type annotation does \ - not match up with the assigned expression's type.", - self_type, - mode: Mode::NonAbi, - opts, - }) + let ctx = ctx.with_type_annotation(type_id).with_help_text( + "This declaration's type annotation does not match up with the assigned \ + expression's type.", + ); + TypedExpression::type_check(ctx, expr) }; let node = TypedAstNode { @@ -261,12 +244,12 @@ impl TypedAstNode { let path = if a.is_absolute { a.call_path.clone() } else { - namespace.find_module_path(&a.call_path) + ctx.namespace.find_module_path(&a.call_path) }; let mut res = match a.import_type { - ImportType::Star => namespace.star_import(&path), - ImportType::SelfImport => namespace.self_import(&path, a.alias), - ImportType::Item(s) => namespace.item_import(&path, &s, a.alias), + ImportType::Star => ctx.namespace.star_import(&path), + ImportType::SelfImport => ctx.namespace.self_import(&path, a.alias), + ImportType::Item(s) => ctx.namespace.item_import(&path, &s, a.alias), }; warnings.append(&mut res.warnings); errors.append(&mut res.errors); @@ -282,15 +265,13 @@ impl TypedAstNode { body, is_mutable, }) => { - check_if_name_is_invalid(&name).ok(&mut warnings, &mut errors); let type_ascription_span = match type_ascription_span { Some(type_ascription_span) => type_ascription_span, None => name.span(), }; let type_ascription = check!( - namespace.resolve_type_with_self( - type_ascription, - self_type, + ctx.resolve_type_with_self( + insert_type(type_ascription), &type_ascription_span, EnforceTypeArguments::Yes, ), @@ -298,18 +279,11 @@ impl TypedAstNode { warnings, errors ); - let result = { - TypedExpression::type_check(TypeCheckArguments { - checkee: body, - namespace, - return_type_annotation: type_ascription, - help_text: "Variable declaration's type annotation does \ - not match up with the assigned expression's type.", - self_type, - mode: Mode::NonAbi, - opts, - }) - }; + let mut ctx = ctx.with_type_annotation(type_ascription).with_help_text( + "Variable declaration's type annotation does not match up \ + with the assigned expression's type.", + ); + let result = TypedExpression::type_check(ctx.by_ref(), body); let body = check!(result, error_recovery_expr(name.span()), warnings, errors); let typed_var_decl = @@ -317,10 +291,9 @@ impl TypedAstNode { name: name.clone(), body, is_mutable: is_mutable.into(), - const_decl_origin: false, type_ascription, }); - namespace.insert_symbol(name, typed_var_decl.clone()); + ctx.namespace.insert_symbol(name, typed_var_decl.clone()); typed_var_decl } Declaration::ConstantDeclaration(ConstantDeclaration { @@ -330,28 +303,22 @@ impl TypedAstNode { visibility, }) => { let result = - type_check_ascribed_expr(namespace, type_ascription.clone(), value); + type_check_ascribed_expr(ctx.by_ref(), type_ascription, value); is_screaming_snake_case(&name).ok(&mut warnings, &mut errors); let value = check!(result, error_recovery_expr(name.span()), warnings, errors); let typed_const_decl = - TypedDeclaration::VariableDeclaration(TypedVariableDeclaration { + TypedDeclaration::ConstantDeclaration(TypedConstantDeclaration { name: name.clone(), - body: value, - is_mutable: if visibility.is_public() { - VariableMutability::ExportedConst - } else { - VariableMutability::Immutable - }, - const_decl_origin: true, - type_ascription: insert_type(type_ascription), + value, + visibility, }); - namespace.insert_symbol(name, typed_const_decl.clone()); + ctx.namespace.insert_symbol(name, typed_const_decl.clone()); typed_const_decl } Declaration::EnumDeclaration(decl) => { let enum_decl = check!( - TypedEnumDeclaration::type_check(decl, namespace, self_type), + TypedEnumDeclaration::type_check(ctx.by_ref(), decl), return err(warnings, errors), warnings, errors @@ -359,7 +326,7 @@ impl TypedAstNode { let name = enum_decl.name.clone(); let decl = TypedDeclaration::EnumDeclaration(enum_decl); let _ = check!( - namespace.insert_symbol(name, decl.clone()), + ctx.namespace.insert_symbol(name, decl.clone()), return err(warnings, errors), warnings, errors @@ -367,52 +334,36 @@ impl TypedAstNode { decl } Declaration::FunctionDeclaration(fn_decl) => { + let mut ctx = ctx.with_type_annotation(insert_type(TypeInfo::Unknown)); let fn_decl = check!( - TypedFunctionDeclaration::type_check(TypeCheckArguments { - checkee: fn_decl.clone(), - namespace, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text, - self_type, - mode: Mode::NonAbi, - opts - }), + TypedFunctionDeclaration::type_check(ctx.by_ref(), fn_decl.clone()), error_recovery_function_declaration(fn_decl), warnings, errors ); let name = fn_decl.name.clone(); let decl = TypedDeclaration::FunctionDeclaration(fn_decl); - namespace.insert_symbol(name, decl.clone()); + ctx.namespace.insert_symbol(name, decl.clone()); decl } Declaration::TraitDeclaration(trait_decl) => { let trait_decl = check!( - TypedTraitDeclaration::type_check(trait_decl, namespace), + TypedTraitDeclaration::type_check(ctx.by_ref(), trait_decl), return err(warnings, errors), warnings, errors ); let name = trait_decl.name.clone(); let decl = TypedDeclaration::TraitDeclaration(trait_decl); - namespace.insert_symbol(name, decl.clone()); + ctx.namespace.insert_symbol(name, decl.clone()); decl } Declaration::Reassignment(Reassignment { lhs, rhs, span }) => { + let ctx = ctx + .with_type_annotation(insert_type(TypeInfo::Unknown)) + .with_help_text(""); check!( - reassignment( - TypeCheckArguments { - checkee: (lhs, rhs), - namespace, - self_type, - // this is unused by `reassignment` - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text: Default::default(), - mode: Mode::NonAbi, - opts, - }, - span, - ), + reassignment(ctx, lhs, rhs, span), return err(warnings, errors), warnings, errors @@ -420,12 +371,12 @@ impl TypedAstNode { } Declaration::ImplTrait(impl_trait) => { let (impl_trait, implementing_for_type_id) = check!( - TypedImplTrait::type_check_impl_trait(impl_trait, namespace, opts), + TypedImplTrait::type_check_impl_trait(ctx.by_ref(), impl_trait), return err(warnings, errors), warnings, errors ); - namespace.insert_trait_implementation( + ctx.namespace.insert_trait_implementation( impl_trait.trait_name.clone(), implementing_for_type_id, impl_trait.methods.clone(), @@ -434,12 +385,12 @@ impl TypedAstNode { } Declaration::ImplSelf(impl_self) => { let impl_trait = check!( - TypedImplTrait::type_check_impl_self(impl_self, namespace, opts), + TypedImplTrait::type_check_impl_self(ctx.by_ref(), impl_self), return err(warnings, errors), warnings, errors ); - namespace.insert_trait_implementation( + ctx.namespace.insert_trait_implementation( impl_trait.trait_name.clone(), impl_trait.implementing_for_type_id, impl_trait.methods.clone(), @@ -448,7 +399,7 @@ impl TypedAstNode { } Declaration::StructDeclaration(decl) => { let decl = check!( - TypedStructDeclaration::type_check(decl, namespace, self_type), + TypedStructDeclaration::type_check(ctx.by_ref(), decl), return err(warnings, errors), warnings, errors @@ -457,7 +408,7 @@ impl TypedAstNode { let decl = TypedDeclaration::StructDeclaration(decl); // insert the struct decl into namespace let _ = check!( - namespace.insert_symbol(name, decl.clone()), + ctx.namespace.insert_symbol(name, decl.clone()), return err(warnings, errors), warnings, errors @@ -466,39 +417,59 @@ impl TypedAstNode { } Declaration::AbiDeclaration(abi_decl) => { let abi_decl = check!( - TypedAbiDeclaration::type_check(abi_decl, namespace, self_type), + TypedAbiDeclaration::type_check(ctx.by_ref(), abi_decl), return err(warnings, errors), warnings, errors ); let name = abi_decl.name.clone(); let decl = TypedDeclaration::AbiDeclaration(abi_decl); - namespace.insert_symbol(name, decl.clone()); + ctx.namespace.insert_symbol(name, decl.clone()); decl } Declaration::StorageDeclaration(StorageDeclaration { span, fields }) => { let mut fields_buf = Vec::with_capacity(fields.len()); for StorageField { name, - type_info: r#type, + type_info, + initializer, } in fields { - let r#type = check!( - namespace.resolve_type_without_self(r#type), + let type_id = check!( + ctx.namespace.resolve_type_without_self( + insert_type(type_info), + &name.span() + ), return err(warnings, errors), warnings, errors ); - fields_buf.push(TypedStorageField::new(name, r#type, span.clone())); - } + let mut ctx = ctx.by_ref().with_type_annotation(type_id); + let initializer = match initializer { + Some(initializer) => Some(check!( + TypedExpression::type_check(ctx.by_ref(), initializer), + return err(warnings, errors), + warnings, + errors, + )), + None => None, + }; + + fields_buf.push(TypedStorageField::new( + name, + type_id, + initializer, + span.clone(), + )); + } let decl = TypedStorageDeclaration::new(fields_buf, span); // insert the storage declaration into the symbols // if there already was one, return an error that duplicate storage // declarations are not allowed check!( - namespace.set_storage_declaration(decl.clone()), + ctx.namespace.set_storage_declaration(decl.clone()), return err(warnings, errors), warnings, errors @@ -507,45 +478,37 @@ impl TypedAstNode { } }) } - AstNodeContent::Expression(a) => { + AstNodeContent::Expression(expr) => { + let ctx = ctx + .with_type_annotation(insert_type(TypeInfo::Unknown)) + .with_help_text(""); let inner = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: a.clone(), - namespace, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text: Default::default(), - self_type, - mode: Mode::NonAbi, - opts - }), - error_recovery_expr(a.span()), + TypedExpression::type_check(ctx, expr.clone()), + error_recovery_expr(expr.span()), warnings, errors ); TypedAstNodeContent::Expression(inner) } AstNodeContent::ReturnStatement(ReturnStatement { expr }) => { + let ctx = ctx + // we use "unknown" here because return statements do not + // necessarily follow the type annotation of their immediate + // surrounding context. Because a return statement is control flow + // that breaks out to the nearest function, we need to type check + // it against the surrounding function. + // That is impossible here, as we don't have that information. It + // is the responsibility of the function declaration to type check + // all return statements contained within it. + .with_type_annotation(insert_type(TypeInfo::Unknown)) + .with_help_text( + "Returned value must match up with the function return type \ + annotation.", + ); + TypedAstNodeContent::ReturnStatement(TypedReturnStatement { expr: check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: expr.clone(), - namespace, - // we use "unknown" here because return statements do not - // necessarily follow the type annotation of their immediate - // surrounding context. Because a return statement is control flow - // that breaks out to the nearest function, we need to type check - // it against the surrounding function. - // That is impossible here, as we don't have that information. It - // is the responsibility of the function declaration to type check - // all return statements contained within it. - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text: - "Returned value must match up with the function return type \ - annotation.", - self_type, - mode: Mode::NonAbi, - opts - }), + TypedExpression::type_check(ctx, expr.clone()), error_recovery_expr(expr.span()), warnings, errors @@ -553,16 +516,10 @@ impl TypedAstNode { }) } AstNodeContent::ImplicitReturnExpression(expr) => { + let ctx = + ctx.with_help_text("Implicit return must match up with block's type."); let typed_expr = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: expr.clone(), - namespace, - return_type_annotation, - help_text: "Implicit return must match up with block's type.", - self_type, - mode: Mode::NonAbi, - opts, - }), + TypedExpression::type_check(ctx, expr.clone()), error_recovery_expr(expr.span()), warnings, errors @@ -570,34 +527,30 @@ impl TypedAstNode { TypedAstNodeContent::ImplicitReturnExpression(typed_expr) } AstNodeContent::WhileLoop(WhileLoop { condition, body }) => { - let typed_condition = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: condition, - namespace, - return_type_annotation: insert_type(TypeInfo::Boolean), - help_text: + let typed_condition = { + let ctx = ctx + .by_ref() + .with_type_annotation(insert_type(TypeInfo::Boolean)) + .with_help_text( "A while loop's loop condition must be a boolean expression.", - self_type, - mode: Mode::NonAbi, - opts - }), - return err(warnings, errors), - warnings, - errors - ); - let (typed_body, _block_implicit_return) = check!( - TypedCodeBlock::type_check(TypeCheckArguments { - checkee: body, - namespace, - return_type_annotation: insert_type(TypeInfo::Tuple(Vec::new())), - help_text: - "A while loop's loop body cannot implicitly return a value.Try \ + ); + check!( + TypedExpression::type_check(ctx, condition), + return err(warnings, errors), + warnings, + errors + ) + }; + + let ctx = ctx + .with_type_annotation(insert_type(TypeInfo::Tuple(Vec::new()))) + .with_help_text( + "A while loop's loop body cannot implicitly return a value. Try \ assigning it to a mutable variable declared outside of the loop \ instead.", - self_type, - mode: Mode::NonAbi, - opts, - }), + ); + let (typed_body, _block_implicit_return) = check!( + TypedCodeBlock::type_check(ctx, body), ( TypedCodeBlock { contents: vec![] }, insert_type(TypeInfo::Tuple(Vec::new())) @@ -635,16 +588,11 @@ impl TypedAstNode { } fn reassignment( - arguments: TypeCheckArguments<'_, (ReassignmentTarget, Expression)>, + ctx: TypeCheckContext, + lhs: ReassignmentTarget, + rhs: Expression, span: Span, ) -> CompileResult { - let TypeCheckArguments { - checkee: (lhs, rhs), - namespace, - self_type, - opts, - .. - } = arguments; let mut errors = vec![]; let mut warnings = vec![]; // ensure that the lhs is a variable expression or struct field access @@ -657,7 +605,7 @@ fn reassignment( Expression::VariableExpression { name, .. } => { // check that the reassigned name exists let unknown_decl = check!( - namespace.resolve_symbol(&name).cloned(), + ctx.namespace.resolve_symbol(&name).cloned(), return err(warnings, errors), warnings, errors @@ -701,22 +649,15 @@ fn reassignment( }; let names_vec = names_vec.into_iter().rev().collect::>(); let (ty_of_field, _ty_of_parent) = check!( - namespace.find_subfield_type(&base_name, &names_vec), + ctx.namespace.find_subfield_type(&base_name, &names_vec), return err(warnings, errors), warnings, errors ); // type check the reassignment + let ctx = ctx.with_type_annotation(ty_of_field).with_help_text(""); let rhs = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: rhs, - namespace, - return_type_annotation: ty_of_field, - help_text: Default::default(), - self_type, - mode: Mode::NonAbi, - opts, - }), + TypedExpression::type_check(ctx, rhs), error_recovery_expr(span), warnings, errors @@ -733,16 +674,13 @@ fn reassignment( errors, ) } - ReassignmentTarget::StorageField(fields) => reassign_storage_subfield(TypeCheckArguments { - checkee: (fields, span, rhs), - namespace, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text: Default::default(), - self_type, - mode: Mode::NonAbi, - opts, - }) - .map(TypedDeclaration::StorageReassignment), + ReassignmentTarget::StorageField(fields) => { + let ctx = ctx + .with_type_annotation(insert_type(TypeInfo::Unknown)) + .with_help_text(""); + reassign_storage_subfield(ctx, fields, rhs, span) + .map(TypedDeclaration::StorageReassignment) + } } } @@ -778,7 +716,7 @@ fn type_check_interface_surface( is_mutable, type_id: check!( namespace.resolve_type_with_self( - look_up_type_id(type_id), + type_id, insert_type(TypeInfo::SelfType), &type_span, EnforceTypeArguments::Yes @@ -793,7 +731,7 @@ fn type_check_interface_surface( .collect(), return_type: check!( namespace.resolve_type_with_self( - return_type, + insert_type(return_type), insert_type(TypeInfo::SelfType), &return_type_span, EnforceTypeArguments::Yes @@ -809,9 +747,8 @@ fn type_check_interface_surface( } fn type_check_trait_methods( + mut ctx: TypeCheckContext, methods: Vec, - namespace: &mut Namespace, - self_type: TypeId, ) -> CompileResult> { let mut warnings = vec![]; let mut errors = vec![]; @@ -828,16 +765,15 @@ fn type_check_trait_methods( .. } in methods { + // A context while checking the signature where `self_type` refers to `SelfType`. + let mut sig_ctx = ctx.by_ref().with_self_type(insert_type(TypeInfo::SelfType)); parameters.clone().into_iter().for_each( |FunctionParameter { - name, - type_id: ref r#type, - .. + name, ref type_id, .. }| { let r#type = check!( - namespace.resolve_type_with_self( - look_up_type_id(*r#type), - insert_type(TypeInfo::SelfType), + sig_ctx.resolve_type_with_self( + *type_id, &name.span(), EnforceTypeArguments::Yes ), @@ -845,7 +781,7 @@ fn type_check_trait_methods( warnings, errors, ); - namespace.insert_symbol( + sig_ctx.namespace.insert_symbol( name.clone(), TypedDeclaration::VariableDeclaration(TypedVariableDeclaration { name: name.clone(), @@ -857,7 +793,6 @@ fn type_check_trait_methods( }, // TODO allow mutable function params? is_mutable: VariableMutability::Immutable, - const_decl_origin: false, type_ascription: r#type, }), ); @@ -915,9 +850,8 @@ fn type_check_trait_methods( name, is_mutable, type_id: check!( - namespace.resolve_type_with_self( - look_up_type_id(type_id), - crate::type_engine::insert_type(TypeInfo::SelfType), + sig_ctx.resolve_type_with_self( + type_id, &type_span, EnforceTypeArguments::Yes ), @@ -933,9 +867,8 @@ fn type_check_trait_methods( // TODO check code block implicit return let return_type = check!( - namespace.resolve_type_with_self( - return_type, - self_type, + ctx.resolve_type_with_self( + insert_type(return_type), &return_type_span, EnforceTypeArguments::Yes ), @@ -943,17 +876,16 @@ fn type_check_trait_methods( warnings, errors, ); + let ctx = ctx + .by_ref() + .with_purity(purity) + .with_type_annotation(return_type) + .with_help_text( + "Trait method body's return type does not match up with its return type \ + annotation.", + ); let (body, _code_block_implicit_return) = check!( - TypedCodeBlock::type_check(TypeCheckArguments { - checkee: body, - namespace, - return_type_annotation: return_type, - help_text: "Trait method body's return type does not match up with \ - its return type annotation.", - self_type, - mode: Mode::NonAbi, - opts: TCOpts { purity } - }), + TypedCodeBlock::type_check(ctx, body), continue, warnings, errors @@ -1050,27 +982,21 @@ impl PartialEq for TypeCheckedStorageReassignDescriptor { } fn reassign_storage_subfield( - arguments: TypeCheckArguments<'_, (Vec, Span, Expression)>, + ctx: TypeCheckContext, + fields: Vec, + rhs: Expression, + span: Span, ) -> CompileResult { - let TypeCheckArguments { - checkee: (fields, span, rhs), - namespace, - return_type_annotation: _return_type_annotation, - help_text: _help_text, - self_type, - opts, - .. - } = arguments; let mut errors = vec![]; let mut warnings = vec![]; - if !namespace.has_storage_declared() { + if !ctx.namespace.has_storage_declared() { errors.push(CompileError::NoDeclaredStorage { span }); return err(warnings, errors); } let storage_fields = check!( - namespace.get_storage_field_descriptors(), + ctx.namespace.get_storage_field_descriptors(), return err(warnings, errors), warnings, errors @@ -1146,16 +1072,9 @@ fn reassign_storage_subfield( } } } + let ctx = ctx.with_type_annotation(curr_type).with_help_text(""); let rhs = check!( - TypedExpression::type_check(TypeCheckArguments { - checkee: rhs, - namespace, - return_type_annotation: curr_type, - help_text: Default::default(), - self_type, - mode: Mode::NonAbi, - opts, - }), + TypedExpression::type_check(ctx, rhs), error_recovery_expr(span), warnings, errors diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index 08307da5d4a..e662c429f7e 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -19,13 +19,13 @@ impl TypedModule { /// Type-check the given parsed module to produce a typed module. /// /// Recursively type-checks submodules first. - pub fn type_check(parsed: ParseModule, namespace: &mut Namespace) -> CompileResult { + pub fn type_check(mut ctx: TypeCheckContext, parsed: ParseModule) -> CompileResult { let ParseModule { submodules, tree } = parsed; // Type-check submodules first in order of declaration. let mut submodules_res = ok(vec![], vec![], vec![]); for (name, submodule) in submodules { - let submodule_res = TypedSubmodule::type_check(name.clone(), submodule, namespace); + let submodule_res = TypedSubmodule::type_check(ctx.by_ref(), name.clone(), submodule); submodules_res = submodules_res.flat_map(|mut submodules| { submodule_res.map(|submodule| { submodules.push((name, submodule)); @@ -38,41 +38,31 @@ impl TypedModule { let ordered_nodes_res = node_dependencies::order_ast_nodes_by_dependency(tree.root_nodes); let typed_nodes_res = ordered_nodes_res - .flat_map(|ordered_nodes| Self::type_check_nodes(ordered_nodes, namespace)); + .flat_map(|ordered_nodes| Self::type_check_nodes(ctx.by_ref(), ordered_nodes)); let validated_nodes_res = typed_nodes_res.flat_map(|typed_nodes| { - let errors = check_supertraits(&typed_nodes, namespace); + let errors = check_supertraits(&typed_nodes, ctx.namespace); ok(typed_nodes, vec![], errors) }); submodules_res.flat_map(|submodules| { validated_nodes_res.map(|all_nodes| Self { submodules, - namespace: namespace.module().clone(), + namespace: ctx.namespace.module().clone(), all_nodes, }) }) } fn type_check_nodes( + mut ctx: TypeCheckContext, nodes: Vec, - namespace: &mut Namespace, ) -> CompileResult> { let mut warnings = Vec::new(); let mut errors = Vec::new(); let typed_nodes = nodes .into_iter() - .map(|node| { - TypedAstNode::type_check(TypeCheckArguments { - checkee: node, - namespace, - return_type_annotation: insert_type(TypeInfo::Unknown), - help_text: Default::default(), - self_type: insert_type(TypeInfo::Contract), - mode: Mode::NonAbi, - opts: Default::default(), - }) - }) + .map(|node| TypedAstNode::type_check(ctx.by_ref(), node)) .filter_map(|res| res.ok(&mut warnings, &mut errors)) .collect(); @@ -86,19 +76,20 @@ impl TypedModule { impl TypedSubmodule { pub fn type_check( + parent_ctx: TypeCheckContext, dep_name: DepName, submodule: ParseSubmodule, - parent_namespace: &mut Namespace, ) -> CompileResult { let ParseSubmodule { library_name, module, } = submodule; - let mut dep_namespace = parent_namespace.enter_submodule(dep_name); - let module_res = TypedModule::type_check(module, &mut dep_namespace); - module_res.map(|module| TypedSubmodule { - library_name, - module, + parent_ctx.enter_submodule(dep_name, |submod_ctx| { + let module_res = TypedModule::type_check(submod_ctx, module); + module_res.map(|module| TypedSubmodule { + library_name, + module, + }) }) } } diff --git a/sway-core/src/semantic_analysis/namespace/items.rs b/sway-core/src/semantic_analysis/namespace/items.rs index 995b5885c47..92f137fc7a4 100644 --- a/sway-core/src/semantic_analysis/namespace/items.rs +++ b/sway-core/src/semantic_analysis/namespace/items.rs @@ -138,33 +138,6 @@ impl Items { .get_methods_for_type(implementing_for_type_id) } - // Given a TypeInfo old_type with a set of methods available to it, make those same methods - // available to TypeInfo new_type. This is useful in situations where old_type is being - // monomorphized to new_type and and we want `get_methods_for_type()` to return the same set of - // methods for new_type as it does for old_type. - pub(crate) fn copy_methods_to_type( - &mut self, - old_type: TypeId, - new_type: TypeId, - type_mapping: &TypeMapping, - ) { - // This map grabs all (trait name, vec of methods) from self.implemented_traits - // corresponding to `old_type`. - let methods = self - .implemented_traits - .get_methods_for_type_by_trait(old_type); - - // Insert into `self.implemented_traits` the contents of the map above but with `new_type` - // as the `TypeInfo` key. - for (trait_name, mut trait_methods) in methods.into_iter() { - trait_methods - .iter_mut() - .for_each(|method| method.copy_types(type_mapping)); - self.implemented_traits - .insert(trait_name, new_type, trait_methods); - } - } - pub(crate) fn get_canonical_path(&self, symbol: &Ident) -> &[Ident] { self.use_synonyms.get(symbol).map(|v| &v[..]).unwrap_or(&[]) } diff --git a/sway-core/src/semantic_analysis/namespace/namespace.rs b/sway-core/src/semantic_analysis/namespace/namespace.rs index 7e8ad5d8d3b..187e260659d 100644 --- a/sway-core/src/semantic_analysis/namespace/namespace.rs +++ b/sway-core/src/semantic_analysis/namespace/namespace.rs @@ -1,16 +1,11 @@ use crate::{ - semantic_analysis::{ - ast_node::TypedExpression, - declaration::{EnforceTypeArguments, Monomorphize, MonomorphizeHelper}, - }, - type_engine::*, - CallPath, CompileResult, Ident, TypeArgument, TypeInfo, TypedDeclaration, - TypedFunctionDeclaration, + semantic_analysis::ast_node::TypedExpression, type_engine::*, CallPath, CompileResult, Ident, + TypedDeclaration, TypedFunctionDeclaration, }; use super::{module::Module, root::Root, submodule_namespace::SubmoduleNamespace, Path, PathBuf}; -use sway_types::{span::Span, Spanned}; +use sway_types::span::Span; use std::collections::VecDeque; @@ -100,49 +95,24 @@ impl Namespace { /// Short-hand for calling [Root::resolve_type_with_self] on `root` with the `mod_path`. pub(crate) fn resolve_type_with_self( &mut self, - type_info: TypeInfo, + mut type_id: TypeId, self_type: TypeId, span: &Span, enforce_type_args: EnforceTypeArguments, ) -> CompileResult { - self.root.resolve_type_with_self( - type_info, - self_type, - span, - enforce_type_args, - &self.mod_path, - ) + type_id.replace_self_type(self_type); + self.root + .resolve_type(type_id, span, enforce_type_args, &self.mod_path) } /// Short-hand for calling [Root::resolve_type_without_self] on `root` and with the `mod_path`. pub(crate) fn resolve_type_without_self( &mut self, - type_info: TypeInfo, + type_id: TypeId, + span: &Span, ) -> CompileResult { self.root - .resolve_type_without_self(type_info, &self.mod_path) - } - - /// Short-hand for calling `monomorphize` from the `Monomorphize` trait, on `root` with the `mod_path`. - pub(crate) fn monomorphize( - &mut self, - decl: T, - type_arguments: Vec, - enforce_type_arguments: EnforceTypeArguments, - self_type: Option, - call_site_span: Option<&Span>, - ) -> CompileResult - where - T: MonomorphizeHelper + Spanned, - { - decl.monomorphize( - type_arguments, - enforce_type_arguments, - self_type, - call_site_span, - &mut self.root, - &self.mod_path, - ) + .resolve_type(type_id, span, EnforceTypeArguments::Yes, &self.mod_path) } /// Short-hand for calling [Root::find_method_for_type] on `root` with the `mod_path`. diff --git a/sway-core/src/semantic_analysis/namespace/root.rs b/sway-core/src/semantic_analysis/namespace/root.rs index d32aa8529b8..dd9581c042d 100644 --- a/sway-core/src/semantic_analysis/namespace/root.rs +++ b/sway-core/src/semantic_analysis/namespace/root.rs @@ -64,17 +64,16 @@ impl Root { }) } - pub(crate) fn resolve_type_with_self( + pub(crate) fn resolve_type( &mut self, - type_info: TypeInfo, - self_type: TypeId, + type_id: TypeId, span: &Span, enforce_type_arguments: EnforceTypeArguments, mod_path: &Path, ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; - let type_id = match type_info { + let type_id = match look_up_type_id(type_id) { TypeInfo::Custom { ref name, type_arguments, @@ -84,13 +83,13 @@ impl Root { .ok(&mut warnings, &mut errors) .cloned() { - Some(TypedDeclaration::StructDeclaration(decl)) => { - let new_decl = check!( - decl.monomorphize( + Some(TypedDeclaration::StructDeclaration(mut decl)) => { + check!( + monomorphize( + &mut decl, type_arguments, enforce_type_arguments, - Some(self_type), - Some(span), + span, self, mod_path // NOTE: Once `TypeInfo::Custom` takes a `CallPath`, this will need to change ), @@ -98,15 +97,15 @@ impl Root { warnings, errors ); - new_decl.create_type_id() + decl.create_type_id() } - Some(TypedDeclaration::EnumDeclaration(decl)) => { - let new_decl = check!( - decl.monomorphize( + Some(TypedDeclaration::EnumDeclaration(mut decl)) => { + check!( + monomorphize( + &mut decl, type_arguments, enforce_type_arguments, - Some(self_type), - Some(span), + span, self, mod_path // NOTE: Once `TypeInfo::Custom` takes a `CallPath`, this will need to change ), @@ -114,7 +113,7 @@ impl Root { warnings, errors ); - new_decl.create_type_id() + decl.create_type_id() } Some(TypedDeclaration::GenericTypeForFunctionScope { name, type_id }) => { insert_type(TypeInfo::Ref(type_id, name.span())) @@ -124,21 +123,14 @@ impl Root { name: name.to_string(), span: name.span(), }); - return err(warnings, errors); + insert_type(TypeInfo::ErrorRecovery) } } } - TypeInfo::SelfType => self_type, TypeInfo::Ref(id, _) => id, TypeInfo::Array(type_id, n) => { let new_type_id = check!( - self.resolve_type_with_self( - look_up_type_id(type_id), - self_type, - span, - enforce_type_arguments, - mod_path - ), + self.resolve_type(type_id, span, enforce_type_arguments, mod_path), insert_type(TypeInfo::ErrorRecovery), warnings, errors @@ -148,9 +140,8 @@ impl Root { TypeInfo::Tuple(mut type_arguments) => { for type_argument in type_arguments.iter_mut() { type_argument.type_id = check!( - self.resolve_type_with_self( - look_up_type_id(type_argument.type_id), - self_type, + self.resolve_type( + type_argument.type_id, span, enforce_type_arguments, mod_path @@ -167,90 +158,6 @@ impl Root { ok(type_id, warnings, errors) } - pub(crate) fn resolve_type_without_self( - &mut self, - type_info: TypeInfo, - mod_path: &Path, - ) -> CompileResult { - let mut warnings = vec![]; - let mut errors = vec![]; - let type_id = match type_info { - TypeInfo::Custom { - name, - type_arguments, - } => { - match self - .resolve_symbol(mod_path, &name) - .ok(&mut warnings, &mut errors) - .cloned() - { - Some(TypedDeclaration::StructDeclaration(decl)) => { - let new_decl = check!( - decl.monomorphize( - type_arguments, - EnforceTypeArguments::No, - None, - None, - self, - mod_path // NOTE: Once `TypeInfo::Custom` takes a `CallPath`, this will need to change - ), - return err(warnings, errors), - warnings, - errors - ); - new_decl.create_type_id() - } - Some(TypedDeclaration::EnumDeclaration(decl)) => { - let new_decl = check!( - decl.monomorphize( - type_arguments, - EnforceTypeArguments::No, - None, - None, - self, - mod_path // NOTE: Once `TypeInfo::Custom` takes a `CallPath`, this will need to change - ), - return err(warnings, errors), - warnings, - errors - ); - new_decl.create_type_id() - } - Some(TypedDeclaration::GenericTypeForFunctionScope { name, type_id }) => { - insert_type(TypeInfo::Ref(type_id, name.span())) - } - _ => insert_type(TypeInfo::Unknown), - } - } - TypeInfo::Ref(id, _) => id, - TypeInfo::Array(type_id, n) => { - let new_type_id = check!( - self.resolve_type_without_self(look_up_type_id(type_id), mod_path), - insert_type(TypeInfo::ErrorRecovery), - warnings, - errors - ); - insert_type(TypeInfo::Array(new_type_id, n)) - } - TypeInfo::Tuple(mut type_arguments) => { - for type_argument in type_arguments.iter_mut() { - type_argument.type_id = check!( - self.resolve_type_without_self( - look_up_type_id(type_argument.type_id), - mod_path - ), - insert_type(TypeInfo::ErrorRecovery), - warnings, - errors - ); - } - insert_type(TypeInfo::Tuple(type_arguments)) - } - o => insert_type(o), - }; - ok(type_id, warnings, errors) - } - /// Given a method and a type (plus a `self_type` to potentially resolve it), find that method /// in the namespace. Requires `args_buf` because of some special casing for the standard /// library where we pull the type from the arguments buffer. @@ -262,7 +169,7 @@ impl Root { pub(crate) fn find_method_for_type( &mut self, mod_path: &Path, - r#type: TypeId, + mut type_id: TypeId, method_path: &Path, self_type: TypeId, args_buf: &VecDeque, @@ -279,16 +186,17 @@ impl Root { ); // grab the local methods from the local module - let local_methods = local_module.get_methods_for_type(r#type); + let local_methods = local_module.get_methods_for_type(type_id); // split into the method name and method prefix let (method_name, method_prefix) = method_path.split_last().expect("method path is empty"); + type_id.replace_self_type(self_type); + // resolve the type - let r#type = check!( - self.resolve_type_with_self( - look_up_type_id(r#type), - self_type, + let type_id = check!( + self.resolve_type( + type_id, &method_name.span(), EnforceTypeArguments::No, method_prefix @@ -307,7 +215,7 @@ impl Root { ); // grab the methods from where the type is declared - let mut type_methods = type_module.get_methods_for_type(r#type); + let mut type_methods = type_module.get_methods_for_type(type_id); let mut methods = local_methods; methods.append(&mut type_methods); @@ -323,7 +231,7 @@ impl Root { { errors.push(CompileError::MethodNotFound { method_name: method_name.clone(), - type_name: r#type.to_string(), + type_name: type_id.to_string(), }); } err(warnings, errors) diff --git a/sway-core/src/semantic_analysis/namespace/trait_map.rs b/sway-core/src/semantic_analysis/namespace/trait_map.rs index ee6202df506..72f9c1eb890 100644 --- a/sway-core/src/semantic_analysis/namespace/trait_map.rs +++ b/sway-core/src/semantic_analysis/namespace/trait_map.rs @@ -1,10 +1,8 @@ use crate::{ - type_engine::{look_up_type_id, TypeId}, + type_engine::{create_type_mapping, look_up_type_id, CopyTypes, TypeId}, CallPath, TypeInfo, TypedFunctionDeclaration, }; -use std::collections::HashMap; - type TraitName = CallPath; // This cannot be a HashMap because of how TypeInfo's are handled. @@ -46,8 +44,7 @@ impl TraitMap { ) { let mut methods_map = im::HashMap::new(); for method in methods.into_iter() { - let method_name = method.name.as_str().to_string(); - methods_map.insert(method_name, method); + methods_map.insert(method.name.as_str().to_string(), method); } self.trait_map .push_back(((trait_name, incoming_type_id), methods_map)); @@ -69,7 +66,7 @@ impl TraitMap { ) -> Vec<((CallPath, TypeId), Vec)> { let mut ret = vec![]; for ((call_path, map_type_id), methods) in self.trait_map.iter() { - if look_up_type_id(*map_type_id) == look_up_type_id(incoming_type_id) { + if look_up_type_id(incoming_type_id).is_subset_of(&look_up_type_id(*map_type_id)) { ret.push(( (call_path.clone(), *map_type_id), methods.values().cloned().collect(), @@ -88,29 +85,14 @@ impl TraitMap { if look_up_type_id(incoming_type_id) == TypeInfo::ErrorRecovery { return methods; } - for ((_, map_type_id), l_methods) in self.trait_map.iter() { - if look_up_type_id(*map_type_id) == look_up_type_id(incoming_type_id) { - methods.append(&mut l_methods.values().cloned().collect()); - } - } - methods - } - - pub(crate) fn get_methods_for_type_by_trait( - &self, - incoming_type_id: TypeId, - ) -> HashMap> { - let mut methods: HashMap> = HashMap::new(); - // small performance gain in bad case - if look_up_type_id(incoming_type_id) == TypeInfo::ErrorRecovery { - return methods; - } - for ((trait_name, map_type_id), trait_methods) in self.trait_map.iter() { - if look_up_type_id(*map_type_id) == look_up_type_id(incoming_type_id) { - methods.insert( - (*trait_name).clone(), - trait_methods.values().cloned().collect(), - ); + for ((_, map_type_id), trait_methods) in self.trait_map.iter() { + if look_up_type_id(incoming_type_id).is_subset_of(&look_up_type_id(*map_type_id)) { + let type_mapping = create_type_mapping(*map_type_id, incoming_type_id); + let mut trait_methods = trait_methods.values().cloned().collect::>(); + trait_methods + .iter_mut() + .for_each(|x| x.copy_types(&type_mapping)); + methods.append(&mut trait_methods); } } methods diff --git a/sway-core/src/semantic_analysis/node_dependencies.rs b/sway-core/src/semantic_analysis/node_dependencies.rs index c9f84691114..69e7006f0f2 100644 --- a/sway-core/src/semantic_analysis/node_dependencies.rs +++ b/sway-core/src/semantic_analysis/node_dependencies.rs @@ -1,6 +1,7 @@ use std::collections::{HashMap, HashSet}; use std::iter::FromIterator; +use crate::type_engine::{TypeArgument, TypeParameter}; use crate::{ error::*, parse_tree::*, @@ -474,10 +475,9 @@ impl Dependencies { } Expression::TupleIndex { prefix, .. } => self.gather_from_expr(prefix), Expression::StorageAccess { .. } => self, - Expression::IntrinsicFunction { kind, .. } => match kind { - IntrinsicFunctionKind::SizeOfVal { exp } => self.gather_from_expr(exp), - _ => self, - }, + Expression::IntrinsicFunction { arguments, .. } => { + self.gather_from_iter(arguments.iter(), |deps, arg| deps.gather_from_expr(arg)) + } } } diff --git a/sway-core/src/semantic_analysis/program.rs b/sway-core/src/semantic_analysis/program.rs index 02c314428b8..ed05292ac25 100644 --- a/sway-core/src/semantic_analysis/program.rs +++ b/sway-core/src/semantic_analysis/program.rs @@ -7,11 +7,12 @@ use crate::{ parse_tree::{ParseProgram, Purity, TreeType}, semantic_analysis::{ namespace::{self, Namespace}, - TypedModule, + TypeCheckContext, TypedModule, }, type_engine::*, types::ToJsonAbi, }; +use fuel_tx::StorageSlot; use fuels_types::JsonABI; use sway_types::{span::Span, Ident, Spanned}; @@ -19,6 +20,7 @@ use sway_types::{span::Span, Ident, Spanned}; pub struct TypedProgram { pub kind: TypedProgramKind, pub root: TypedModule, + pub storage_slots: Vec, } impl TypedProgram { @@ -31,12 +33,17 @@ impl TypedProgram { initial_namespace: namespace::Module, ) -> CompileResult { let mut namespace = Namespace::init_root(initial_namespace); + let ctx = TypeCheckContext::from_root(&mut namespace); let ParseProgram { root, kind } = parsed; let mod_span = root.tree.span.clone(); - let mod_res = TypedModule::type_check(root, &mut namespace); + let mod_res = TypedModule::type_check(ctx, root); mod_res.flat_map(|root| { let kind_res = Self::validate_root(&root, kind, mod_span); - kind_res.map(|kind| Self { kind, root }) + kind_res.map(|kind| Self { + kind, + root, + storage_slots: vec![], + }) }) } @@ -220,6 +227,60 @@ impl TypedProgram { err(vec![], errors) } } + + pub fn get_typed_program_with_initialized_storage_slots(&self) -> CompileResult { + let mut warnings = vec![]; + let mut errors = vec![]; + match &self.kind { + TypedProgramKind::Contract { declarations, .. } => { + let storage_decl = declarations + .iter() + .find(|decl| matches!(decl, TypedDeclaration::StorageDeclaration(_))); + + // Expecting at most a single storage declaration + match storage_decl { + Some(TypedDeclaration::StorageDeclaration(decl)) => { + let mut storage_slots = check!( + decl.get_initialized_storage_slots(), + return err(warnings, errors), + warnings, + errors, + ); + // Sort the slots to standardize the output. Not strictly required by the + // spec. + storage_slots.sort(); + ok( + Self { + kind: self.kind.clone(), + root: self.root.clone(), + storage_slots, + }, + warnings, + errors, + ) + } + _ => ok( + Self { + kind: self.kind.clone(), + root: self.root.clone(), + storage_slots: vec![], + }, + warnings, + errors, + ), + } + } + _ => ok( + Self { + kind: self.kind.clone(), + root: self.root.clone(), + storage_slots: vec![], + }, + warnings, + errors, + ), + } + } } #[derive(Clone, Debug)] diff --git a/sway-core/src/semantic_analysis/type_check_arguments.rs b/sway-core/src/semantic_analysis/type_check_arguments.rs deleted file mode 100644 index 11d9adf467e..00000000000 --- a/sway-core/src/semantic_analysis/type_check_arguments.rs +++ /dev/null @@ -1,18 +0,0 @@ -use crate::parse_tree::declaration::Purity; -use crate::semantic_analysis::{ast_node::Mode, *}; -use crate::type_engine::*; - -pub struct TypeCheckArguments<'a, T> { - pub checkee: T, - pub namespace: &'a mut Namespace, - pub return_type_annotation: TypeId, - pub help_text: &'static str, - pub self_type: TypeId, - pub mode: Mode, - pub opts: TCOpts, -} - -#[derive(Default, Clone, Copy)] -pub struct TCOpts { - pub(crate) purity: Purity, -} diff --git a/sway-core/src/semantic_analysis/type_check_context.rs b/sway-core/src/semantic_analysis/type_check_context.rs new file mode 100644 index 00000000000..88130d509e4 --- /dev/null +++ b/sway-core/src/semantic_analysis/type_check_context.rs @@ -0,0 +1,224 @@ +use crate::{ + parse_tree::declaration::Purity, + semantic_analysis::{ast_node::Mode, Namespace}, + type_engine::{ + insert_type, monomorphize, unify_with_self, CopyTypes, EnforceTypeArguments, + MonomorphizeHelper, TypeArgument, TypeId, TypeInfo, + }, + CompileResult, CompileWarning, TypeError, +}; +use sway_types::{span::Span, Ident}; + +/// Contextual state tracked and accumulated throughout type-checking. +pub struct TypeCheckContext<'ns> { + /// The namespace context accumulated throughout type-checking. + /// + /// Internally, this includes: + /// + /// - The `root` module from which all other modules maybe be accessed using absolute paths. + /// - The `init` module used to initialise submodule namespaces. + /// - A `mod_path` that represents the current module being type-checked. This is automatically + /// updated upon entering/exiting submodules via the `enter_submodule` method. + pub(crate) namespace: &'ns mut Namespace, + + // The following set of fields are intentionally private. When a `TypeCheckContext` is passed + // into a new node during type checking, these fields should be updated using the `with_*` + // methods which provides a new `TypeCheckContext`, ensuring we don't leak our changes into + // the parent nodes. + /// While type-checking an `impl` (whether inherent or for a `trait`/`abi`) this represents the + /// type for which we are implementing. For example in `impl Foo {}` or `impl Trait for Foo + /// {}`, this represents the type ID of `Foo`. + self_type: TypeId, + /// While type-checking an expression, this indicates the expected type. + /// + /// Assists type inference. + type_annotation: TypeId, + /// Whether or not we're within an `abi` implementation. + /// + /// This is `ImplAbiFn` while checking `abi` implementations whether at their original impl + /// declaration or within an abi cast expression. + mode: Mode, + /// Provides "help text" to `TypeError`s during unification. + // TODO: We probably shouldn't carry this through the `Context`, but instead pass it directly + // to `unify` as necessary? + help_text: &'static str, + /// Tracks the purity of the context, e.g. whether or not we should be allowed to write to + /// storage. + purity: Purity, +} + +impl<'ns> TypeCheckContext<'ns> { + /// Initialise a context at the top-level of a module with its namespace. + /// + /// Initializes with: + /// + /// - type_annotation: unknown + /// - mode: NoneAbi + /// - help_text: "" + /// - purity: Pure + pub fn from_root(root_namespace: &'ns mut Namespace) -> Self { + Self::from_module_namespace(root_namespace) + } + + fn from_module_namespace(namespace: &'ns mut Namespace) -> Self { + Self { + namespace, + type_annotation: insert_type(TypeInfo::Unknown), + help_text: "", + // TODO: Contract? Should this be passed in based on program kind (aka TreeType)? + self_type: insert_type(TypeInfo::Contract), + mode: Mode::NonAbi, + purity: Purity::default(), + } + } + + /// Create a new context that mutably borrows the inner `namespace` with a lifetime bound by + /// `self`. + /// + /// This is particularly useful when type-checking a node that has more than one child node + /// (very often the case). By taking the context with the namespace lifetime bound to `self` + /// rather than the original namespace reference, we instead restrict the returned context to + /// the local scope and avoid consuming the original context when providing context to the + /// first visited child node. + pub fn by_ref(&mut self) -> TypeCheckContext { + TypeCheckContext { + namespace: self.namespace, + type_annotation: self.type_annotation, + self_type: self.self_type, + mode: self.mode, + help_text: self.help_text, + purity: self.purity, + } + } + + /// Scope the `TypeCheckContext` with the given `Namespace`. + pub fn scoped(self, namespace: &mut Namespace) -> TypeCheckContext { + TypeCheckContext { + namespace, + type_annotation: self.type_annotation, + self_type: self.self_type, + mode: self.mode, + help_text: self.help_text, + purity: self.purity, + } + } + + /// Enter the submodule with the given name and produce a type-check context ready for + /// type-checking its content. + /// + /// Returns the result of the given `with_submod_ctx` function. + pub fn enter_submodule(self, dep_name: Ident, with_submod_ctx: F) -> T + where + F: FnOnce(TypeCheckContext) -> T, + { + // We're checking a submodule, so no need to pass through anything other than the + // namespace. However, we will likely want to pass through the type engine and declaration + // engine here once they're added. + let Self { namespace, .. } = self; + let mut submod_ns = namespace.enter_submodule(dep_name); + let submod_ctx = TypeCheckContext::from_module_namespace(&mut submod_ns); + with_submod_ctx(submod_ctx) + } + + /// Map this `TypeCheckContext` instance to a new one with the given `help_text`. + pub(crate) fn with_help_text(self, help_text: &'static str) -> Self { + Self { help_text, ..self } + } + + /// Map this `TypeCheckContext` instance to a new one with the given type annotation. + pub(crate) fn with_type_annotation(self, type_annotation: TypeId) -> Self { + Self { + type_annotation, + ..self + } + } + + /// Map this `TypeCheckContext` instance to a new one with the given ABI `mode`. + pub(crate) fn with_mode(self, mode: Mode) -> Self { + Self { mode, ..self } + } + + /// Map this `TypeCheckContext` instance to a new one with the given purity. + pub(crate) fn with_purity(self, purity: Purity) -> Self { + Self { purity, ..self } + } + + /// Map this `TypeCheckContext` instance to a new one with the given purity. + pub(crate) fn with_self_type(self, self_type: TypeId) -> Self { + Self { self_type, ..self } + } + + // A set of accessor methods. We do this rather than making the fields `pub` in order to ensure + // that these are only updated via the `with_*` methods that produce a new `TypeCheckContext`. + + pub(crate) fn help_text(&self) -> &'static str { + self.help_text + } + + pub(crate) fn type_annotation(&self) -> TypeId { + self.type_annotation + } + + pub(crate) fn mode(&self) -> Mode { + self.mode + } + + pub(crate) fn purity(&self) -> Purity { + self.purity + } + + pub(crate) fn self_type(&self) -> TypeId { + self.self_type + } + + // Provide some convenience functions around the inner context. + + /// Short-hand for calling the `monomorphize` function in the type engine + pub(crate) fn monomorphize( + &mut self, + value: &mut T, + type_arguments: Vec, + enforce_type_arguments: EnforceTypeArguments, + call_site_span: &Span, + ) -> CompileResult<()> + where + T: MonomorphizeHelper + CopyTypes, + { + monomorphize( + value, + type_arguments, + enforce_type_arguments, + call_site_span, + &mut self.namespace.root, + &self.namespace.mod_path, + ) + } + + /// Short-hand for calling [Namespace::resolve_type_with_self] with the `self_type` provided by + /// the `TypeCheckContext`. + pub(crate) fn resolve_type_with_self( + &mut self, + type_id: TypeId, + span: &Span, + enforce_type_args: EnforceTypeArguments, + ) -> CompileResult { + self.namespace + .resolve_type_with_self(type_id, self.self_type, span, enforce_type_args) + } + + /// Short-hand around `type_engine::unify_with_self`, where the `TypeCheckContext` provides the + /// type annotation, self type and help text. + pub(crate) fn unify_with_self( + &self, + ty: TypeId, + span: &Span, + ) -> (Vec, Vec) { + unify_with_self( + ty, + self.type_annotation(), + self.self_type(), + span, + self.help_text(), + ) + } +} diff --git a/sway-core/src/type_engine/engine.rs b/sway-core/src/type_engine/engine.rs index c7f089ba5ae..bbd30a4ab10 100644 --- a/sway-core/src/type_engine/engine.rs +++ b/sway-core/src/type_engine/engine.rs @@ -1,9 +1,10 @@ use super::*; use crate::concurrent_slab::ConcurrentSlab; +use crate::namespace::{Path, Root}; use crate::type_engine::AbiName; use lazy_static::lazy_static; use sway_types::span::Span; -use sway_types::Spanned; +use sway_types::{Ident, Spanned}; lazy_static! { static ref TYPE_ENGINE: Engine = Engine::default(); @@ -30,6 +31,95 @@ impl Engine { } } + fn monomorphize( + &self, + value: &mut T, + mut type_arguments: Vec, + enforce_type_arguments: EnforceTypeArguments, + call_site_span: &Span, + namespace: &mut Root, + module_path: &Path, + ) -> CompileResult<()> + where + T: MonomorphizeHelper + CopyTypes, + { + let mut warnings = vec![]; + let mut errors = vec![]; + match ( + value.type_parameters().is_empty(), + type_arguments.is_empty(), + ) { + (true, true) => ok((), warnings, errors), + (false, true) => { + if let EnforceTypeArguments::Yes = enforce_type_arguments { + errors.push(CompileError::NeedsTypeArguments { + name: value.name().clone(), + span: call_site_span.clone(), + }); + return err(warnings, errors); + } + let type_mapping = insert_type_parameters(value.type_parameters()); + value.copy_types(&type_mapping); + ok((), warnings, errors) + } + (true, false) => { + let type_arguments_span = type_arguments + .iter() + .map(|x| x.span.clone()) + .reduce(Span::join) + .unwrap_or_else(|| value.name().span()); + errors.push(CompileError::DoesNotTakeTypeArguments { + name: value.name().clone(), + span: type_arguments_span, + }); + err(warnings, errors) + } + (false, false) => { + let type_arguments_span = type_arguments + .iter() + .map(|x| x.span.clone()) + .reduce(Span::join) + .unwrap_or_else(|| value.name().span()); + if value.type_parameters().len() != type_arguments.len() { + errors.push(CompileError::IncorrectNumberOfTypeArguments { + given: type_arguments.len(), + expected: value.type_parameters().len(), + span: type_arguments_span, + }); + return err(warnings, errors); + } + for type_argument in type_arguments.iter_mut() { + type_argument.type_id = check!( + namespace.resolve_type( + type_argument.type_id, + &type_argument.span, + enforce_type_arguments, + module_path + ), + insert_type(TypeInfo::ErrorRecovery), + warnings, + errors + ); + } + let type_mapping = insert_type_parameters(value.type_parameters()); + for ((_, interim_type), type_argument) in + type_mapping.iter().zip(type_arguments.iter()) + { + let (mut new_warnings, new_errors) = unify( + *interim_type, + type_argument.type_id, + &type_argument.span, + "Type argument is not assignable to generic type parameter.", + ); + warnings.append(&mut new_warnings); + errors.append(&mut new_errors.into_iter().map(|x| x.into()).collect()); + } + value.copy_types(&type_mapping); + ok((), warnings, errors) + } + } + } + /// Make the types of two type terms equivalent (or produce an error if /// there is a conflict between them). // @@ -380,6 +470,27 @@ pub(crate) fn look_up_type_id_raw(id: TypeId) -> TypeInfo { TYPE_ENGINE.look_up_type_id_raw(id) } +pub(crate) fn monomorphize( + value: &mut T, + type_arguments: Vec, + enforce_type_arguments: EnforceTypeArguments, + call_site_span: &Span, + namespace: &mut Root, + module_path: &Path, +) -> CompileResult<()> +where + T: MonomorphizeHelper + CopyTypes, +{ + TYPE_ENGINE.monomorphize( + value, + type_arguments, + enforce_type_arguments, + call_site_span, + namespace, + module_path, + ) +} + pub fn unify_with_self( a: TypeId, b: TypeId, @@ -427,3 +538,43 @@ enum NumericCastCompatResult { Compatible, CastableWithWarning(Warning), } + +pub(crate) trait MonomorphizeHelper { + fn name(&self) -> &Ident; + fn type_parameters(&self) -> &[TypeParameter]; +} + +/// This type is used to denote if, during monomorphization, the compiler +/// should enforce that type arguments be provided. An example of that +/// might be this: +/// +/// ```ignore +/// struct Point { +/// x: u64, +/// y: u64 +/// } +/// +/// fn add(p1: Point, p2: Point) -> Point { +/// Point { +/// x: p1.x + p2.x, +/// y: p1.y + p2.y +/// } +/// } +/// ``` +/// +/// `EnforeTypeArguments` would require that the type annotations +/// for `p1` and `p2` contain `<...>`. This is to avoid ambiguous definitions: +/// +/// ```ignore +/// fn add(p1: Point, p2: Point) -> Point { +/// Point { +/// x: p1.x + p2.x, +/// y: p1.y + p2.y +/// } +/// } +/// ``` +#[derive(Clone, Copy)] +pub(crate) enum EnforceTypeArguments { + Yes, + No, +} diff --git a/sway-core/src/type_engine/mod.rs b/sway-core/src/type_engine/mod.rs index 5c04578f3a0..23f6fd58f9a 100644 --- a/sway-core/src/type_engine/mod.rs +++ b/sway-core/src/type_engine/mod.rs @@ -4,9 +4,12 @@ mod engine; mod integer_bits; mod replace_self_type; mod resolved_type; +mod trait_constraint; +mod type_argument; mod type_id; mod type_info; mod type_mapping; +mod type_parameter; mod unresolved_type_check; pub(crate) use copy_types::*; @@ -15,9 +18,12 @@ pub use engine::*; pub use integer_bits::*; pub(crate) use replace_self_type::*; pub(crate) use resolved_type::*; +pub(crate) use trait_constraint::*; +pub(crate) use type_argument::*; pub use type_id::*; pub use type_info::*; pub(crate) use type_mapping::*; +pub(crate) use type_parameter::*; pub(crate) use unresolved_type_check::*; use crate::error::*; diff --git a/sway-core/src/type_engine/trait_constraint.rs b/sway-core/src/type_engine/trait_constraint.rs new file mode 100644 index 00000000000..70bc2f985ad --- /dev/null +++ b/sway-core/src/type_engine/trait_constraint.rs @@ -0,0 +1,6 @@ +use crate::CallPath; + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub(crate) struct TraitConstraint { + pub(crate) call_path: CallPath, +} diff --git a/sway-core/src/parse_tree/declaration/type_argument.rs b/sway-core/src/type_engine/type_argument.rs similarity index 100% rename from sway-core/src/parse_tree/declaration/type_argument.rs rename to sway-core/src/type_engine/type_argument.rs diff --git a/sway-core/src/type_engine/type_info.rs b/sway-core/src/type_engine/type_info.rs index 229da791d79..814de264ff7 100644 --- a/sway-core/src/type_engine/type_info.rs +++ b/sway-core/src/type_engine/type_info.rs @@ -1,6 +1,6 @@ use super::*; -use crate::{semantic_analysis::*, types::*, CallPath, Ident, TypeArgument, TypeParameter}; +use crate::{semantic_analysis::*, types::*, CallPath, Ident}; use sway_types::{span::Span, Spanned}; @@ -863,6 +863,169 @@ impl TypeInfo { ok(generics, warnings, errors) } + /// Given two `TypeInfo`'s `self` and `other`, check to see if `self` is + /// unidirectionally a subset of `other`. + /// + /// `self` is a subset of `other` if it can be generalized over `other`. + /// For example, the generic `T` is a subset of the generic `F` because + /// anything of the type `T` could also be of the type `F` (minus any + /// external context that may make this statement untrue). + /// + /// Given: + /// + /// ```ignore + /// struct Data { + /// x: T, + /// y: F, + /// } + /// ``` + /// + /// the type `Data` is a subset of any generic type. + /// + /// Given: + /// + /// ```ignore + /// struct Data { + /// x: T, + /// y: F, + /// } + /// + /// impl Data { } + /// ``` + /// + /// the type `Data` is a subset of `Data`, but _`Data` is + /// not a subset of `Data`_. + /// + /// Given: + /// + /// ```ignore + /// struct Data { + /// x: T, + /// y: F, + /// } + /// + /// impl Data { } + /// + /// fn dummy() { + /// // the type of foo is Data + /// let foo = Data { + /// x: true, + /// y: 1u64 + /// }; + /// // the type of bar is Data + /// let bar = Data { + /// x: 0u8, + /// y: 0u8 + /// }; + /// } + /// ``` + /// + /// | type: | is subset of: | is not a subset of: | + /// |-------------------|----------------------------------------------|---------------------| + /// | `Data` | `Data`, any generic type | | + /// | `Data` | any generic type | `Data` | + /// | `Data` | `Data`, any generic type | `Data` | + /// | `Data` | `Data`, `Data`, any generic type | | + /// + pub(crate) fn is_subset_of(&self, other: &TypeInfo) -> bool { + match (self, other) { + // any type is the subset of a generic + (_, Self::UnknownGeneric { .. }) => true, + (Self::Ref(l, _), Self::Ref(r, _)) => { + look_up_type_id(*l).is_subset_of(&look_up_type_id(*r)) + } + (Self::Array(l0, l1), Self::Array(r0, r1)) => { + look_up_type_id(*l0).is_subset_of(&look_up_type_id(*r0)) && l1 == r1 + } + ( + Self::Custom { + name: l_name, + type_arguments: l_type_args, + }, + Self::Custom { + name: r_name, + type_arguments: r_type_args, + }, + ) => { + let l_types = l_type_args + .iter() + .map(|x| look_up_type_id(x.type_id)) + .collect::>(); + let r_types = r_type_args + .iter() + .map(|x| look_up_type_id(x.type_id)) + .collect::>(); + l_name == r_name && types_are_subset_of(&l_types, &r_types) + } + ( + Self::Enum { + name: l_name, + variant_types: l_variant_types, + type_parameters: l_type_parameters, + }, + Self::Enum { + name: r_name, + variant_types: r_variant_types, + type_parameters: r_type_parameters, + }, + ) => { + let l_names = l_variant_types + .iter() + .map(|x| x.name.clone()) + .collect::>(); + let r_names = r_variant_types + .iter() + .map(|x| x.name.clone()) + .collect::>(); + let l_types = l_type_parameters + .iter() + .map(|x| look_up_type_id(x.type_id)) + .collect::>(); + let r_types = r_type_parameters + .iter() + .map(|x| look_up_type_id(x.type_id)) + .collect::>(); + l_name == r_name && l_names == r_names && types_are_subset_of(&l_types, &r_types) + } + ( + Self::Struct { + name: l_name, + fields: l_fields, + type_parameters: l_type_parameters, + }, + Self::Struct { + name: r_name, + fields: r_fields, + type_parameters: r_type_parameters, + }, + ) => { + let l_names = l_fields.iter().map(|x| x.name.clone()).collect::>(); + let r_names = r_fields.iter().map(|x| x.name.clone()).collect::>(); + let l_types = l_type_parameters + .iter() + .map(|x| look_up_type_id(x.type_id)) + .collect::>(); + let r_types = r_type_parameters + .iter() + .map(|x| look_up_type_id(x.type_id)) + .collect::>(); + l_name == r_name && l_names == r_names && types_are_subset_of(&l_types, &r_types) + } + (Self::Tuple(l_types), Self::Tuple(r_types)) => { + let l_types = l_types + .iter() + .map(|x| look_up_type_id(x.type_id)) + .collect::>(); + let r_types = r_types + .iter() + .map(|x| look_up_type_id(x.type_id)) + .collect::>(); + types_are_subset_of(&l_types, &r_types) + } + (a, b) => a == b, + } + } + /// Given a `TypeInfo` `self` and a list of `Ident`'s `subfields`, /// iterate through the elements of `subfields` as `subfield`, /// and recursively apply `subfield` to `self`. @@ -982,6 +1145,113 @@ impl TypeInfo { } } +/// Given two lists of `TypeInfo`'s `left` and `right`, check to see if +/// `left` is a subset of `right`. +/// +/// `left` is a subset of `right` if the following invariants are true: +/// 1. `left` and and `right` are of the same length _n_ +/// 2. For every _i_ in [0, n), `left`áµ¢ is a subset of `right`áµ¢ +/// 3. The elements of `left` satisfy the trait constraints of `right` +/// +/// A property that falls of out these constraints are that if `left` and +/// `right` are empty, then `left` is a subset of `right`. +/// +/// Given: +/// +/// ```ignore +/// left: [T] +/// right: [T, F] +/// ``` +/// +/// `left` is not a subset of `right` because it violates invariant #1. +/// +/// Given: +/// +/// ```ignore +/// left: [T, F] +/// right: [bool, F] +/// ``` +/// +/// `left` is not a subset of `right` because it violates invariant #2. +/// +/// Given: +/// +/// ```ignore +/// left: [T, F] +/// right: [T, T] +/// ``` +/// +/// `left` is not a subset of `right` because it violates invariant #3. +/// +/// Given: +/// +/// ```ignore +/// left: [T, T] +/// right: [T, F] +/// ``` +/// +/// `left` is a subset of `right`. +/// +/// Given: +/// +/// ```ignore +/// left: [bool, T] +/// right: [T, F] +/// ``` +/// +/// `left` is a subset of `right`. +/// +/// Given: +/// +/// ```ignore +/// left: [Data, Data] +/// right: [Data, Data] +/// ``` +/// +/// `left` is a subset of `right`. +/// +fn types_are_subset_of(left: &[TypeInfo], right: &[TypeInfo]) -> bool { + // invariant 1. `left` and and `right` are of the same length _n_ + if left.len() != right.len() { + return false; + } + + // if `left` and `right` are empty, `left` is inherently a subset of `right` + if left.is_empty() && right.is_empty() { + return true; + } + + // invariant 2. For every _i_ in [0, n), `left`áµ¢ is a subset of `right`áµ¢ + for (l, r) in left.iter().zip(right.iter()) { + if !l.is_subset_of(r) { + return false; + } + } + + // invariant 3. The elements of `left` satisfy the trait constraints of `right` + let mut constraints = vec![]; + for i in 0..(right.len() - 1) { + for j in (i + 1)..right.len() { + let a = right.get(i).unwrap(); + let b = right.get(j).unwrap(); + if a == b { + // if a and b are the same type + constraints.push((i, j)); + } + } + } + for (i, j) in constraints.into_iter() { + let a = left.get(i).unwrap(); + let b = left.get(j).unwrap(); + if a != b { + return false; + } + } + + // if all of the invariants are met, then `self` is a subset of `other`! + true +} + fn print_inner_types(name: String, inner_types: impl Iterator) -> String { let inner_types = inner_types.map(|x| x.to_string()).collect::>(); format!( diff --git a/sway-core/src/type_engine/type_mapping.rs b/sway-core/src/type_engine/type_mapping.rs index 8335676addc..bb83531fcc6 100644 --- a/sway-core/src/type_engine/type_mapping.rs +++ b/sway-core/src/type_engine/type_mapping.rs @@ -1,5 +1,3 @@ -use crate::TypeParameter; - use super::*; pub(crate) type TypeMapping = Vec<(TypeId, TypeId)>; @@ -17,3 +15,111 @@ pub(crate) fn insert_type_parameters(type_parameters: &[TypeParameter]) -> TypeM }) .collect() } + +pub(crate) fn create_type_mapping(superset_type: TypeId, subset_type: TypeId) -> TypeMapping { + match (look_up_type_id(superset_type), look_up_type_id(subset_type)) { + (TypeInfo::Ref(superset_type, _), TypeInfo::Ref(subset_type, _)) => { + create_type_mapping(superset_type, subset_type) + } + (TypeInfo::Ref(superset_type, _), _) => create_type_mapping(superset_type, subset_type), + (_, TypeInfo::Ref(subset_type, _)) => create_type_mapping(superset_type, subset_type), + (TypeInfo::UnknownGeneric { .. }, _) => { + vec![(superset_type, subset_type)] + } + ( + TypeInfo::Custom { + type_arguments: type_parameters, + .. + }, + TypeInfo::Custom { type_arguments, .. }, + ) => { + let type_parameters = type_parameters + .iter() + .map(|x| x.type_id) + .collect::>(); + let type_arguments = type_arguments.iter().map(|x| x.type_id).collect::>(); + insert_type_parameters_with_type_arguments(type_parameters, type_arguments) + } + ( + TypeInfo::Enum { + type_parameters, .. + }, + TypeInfo::Enum { + type_parameters: type_arguments, + .. + }, + ) => { + let type_parameters = type_parameters + .iter() + .map(|x| x.type_id) + .collect::>(); + let type_arguments = type_arguments.iter().map(|x| x.type_id).collect::>(); + insert_type_parameters_with_type_arguments(type_parameters, type_arguments) + } + ( + TypeInfo::Struct { + type_parameters, .. + }, + TypeInfo::Struct { + type_parameters: type_arguments, + .. + }, + ) => { + let type_parameters = type_parameters + .iter() + .map(|x| x.type_id) + .collect::>(); + let type_arguments = type_arguments.iter().map(|x| x.type_id).collect::>(); + insert_type_parameters_with_type_arguments(type_parameters, type_arguments) + } + (TypeInfo::Tuple(type_parameters), TypeInfo::Tuple(type_arguments)) => { + insert_type_parameters_with_type_arguments( + type_parameters + .iter() + .map(|x| x.type_id) + .collect::>(), + type_arguments.iter().map(|x| x.type_id).collect::>(), + ) + } + (TypeInfo::Array(superset_type, _), TypeInfo::Array(subset_type, _)) => { + vec![(superset_type, subset_type)] + } + ( + TypeInfo::Storage { + fields: type_parameters, + }, + TypeInfo::Storage { + fields: type_arguments, + }, + ) => { + let type_parameters = type_parameters + .iter() + .map(|x| x.type_id) + .collect::>(); + let type_arguments = type_arguments.iter().map(|x| x.type_id).collect::>(); + insert_type_parameters_with_type_arguments(type_parameters, type_arguments) + } + (TypeInfo::Unknown, TypeInfo::Unknown) + | (TypeInfo::Boolean, TypeInfo::Boolean) + | (TypeInfo::SelfType, TypeInfo::SelfType) + | (TypeInfo::Byte, TypeInfo::Byte) + | (TypeInfo::B256, TypeInfo::B256) + | (TypeInfo::Numeric, TypeInfo::Numeric) + | (TypeInfo::Contract, TypeInfo::Contract) + | (TypeInfo::ErrorRecovery, TypeInfo::ErrorRecovery) + | (TypeInfo::Str(_), TypeInfo::Str(_)) + | (TypeInfo::UnsignedInteger(_), TypeInfo::UnsignedInteger(_)) + | (TypeInfo::ContractCaller { .. }, TypeInfo::ContractCaller { .. }) => vec![], + _ => vec![], + } +} + +fn insert_type_parameters_with_type_arguments( + type_parameters: Vec, + type_arguments: Vec, +) -> TypeMapping { + type_parameters + .into_iter() + .zip(type_arguments.into_iter()) + .collect::>() +} diff --git a/sway-core/src/parse_tree/declaration/type_parameter.rs b/sway-core/src/type_engine/type_parameter.rs similarity index 88% rename from sway-core/src/parse_tree/declaration/type_parameter.rs rename to sway-core/src/type_engine/type_parameter.rs index bdacbd14548..f4899a796b7 100644 --- a/sway-core/src/parse_tree/declaration/type_parameter.rs +++ b/sway-core/src/type_engine/type_parameter.rs @@ -1,8 +1,11 @@ -use crate::{error::*, parse_tree::*, semantic_analysis::*, type_engine::*}; +use crate::{error::*, semantic_analysis::*, type_engine::*}; use sway_types::{ident::Ident, span::Span, Spanned}; -use std::hash::{Hash, Hasher}; +use std::{ + fmt, + hash::{Hash, Hasher}, +}; #[derive(Debug, Clone, Eq)] pub struct TypeParameter { @@ -57,11 +60,17 @@ impl ReplaceSelfType for TypeParameter { } } +impl fmt::Display for TypeParameter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}: {}", self.name_ident, self.type_id) + } +} + impl TypeParameter { pub(crate) fn type_check( + ctx: TypeCheckContext, type_parameter: TypeParameter, - namespace: &mut Namespace, - ) -> CompileResult { + ) -> CompileResult { let mut warnings = vec![]; let mut errors = vec![]; if !type_parameter.trait_constraints.is_empty() { @@ -78,7 +87,7 @@ impl TypeParameter { name: type_parameter.name_ident.clone(), type_id, }; - namespace + ctx.namespace .insert_symbol(type_parameter.name_ident.clone(), type_parameter_decl) .ok(&mut warnings, &mut errors); let type_parameter = TypeParameter { @@ -89,8 +98,3 @@ impl TypeParameter { ok(type_parameter, warnings, errors) } } - -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub(crate) struct TraitConstraint { - pub(crate) call_path: CallPath, -} diff --git a/sway-core/tests/sway_to_ir/eq_intrinsic.ir b/sway-core/tests/sway_to_ir/eq_intrinsic.ir new file mode 100644 index 00000000000..119bcf76de2 --- /dev/null +++ b/sway-core/tests/sway_to_ir/eq_intrinsic.ir @@ -0,0 +1,21 @@ +script { + fn main() -> bool, !1 { + local ptr bool _ + + entry: + v0 = const u64 1, !2 + v1 = const u64 2, !3 + v2 = cmp eq v0 v1 + v3 = get_ptr ptr bool _, ptr bool, 0, !4 + store v2, ptr v3, !4 + v4 = const bool true, !5 + ret bool v4 + } +} + +!0 = filepath "/path/to/eq_intrinsic.sw" +!1 = span !0 9 59 +!2 = span !0 44 45 +!3 = span !0 47 48 +!4 = span !0 31 50 +!5 = span !0 53 57 diff --git a/sway-core/tests/sway_to_ir/eq_intrinsic.sw b/sway-core/tests/sway_to_ir/eq_intrinsic.sw new file mode 100644 index 00000000000..d32660611f4 --- /dev/null +++ b/sway-core/tests/sway_to_ir/eq_intrinsic.sw @@ -0,0 +1,6 @@ +script; + +fn main() -> bool { + let _ = __eq(1, 2); + true +} diff --git a/sway-core/tests/sway_to_ir/local_const_init.ir b/sway-core/tests/sway_to_ir/local_const_init.ir new file mode 100644 index 00000000000..c610e0b6608 --- /dev/null +++ b/sway-core/tests/sway_to_ir/local_const_init.ir @@ -0,0 +1,20 @@ +script { + fn main() -> u64, !1 { + local ptr { u64 } X + + entry: + v0 = get_ptr ptr { u64 } X, ptr { u64 }, 0, !2 + v1 = const { u64 } { u64 1 }, !3 + store v1, ptr v0, !2 + v2 = get_ptr ptr { u64 } X, ptr { u64 }, 0, !4 + v3 = extract_value v2, { u64 }, 0, !5 + ret u64 v3 + } +} + +!0 = filepath "/path/to/local_const_init.sw" +!1 = span !0 70 114 +!2 = span !0 91 106 +!3 = span !0 33 68 +!4 = span !0 109 110 +!5 = span !0 22 29 diff --git a/sway-core/tests/sway_to_ir/local_const_init.sw b/sway-core/tests/sway_to_ir/local_const_init.sw new file mode 100644 index 00000000000..cb05b34bea7 --- /dev/null +++ b/sway-core/tests/sway_to_ir/local_const_init.sw @@ -0,0 +1,14 @@ +script; + +struct S { + s : u64 +} + +fn s(x : u64) -> S { + S { s: x } +} + +fn main() -> u64 { + const X = s(1); + X.s +} diff --git a/sway-fmt-v2/Cargo.toml b/sway-fmt-v2/Cargo.toml index eb8accb7da7..7c82823560a 100644 --- a/sway-fmt-v2/Cargo.toml +++ b/sway-fmt-v2/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sway-fmt-v2" -version = "0.16.1" +version = "0.16.2" authors = ["Fuel Labs "] edition = "2021" homepage = "https://fuel.network/" @@ -11,11 +11,11 @@ description = "Sway sway-fmt-v2." [dependencies] anyhow = "1" -forc-util = { version = "0.16.1", path = "../forc-util" } +forc-util = { version = "0.16.2", path = "../forc-util" } serde = { version = "1.0", features = ["derive"] } serde_ignored = "0.1" -sway-core = { version = "0.16.1", path = "../sway-core" } -sway-parse = { version = "0.16.1", path = "../sway-parse" } -sway-types = { version = "0.16.1", path = "../sway-types" } +sway-core = { version = "0.16.2", path = "../sway-core" } +sway-parse = { version = "0.16.2", path = "../sway-parse" } +sway-types = { version = "0.16.2", path = "../sway-types" } thiserror = "1.0.30" toml = "0.5" diff --git a/sway-fmt-v2/src/config/heuristics.rs b/sway-fmt-v2/src/config/heuristics.rs index f145504c987..3760424481b 100644 --- a/sway-fmt-v2/src/config/heuristics.rs +++ b/sway-fmt-v2/src/config/heuristics.rs @@ -69,12 +69,12 @@ pub struct WidthHeuristics { // Maximum width of the args of a function-like attributes before falling // back to vertical formatting. pub(crate) attr_fn_like_width: usize, - // Maximum width in the body of a struct lit before falling back to + // Maximum width in the body of a user-defined structure literal before falling back to // vertical formatting. - pub(crate) struct_lit_width: usize, - // Maximum width in the body of a struct variant before falling back + pub(crate) structure_lit_width: usize, + // Maximum width in the body of a user-defined structure field before falling back // to vertical formatting. - pub(crate) struct_variant_width: usize, + pub(crate) structure_field_width: usize, // Maximum width of an array literal before falling back to vertical // formatting. pub(crate) array_width: usize, @@ -97,8 +97,8 @@ impl WidthHeuristics { WidthHeuristics { fn_call_width: usize::max_value(), attr_fn_like_width: usize::max_value(), - struct_lit_width: 0, - struct_variant_width: 0, + structure_lit_width: 0, + structure_field_width: 0, array_width: usize::max_value(), chain_width: usize::max_value(), single_line_if_else_max_width: 0, @@ -109,8 +109,8 @@ impl WidthHeuristics { WidthHeuristics { fn_call_width: max_width, attr_fn_like_width: max_width, - struct_lit_width: max_width, - struct_variant_width: max_width, + structure_lit_width: max_width, + structure_field_width: max_width, array_width: max_width, chain_width: max_width, single_line_if_else_max_width: max_width, @@ -131,8 +131,9 @@ impl WidthHeuristics { fn_call_width: (DEFAULT_FN_CALL_WIDTH as f32 * max_width_ratio).round() as usize, attr_fn_like_width: (DEFAULT_ATTR_FN_LIKE_WIDTH as f32 * max_width_ratio).round() as usize, - struct_lit_width: (DEFAULT_STRUCT_LIT_WIDTH as f32 * max_width_ratio).round() as usize, - struct_variant_width: (DEFAULT_STRUCT_VAR_WIDTH as f32 * max_width_ratio).round() + structure_lit_width: (DEFAULT_STRUCT_LIT_WIDTH as f32 * max_width_ratio).round() + as usize, + structure_field_width: (DEFAULT_STRUCT_VAR_WIDTH as f32 * max_width_ratio).round() as usize, array_width: (DEFAULT_ARRAY_WIDTH as f32 * max_width_ratio).round() as usize, chain_width: (DEFAULT_CHAIN_WIDTH as f32 * max_width_ratio).round() as usize, diff --git a/sway-fmt-v2/src/config/user_def.rs b/sway-fmt-v2/src/config/user_def.rs index b107ca9e3ba..66e698b2120 100644 --- a/sway-fmt-v2/src/config/user_def.rs +++ b/sway-fmt-v2/src/config/user_def.rs @@ -1,28 +1,23 @@ //! Configuration options related to formatting user-defined structures. -use crate::constants::{ - DEFAULT_ENUM_VARIANT_ALIGN_THRESHOLD, DEFAULT_STRUCT_FIELD_ALIGN_THRESHOLD, -}; +use serde::{Deserialize, Serialize}; use super::user_opts::StructuresOptions; /// Styling preferences for user-defined structures like `struct`s or `enum`s. #[derive(Debug)] pub struct Structures { - /// Align enum variants discrims, if their diffs fit within threshold. - pub enum_variant_align_threshold: usize, - /// Align struct fields if their diffs fits within threshold. - pub struct_field_align_threshold: usize, - /// Put small struct literals on a single line. - pub struct_lit_single_line: bool, + /// Align fields of user-defined structures if their diffs fit within threshold. + pub field_alignment: FieldAlignment, + /// Put small user-defined structure literals on a single line. + pub small_structures_single_line: bool, } impl Default for Structures { fn default() -> Self { Self { - enum_variant_align_threshold: DEFAULT_ENUM_VARIANT_ALIGN_THRESHOLD, - struct_field_align_threshold: DEFAULT_STRUCT_FIELD_ALIGN_THRESHOLD, - struct_lit_single_line: true, + field_alignment: FieldAlignment::Off, + small_structures_single_line: true, } } } @@ -31,15 +26,17 @@ impl Structures { pub fn from_opts(opts: &StructuresOptions) -> Self { let default = Self::default(); Self { - enum_variant_align_threshold: opts - .enum_variant_align_threshold - .unwrap_or(default.enum_variant_align_threshold), - struct_field_align_threshold: opts - .struct_field_align_threshold - .unwrap_or(default.struct_field_align_threshold), - struct_lit_single_line: opts + field_alignment: opts.field_alignment.unwrap_or(default.field_alignment), + small_structures_single_line: opts .struct_lit_single_line - .unwrap_or(default.struct_lit_single_line), + .unwrap_or(default.small_structures_single_line), } } } + +/// Align fields if they fit within a provided threshold. +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +pub enum FieldAlignment { + AlignFields(usize), + Off, +} diff --git a/sway-fmt-v2/src/config/user_opts.rs b/sway-fmt-v2/src/config/user_opts.rs index bbb9ffb9141..c0c03bdb221 100644 --- a/sway-fmt-v2/src/config/user_opts.rs +++ b/sway-fmt-v2/src/config/user_opts.rs @@ -8,6 +8,7 @@ use super::{ items::{ItemBraceStyle, ItemsLayout}, lists::{ListTactic, SeparatorTactic}, literals::HexLiteralCase, + user_def::FieldAlignment, whitespace::{IndentStyle, NewlineStyle}, }; /// See parent struct [Whitespace]. @@ -77,8 +78,7 @@ pub struct HeuristicsOptions { /// See parent struct [Structures]. #[derive(Serialize, Deserialize, Debug, Copy, Clone)] pub struct StructuresOptions { - pub enum_variant_align_threshold: Option, - pub struct_field_align_threshold: Option, + pub field_alignment: Option, pub struct_lit_single_line: Option, } /// See parent struct [Comments]. diff --git a/sway-fmt-v2/src/constants.rs b/sway-fmt-v2/src/constants.rs index c4c7d76e1bc..f95b30dec73 100644 --- a/sway-fmt-v2/src/constants.rs +++ b/sway-fmt-v2/src/constants.rs @@ -36,13 +36,6 @@ pub const DEFAULT_BLANK_LINES_LOWER_BOUND: usize = 0; /// Write an items and its attribute on the same line if their combined width is below a threshold. pub const DEFAULT_INLINE_ATTR_WIDTH: usize = 0; -/////USER_DEFINED_STRUCTURES///// - -/// Default max threshold for aligning struct fields. -pub const DEFAULT_STRUCT_FIELD_ALIGN_THRESHOLD: usize = 0; -/// Default max threshold for aligning enum variants. -pub const DEFAULT_ENUM_VARIANT_ALIGN_THRESHOLD: usize = 0; - /////COMMENTS///// /// Default max length of comments. diff --git a/sway-fmt-v2/src/error.rs b/sway-fmt-v2/src/error.rs index fda03500a60..d1b021c871b 100644 --- a/sway-fmt-v2/src/error.rs +++ b/sway-fmt-v2/src/error.rs @@ -5,6 +5,8 @@ use thiserror::Error; pub enum FormatterError { #[error("Error parsing file: {0}")] ParseFileError(#[from] sway_parse::ParseFileError), + #[error("Error formatting a message into a stream: {0}")] + FormatError(#[from] std::fmt::Error), } #[derive(Debug, Error)] diff --git a/sway-fmt-v2/src/fmt.rs b/sway-fmt-v2/src/fmt.rs index 1282c9a0e07..9636cf46dc5 100644 --- a/sway-fmt-v2/src/fmt.rs +++ b/sway-fmt-v2/src/fmt.rs @@ -1,9 +1,8 @@ use crate::utils::{ - attributes::format_attributes, indent_style::Shape, newline_style::apply_newline_style, + indent_style::Shape, newline_style::apply_newline_style, program_type::insert_program_type, }; use std::{path::Path, sync::Arc}; use sway_core::BuildConfig; -use sway_parse::ItemKind; pub use crate::{ config::manifest::Config, @@ -19,7 +18,11 @@ pub struct Formatter { pub type FormattedCode = String; pub trait Format { - fn format(&self, formatter: &mut Formatter) -> FormattedCode; + fn format( + &self, + formatted_code: &mut FormattedCode, + formatter: &mut Formatter, + ) -> Result<(), FormatterError>; } impl Formatter { @@ -38,34 +41,38 @@ impl Formatter { build_config: Option<&BuildConfig>, ) -> Result { let path = build_config.map(|build_config| build_config.canonical_root_module()); - let items = sway_parse::parse_file(src, path)?.items; - let formatted_raw_newline = items - .into_iter() - .map(|item| -> Result { - use ItemKind::*; - // format attributes first, then add corresponding item - let mut buf = format_attributes(item.attribute_list, self); - buf.push_str(&match item.value { - Use(item_use) => item_use.format(self), - Struct(item_struct) => item_struct.format(self), - Enum(item_enum) => item_enum.format(self), - Fn(item_fn) => item_fn.format(self), - Trait(item_trait) => item_trait.format(self), - Impl(item_impl) => item_impl.format(self), - Abi(item_abi) => item_abi.format(self), - Const(item_const) => item_const.format(self), - Storage(item_storage) => item_storage.format(self), - }); - Ok(buf) - }) - .collect::, _>>()? - .join("\n"); - let mut formatted_code = String::from(&formatted_raw_newline); + let src_len = src.len(); + let module = sway_parse::parse_file(src, path)?; + // Get parsed items + let items = module.items; + // Get the program type (script, predicate, contract or library) + let program_type = module.kind; + + // Formatted code will be pushed here with raw newline stlye. + // Which means newlines are not converted into system-specific versions by `apply_newline_style`. + // Use the length of src as a hint of the memory size needed for `raw_formatted_code`, + // which will reduce the number of reallocations + let mut raw_formatted_code = String::with_capacity(src_len); + + // Insert program type to the formatted code. + insert_program_type(&mut raw_formatted_code, program_type); + + // Insert parsed & formatted items into the formatted code. + let mut iter = items.iter().peekable(); + while let Some(item) = iter.next() { + // format Annotated + item.format(&mut raw_formatted_code, self)?; + if iter.peek().is_some() { + raw_formatted_code.push('\n'); + } + } + + let mut formatted_code = String::from(&raw_formatted_code); apply_newline_style( // The user's setting for `NewlineStyle` self.config.whitespace.newline_style, &mut formatted_code, - &formatted_raw_newline, + &raw_formatted_code, ); Ok(formatted_code) } @@ -73,15 +80,106 @@ impl Formatter { #[cfg(test)] mod tests { - use crate::utils::indent_style::Shape; - use std::sync::Arc; - use super::{Config, Formatter}; + use crate::{config::user_def::FieldAlignment, utils::indent_style::Shape}; + use std::sync::Arc; fn get_formatter(config: Config, shape: Shape) -> Formatter { Formatter { config, shape } } + #[test] + fn test_const() { + let sway_code_to_format = r#"contract; +pub const TEST:u16=10;"#; + let correct_sway_code = r#"contract; + +pub const TEST: u16 = 10;"#; + let mut formatter = Formatter::default(); + let formatted_sway_code = + Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap(); + assert!(correct_sway_code == formatted_sway_code) + } + + #[test] + fn test_struct_multiline_line_alignment() { + let sway_code_to_format = r#"contract; +pub struct Foo { + barbazfoo: u64, + baz : bool, +} +"#; + let correct_sway_code = r#"contract; + +pub struct Foo { + barbazfoo : u64, + baz : bool, +}"#; + let mut config = Config::default(); + config.structures.field_alignment = FieldAlignment::AlignFields(40); + let mut formatter = get_formatter(config, Shape::default()); + let formatted_sway_code = + Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap(); + assert!(correct_sway_code == formatted_sway_code) + } + #[test] + fn test_struct_single_line() { + let sway_code_to_format = r#"contract; +pub struct Foo { + bar: u64, + baz: bool, +} +"#; + let correct_sway_code = r#"contract; + +pub struct Foo { bar: u64, baz: bool }"#; + let mut config = Config::default(); + config.structures.small_structures_single_line = true; + config.whitespace.max_width = 300; + let mut formatter = get_formatter(config, Shape::default()); + let formatted_sway_code = + Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap(); + assert!(correct_sway_code == formatted_sway_code) + } + #[test] + fn test_enum_single_line() { + let sway_code_to_format = r#"contract; +pub enum Foo { + bar: u64, + baz: bool, +} +"#; + let correct_sway_code = r#"contract; + +pub enum Foo { bar: u64, baz: bool }"#; + let mut config = Config::default(); + config.structures.small_structures_single_line = true; + config.whitespace.max_width = 300; + let mut formatter = get_formatter(config, Shape::default()); + let formatted_sway_code = + Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap(); + assert!(correct_sway_code == formatted_sway_code) + } + #[test] + fn test_struct_multi_line() { + let sway_code_to_format = r#"contract; +pub struct Foo { + bar: u64, + baz: bool +} +"#; + let correct_sway_code = r#"contract; + +pub struct Foo { + bar: u64, + baz: bool, +}"#; + let mut formatter = Formatter::default(); + let formatted_sway_code = + Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap(); + assert!(correct_sway_code == formatted_sway_code) + } + #[test] fn test_enum_without_variant_alignment() { let sway_code_to_format = r#"contract; @@ -90,18 +188,18 @@ enum Color { Blue: (), Green: (), Red: (), Silver: (), - Grey: (), } + Grey: () } "#; + let correct_sway_code = r#"contract; - // Until #1995 is addressed we will not have contract; in the output - let correct_sway_code = r#"enum Color { - Blue : (), - Green : (), - Red : (), - Silver : (), - Grey : (), +enum Color { + Blue: (), + Green: (), + Red: (), + Silver: (), + Grey: (), }"#; - let mut formatter = get_formatter(Config::default(), Shape::default()); + let mut formatter = Formatter::default(); let formatted_sway_code = Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap(); assert!(correct_sway_code == formatted_sway_code) @@ -116,21 +214,59 @@ enum Color { Silver: (), Grey: (), } "#; + let correct_sway_code = r#"contract; - // Until #1995 is addressed we will not have contract; in the output - let correct_sway_code = r#"enum Color { - Blue : (), - Green : (), - Red : (), - Silver : (), - Grey : (), +enum Color { + Blue : (), + Green : (), + Red : (), + Silver : (), + Grey : (), }"#; // Creating a config with enum_variant_align_threshold that exceeds longest variant length - let mut config = Config::default(); - config.structures.enum_variant_align_threshold = 20; + let mut formatter = Formatter::default(); + formatter.config.structures.field_alignment = FieldAlignment::AlignFields(20); - let mut formatter = get_formatter(config, Shape::default()); + let formatted_sway_code = + Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap(); + assert!(correct_sway_code == formatted_sway_code) + } + #[test] + fn test_item_abi_with_generics_and_attributes() { + let sway_code_to_format = r#"contract; + +abi StorageMapExample { + #[storage(write)]fn insert_into_map1(key: u64, value: u64); + +fn hello(key: u64, value: u64); +}"#; + let correct_sway_code = r#"contract; + +abi StorageMapExample { + #[storage(write)] + fn insert_into_map1(key: u64, value: u64); + + fn hello(key: u64, value: u64); +}"#; + let mut formatter = Formatter::default(); + let formatted_sway_code = + Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap(); + assert!(correct_sway_code == formatted_sway_code) + } + + #[test] + fn test_multi_items() { + let sway_code_to_format = r#"contract; + +pub const TEST: u16 = 10; +pub const TEST1: u16 = 10;"#; + let correct_sway_code = r#"contract; + +pub const TEST: u16 = 10; +pub const TEST1: u16 = 10;"#; + + let mut formatter = Formatter::default(); let formatted_sway_code = Formatter::format(&mut formatter, Arc::from(sway_code_to_format), None).unwrap(); assert!(correct_sway_code == formatted_sway_code) diff --git a/sway-fmt-v2/src/items/item_abi.rs b/sway-fmt-v2/src/items/item_abi.rs index 9699f370074..c634a1591bd 100644 --- a/sway-fmt-v2/src/items/item_abi.rs +++ b/sway-fmt-v2/src/items/item_abi.rs @@ -1,8 +1,114 @@ -use crate::fmt::{Format, FormattedCode, Formatter}; -use sway_parse::ItemAbi; +use crate::{ + config::items::ItemBraceStyle, + fmt::{Format, FormattedCode, Formatter}, + utils::{attribute::FormatDecl, bracket::CurlyBrace}, + FormatterError, +}; +use std::fmt::Write; +use sway_parse::{token::Delimiter, ItemAbi}; +use sway_types::Spanned; impl Format for ItemAbi { - fn format(&self, _formatter: &mut Formatter) -> FormattedCode { - todo!() + fn format( + &self, + formatted_code: &mut FormattedCode, + formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + // Add enum token + write!(formatted_code, "{} ", self.abi_token.span().as_str())?; + + // Add name of the abi + formatted_code.push_str(self.name.as_str()); + Self::open_curly_brace(formatted_code, formatter)?; + + // Add item fields + // abi_items + let mut abi_items_iter = self.abi_items.get().iter().peekable(); + while let Some(item) = abi_items_iter.next() { + let attribute_list = item.0.attribute_list.clone(); + // add indent + format attribute if it exists + if !attribute_list.is_empty() { + formatted_code.push_str(&formatter.shape.indent.to_string(formatter)); + for attr in attribute_list { + attr.format(formatted_code, formatter)?; + } + } + // add indent + format item + formatted_code.push_str(&formatter.shape.indent.to_string(formatter)); + writeln!( + formatted_code, + "{}{}", + item.0.value.span().as_str(), // FnSignature formatting (todo!) + item.1.span().as_str() // SemicolonToken + )?; + + if abi_items_iter.peek().is_some() { + formatted_code.push('\n'); + } + } + + // abi_defs_opt + if let Some(abi_defs) = self.abi_defs_opt.clone() { + let mut iter = abi_defs.get().iter().peekable(); + while let Some(item) = iter.next() { + let attribute_list = item.attribute_list.clone(); + // add indent + format attribute if it exists + if !attribute_list.is_empty() { + formatted_code.push_str(&formatter.shape.indent.to_string(formatter)); + for attr in attribute_list { + attr.format(formatted_code, formatter)?; + } + } + + // add indent + format item + formatted_code.push_str(&formatter.shape.indent.to_string(formatter)); + item.value.format(formatted_code, formatter)?; // ItemFn formatting (todo!) + if iter.peek().is_some() { + formatted_code.push('\n'); + } + } + } + Self::close_curly_brace(formatted_code, formatter)?; + + Ok(()) + } +} + +impl CurlyBrace for ItemAbi { + fn open_curly_brace( + line: &mut String, + formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + let brace_style = formatter.config.items.item_brace_style; + let extra_width = formatter.config.whitespace.tab_spaces; + let mut shape = formatter.shape; + let open_brace = Delimiter::Brace.as_open_char(); + match brace_style { + ItemBraceStyle::AlwaysNextLine => { + // Add openning brace to the next line. + write!(line, "\n{}\n", open_brace)?; + shape = shape.block_indent(extra_width); + } + _ => { + // Add opening brace to the same line + writeln!(line, " {}", open_brace)?; + shape = shape.block_indent(extra_width); + } + } + + formatter.shape = shape; + Ok(()) + } + fn close_curly_brace( + line: &mut String, + formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + line.push(Delimiter::Brace.as_close_char()); + // If shape is becoming left-most alligned or - indent just have the defualt shape + formatter.shape = formatter + .shape + .shrink_left(formatter.config.whitespace.tab_spaces) + .unwrap_or_default(); + Ok(()) } } diff --git a/sway-fmt-v2/src/items/item_const.rs b/sway-fmt-v2/src/items/item_const.rs index c4a2810c489..315c85a61b5 100644 --- a/sway-fmt-v2/src/items/item_const.rs +++ b/sway-fmt-v2/src/items/item_const.rs @@ -1,8 +1,47 @@ -use crate::fmt::{Format, FormattedCode, Formatter}; +use crate::{ + fmt::{Format, FormattedCode, Formatter}, + FormatterError, +}; use sway_parse::ItemConst; +use sway_types::Spanned; impl Format for ItemConst { - fn format(&self, _formatter: &mut Formatter) -> FormattedCode { - todo!() + fn format( + &self, + formatted_code: &mut FormattedCode, + _formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + // Check if visibility token exists if so add it. + if let Some(visibility_token) = &self.visibility { + formatted_code.push_str(visibility_token.span().as_str()); + formatted_code.push(' '); + } + + // Add the const token + formatted_code.push_str(self.const_token.span().as_str()); + formatted_code.push(' '); + + // Add name of the const + formatted_code.push_str(self.name.as_str()); + + // Check if ty exists + if let Some(ty) = &self.ty_opt { + // Add colon + formatted_code.push_str(ty.0.span().as_str()); + // TODO: We are not applying any custom formatting to ty probably we will need to in the future. + // Add ty + formatted_code.push(' '); + formatted_code.push_str(ty.1.span().as_str()); + } + + formatted_code.push(' '); + // Add equal token + formatted_code.push_str(self.eq_token.ident().as_str()); + formatted_code.push(' '); + + // TODO: We are not applying any custom formatting to expr, probably we will need to in the future. + formatted_code.push_str(self.expr.span().as_str()); + formatted_code.push_str(self.semicolon_token.ident().as_str()); + Ok(()) } } diff --git a/sway-fmt-v2/src/items/item_enum.rs b/sway-fmt-v2/src/items/item_enum.rs index 1d431bac8fa..067ff58331e 100644 --- a/sway-fmt-v2/src/items/item_enum.rs +++ b/sway-fmt-v2/src/items/item_enum.rs @@ -1,104 +1,228 @@ use crate::{ - config::items::ItemBraceStyle, + config::{items::ItemBraceStyle, user_def::FieldAlignment}, fmt::{Format, FormattedCode, Formatter}, - utils::bracket::CurlyBrace, + utils::{bracket::CurlyBrace, item_len::ItemLen}, + FormatterError, +}; +use std::fmt::Write; +use sway_parse::{ + token::{Delimiter, PunctKind}, + ItemEnum, }; -use sway_parse::ItemEnum; use sway_types::Spanned; impl Format for ItemEnum { - fn format(&self, formatter: &mut Formatter) -> FormattedCode { - // TODO: creating this formatted_code with String::new() will likely cause lots of - // reallocations maybe we can explore how we can do this, starting with with_capacity may help. - let mut formatted_code = String::new(); - let enum_variant_align_threshold = formatter.config.structures.enum_variant_align_threshold; - - if let Some(visibility_token) = &self.visibility { - formatted_code.push_str(visibility_token.span().as_str()); - formatted_code.push(' '); - } + fn format( + &self, + formatted_code: &mut FormattedCode, + formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + // Bring configurations into scope. + // + // Should small enums formatted into a single line. + let enum_lit_single_line = formatter.config.structures.small_structures_single_line; - // Add enum token - formatted_code.push_str(self.enum_token.span().as_str()); - formatted_code.push(' '); + // Get the width limit of an enum to be formatted into single line if `enum_lit_single_line` is true. + let width_heuristics = formatter + .config + .heuristics + .heuristics_pref + .to_width_heuristics(&formatter.config.whitespace); + let enum_lit_width = width_heuristics.structure_lit_width; + + let multiline = !enum_lit_single_line || self.get_formatted_len() > enum_lit_width; + + format_enum(self, formatted_code, formatter, multiline)?; + Ok(()) + } +} + +/// Format the enum if the multiline is passed as false enum will be formatted into a single line. +/// +/// ##examples +/// +/// (multiline : false): +/// +/// ```rust,ignore +/// enum Foo { bar: u64, baz: bool } +/// ``` +/// +/// (multiline : true): +/// ```rust,ignore +/// enum Foo { +/// bar: u64, +/// baz: bool, +/// } +/// ``` +fn format_enum( + item_enum: &ItemEnum, + formatted_code: &mut FormattedCode, + formatter: &mut Formatter, + multiline: bool, +) -> Result<(), FormatterError> { + // If there is a visibility token add it to the formatted_code with a ` ` after it. + if let Some(visibility) = &item_enum.visibility { + write!(formatted_code, "{} ", visibility.span().as_str())?; + } + // Add enum token + write!(formatted_code, "{} ", item_enum.enum_token.span().as_str())?; + + // Add enum name + formatted_code.push_str(item_enum.name.as_str()); + + // Format `GenericParams`, if any + if let Some(generics) = &item_enum.generics { + generics.format(formatted_code, formatter)?; + } - // Add name of the enum. - formatted_code.push_str(self.name.as_str()); - ItemEnum::open_curly_brace(&mut formatted_code, formatter); + let variants = item_enum.fields.clone().into_inner(); - let type_fields = &self.fields.clone().into_inner().value_separator_pairs; + // Handle openning brace + ItemEnum::open_curly_brace(formatted_code, formatter)?; + if multiline { + formatted_code.push('\n'); + // Determine alignment tactic + match formatter.config.structures.field_alignment { + FieldAlignment::AlignFields(enum_variant_align_threshold) => { + let value_pairs = variants.value_separator_pairs; + // In first iteration we are going to be collecting the lengths of the enum variants. + let variant_length: Vec = value_pairs + .iter() + .map(|variant| variant.0.name.as_str().len()) + .collect(); - // In first iteration we are going to be collecting the lengths of the enum variants. - let variant_length: Vec = type_fields - .iter() - .map(|variant| variant.0.name.as_str().len()) - .collect(); + // Find the maximum length in the variant_length vector that is still smaller than enum_field_align_threshold. + let mut max_valid_variant_length = 0; + variant_length.iter().for_each(|length| { + if *length > max_valid_variant_length && *length < enum_variant_align_threshold + { + max_valid_variant_length = *length; + } + }); - // Find the maximum length in the variant_length vector that is still smaller than enum_variant_align_threshold. - let mut max_valid_variant_length = 0; + let mut value_pairs_iter = value_pairs.iter().enumerate().peekable(); + for (var_index, variant) in value_pairs_iter.clone() { + formatted_code.push_str(&formatter.shape.indent.to_string(formatter)); - variant_length.iter().for_each(|length| { - if *length > max_valid_variant_length && *length < enum_variant_align_threshold { - max_valid_variant_length = *length; + let type_field = &variant.0; + // Add name + formatted_code.push_str(type_field.name.as_str()); + let current_variant_length = variant_length[var_index]; + if current_variant_length < max_valid_variant_length { + // We need to add alignment between : and ty + // max_valid_variant_length: the length of the variant that we are taking as a reference to align + // current_variant_length: the length of the current variant that we are trying to format + let mut required_alignment = + max_valid_variant_length - current_variant_length; + while required_alignment != 0 { + formatted_code.push(' '); + required_alignment -= 1; + } + } + // Add `:`, ty & `CommaToken` + // + // TODO(#2101): We are currently converting ty to string directly but we will probably need to format ty before adding. + write!( + formatted_code, + " {} {}", + type_field.colon_token.ident().as_str(), + type_field.ty.span().as_str(), + )?; + if value_pairs_iter.peek().is_some() { + writeln!(formatted_code, "{}", variant.1.span().as_str())?; + } else if let Some(final_value) = &variants.final_value_opt { + formatted_code.push_str(final_value.span().as_str()); + } + } } - }); - - // In second iteration we are going to be comparing current variants length and maximum accepted variant length - // for calculating the alignment required. - for (index, type_field) in type_fields.iter().enumerate() { - let type_field = &type_field.0; - // Push the current indentation level - formatted_code.push_str(&formatter.shape.indent.to_string(formatter)); - formatted_code.push_str(type_field.name.as_str()); - - // Currently does not apply custom formatting for ty, - let current_variant_length = variant_length[index]; - - if current_variant_length < max_valid_variant_length { - // We need to add alignment between : and ty - // max_valid_variant_length: the length of the variant that we are taking as a reference to allign - // current_variant_length: the length of the current variant that we are trying to format - let required_alignment = max_valid_variant_length - current_variant_length; - // TODO: Improve handling this - formatted_code.push_str(&(0..required_alignment).map(|_| ' ').collect::()); + FieldAlignment::Off => { + let mut value_pairs_iter = variants.value_separator_pairs.iter().peekable(); + for variant in value_pairs_iter.clone() { + formatted_code.push_str(&formatter.shape.indent.to_string(formatter)); + let item_field = &variant.0; + item_field.format(formatted_code, formatter)?; + + if value_pairs_iter.peek().is_some() { + writeln!(formatted_code, "{}", variant.1.span().as_str())?; + } + } + if let Some(final_value) = &variants.final_value_opt { + formatted_code.push_str(&formatter.shape.indent.to_string(formatter)); + final_value.format(formatted_code, formatter)?; + writeln!(formatted_code, "{}", PunctKind::Comma.as_char())?; + } } - formatted_code.push_str(" : "); - // TODO: We are currently converting ty to string directly but we will probably need to format ty before adding. - formatted_code.push_str(type_field.ty.span().as_str()); - formatted_code.push(','); - - // TODO: Here we assume that next enum variant is going to be in the new line but - // from the config we may understand next enum variant should be in the same line instead. - formatted_code.push('\n'); } - ItemEnum::close_curly_brace(&mut formatted_code, formatter); - formatted_code + } else { + // non-multiline formatting + formatted_code.push(' '); + let mut value_pairs_iter = variants.value_separator_pairs.iter().peekable(); + for variant in value_pairs_iter.clone() { + let item_field = &variant.0; + item_field.format(formatted_code, formatter)?; + + if value_pairs_iter.peek().is_some() { + write!(formatted_code, "{} ", variant.1.span().as_str())?; + } + } + if let Some(final_value) = &variants.final_value_opt { + final_value.format(formatted_code, formatter)?; + formatted_code.push(' '); + } else { + formatted_code.pop(); + formatted_code.pop(); + formatted_code.push(' '); + } + } + + // Handle closing brace + ItemEnum::close_curly_brace(formatted_code, formatter)?; + Ok(()) +} + +impl ItemLen for ItemEnum { + fn get_formatted_len(&self) -> usize { + // TODO while determininig the length we may want to format to some degree and take length. + let str_item = &self.span().as_str().len(); + *str_item as usize } } impl CurlyBrace for ItemEnum { - fn open_curly_brace(line: &mut String, formatter: &mut Formatter) { + fn open_curly_brace( + line: &mut String, + formatter: &mut Formatter, + ) -> Result<(), FormatterError> { let brace_style = formatter.config.items.item_brace_style; + let extra_width = formatter.config.whitespace.tab_spaces; let mut shape = formatter.shape; + let open_brace = Delimiter::Brace.as_open_char(); match brace_style { ItemBraceStyle::AlwaysNextLine => { // Add openning brace to the next line. - line.push_str("\n{\n"); - shape = shape.block_indent(1); + writeln!(line, "\n{}", open_brace)?; + shape = shape.block_indent(extra_width); } _ => { // Add opening brace to the same line - line.push_str(" {\n"); - shape = shape.block_indent(1); + write!(line, " {}", open_brace)?; + shape = shape.block_indent(extra_width); } } formatter.shape = shape; + Ok(()) } - - fn close_curly_brace(line: &mut String, formatter: &mut Formatter) { - line.push('}'); - // If shape is becoming left-most alligned or - indent just have the defualt shape - formatter.shape = formatter.shape.shrink_left(1).unwrap_or_default(); + fn close_curly_brace( + line: &mut String, + formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + line.push(Delimiter::Brace.as_close_char()); + // If shape is becoming left-most aligned or - indent just have the defualt shape + formatter.shape = formatter + .shape + .shrink_left(formatter.config.whitespace.tab_spaces) + .unwrap_or_default(); + Ok(()) } } diff --git a/sway-fmt-v2/src/items/item_fn.rs b/sway-fmt-v2/src/items/item_fn.rs index 123a294debd..d50df8d7827 100644 --- a/sway-fmt-v2/src/items/item_fn.rs +++ b/sway-fmt-v2/src/items/item_fn.rs @@ -1,8 +1,15 @@ -use crate::fmt::{Format, FormattedCode, Formatter}; +use crate::{ + fmt::{Format, FormattedCode, Formatter}, + FormatterError, +}; use sway_parse::ItemFn; impl Format for ItemFn { - fn format(&self, _formatter: &mut Formatter) -> FormattedCode { + fn format( + &self, + _formatted_code: &mut FormattedCode, + _formatter: &mut Formatter, + ) -> Result<(), FormatterError> { todo!() } } diff --git a/sway-fmt-v2/src/items/item_impl.rs b/sway-fmt-v2/src/items/item_impl.rs index 2a7e53e3ce0..a14aedad852 100644 --- a/sway-fmt-v2/src/items/item_impl.rs +++ b/sway-fmt-v2/src/items/item_impl.rs @@ -1,8 +1,15 @@ -use crate::fmt::{Format, FormattedCode, Formatter}; +use crate::{ + fmt::{Format, FormattedCode, Formatter}, + FormatterError, +}; use sway_parse::ItemImpl; impl Format for ItemImpl { - fn format(&self, _formatter: &mut Formatter) -> FormattedCode { + fn format( + &self, + _formatted_code: &mut FormattedCode, + _formatter: &mut Formatter, + ) -> Result<(), FormatterError> { todo!() } } diff --git a/sway-fmt-v2/src/items/item_storage.rs b/sway-fmt-v2/src/items/item_storage.rs index dd7f2962a23..8d3f385adb7 100644 --- a/sway-fmt-v2/src/items/item_storage.rs +++ b/sway-fmt-v2/src/items/item_storage.rs @@ -1,8 +1,15 @@ -use crate::fmt::{Format, FormattedCode, Formatter}; +use crate::{ + fmt::{Format, FormattedCode, Formatter}, + FormatterError, +}; use sway_parse::ItemStorage; impl Format for ItemStorage { - fn format(&self, _formatter: &mut Formatter) -> FormattedCode { + fn format( + &self, + _formatted_code: &mut FormattedCode, + _formatter: &mut Formatter, + ) -> Result<(), FormatterError> { todo!() } } diff --git a/sway-fmt-v2/src/items/item_struct.rs b/sway-fmt-v2/src/items/item_struct.rs index ebc34dcab60..6d4c35c697c 100644 --- a/sway-fmt-v2/src/items/item_struct.rs +++ b/sway-fmt-v2/src/items/item_struct.rs @@ -1,8 +1,233 @@ -use crate::fmt::{Format, FormattedCode, Formatter}; -use sway_parse::ItemStruct; +use crate::{ + config::{items::ItemBraceStyle, user_def::FieldAlignment}, + fmt::{Format, FormattedCode, Formatter}, + utils::{bracket::CurlyBrace, item_len::ItemLen}, + FormatterError, +}; +use std::fmt::Write; +use sway_parse::{ + token::{Delimiter, PunctKind}, + ItemStruct, +}; +use sway_types::Spanned; impl Format for ItemStruct { - fn format(&self, _formatter: &mut Formatter) -> FormattedCode { - todo!() + fn format( + &self, + formatted_code: &mut FormattedCode, + formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + // Bring configurations into scope. + // + // Should small structs formatted into a single line. + let struct_lit_single_line = formatter.config.structures.small_structures_single_line; + + // Get the width limit of a struct to be formatted into single line if struct_lit_single_line is true. + let width_heuristics = formatter + .config + .heuristics + .heuristics_pref + .to_width_heuristics(&formatter.config.whitespace); + let struct_lit_width = width_heuristics.structure_lit_width; + + let multiline = !struct_lit_single_line || self.get_formatted_len() > struct_lit_width; + + format_struct(self, formatted_code, formatter, multiline)?; + Ok(()) + } +} + +/// Format the struct if the multiline is passed as false struct will be formatted into a single line. +/// +/// ## Examples +/// +/// (multiline : false): +/// +/// ```rust,ignore +/// struct Foo { bar: u64, baz: bool } +/// ``` +/// +/// (multiline : true): +/// +/// ```rust,ignore +/// struct Foo { +/// bar: u64, +/// baz: bool, +/// } +/// ``` +fn format_struct( + item_struct: &ItemStruct, + formatted_code: &mut FormattedCode, + formatter: &mut Formatter, + multiline: bool, +) -> Result<(), FormatterError> { + // If there is a visibility token add it to the formatted_code with a ` ` after it. + if let Some(visibility) = &item_struct.visibility { + write!(formatted_code, "{} ", visibility.span().as_str())?; + } + // Add struct token + write!( + formatted_code, + "{} ", + item_struct.struct_token.span().as_str() + )?; + + // Add struct name + formatted_code.push_str(item_struct.name.as_str()); + + // Format `GenericParams`, if any + if let Some(generics) = &item_struct.generics { + generics.format(formatted_code, formatter)?; + } + + let fields = item_struct.fields.clone().into_inner(); + + // Handle openning brace + ItemStruct::open_curly_brace(formatted_code, formatter)?; + if multiline { + formatted_code.push('\n'); + // Determine alignment tactic + match formatter.config.structures.field_alignment { + FieldAlignment::AlignFields(struct_field_align_threshold) => { + let value_pairs = fields.value_separator_pairs; + // In first iteration we are going to be collecting the lengths of the struct fields. + let field_length: Vec = value_pairs + .iter() + .map(|field| field.0.name.as_str().len()) + .collect(); + + // Find the maximum length in the `field_length` vector that is still smaller than `struct_field_align_threshold`. + // `max_valid_field_length`: the length of the field that we are taking as a reference to align. + let mut max_valid_field_length = 0; + field_length.iter().for_each(|length| { + if *length > max_valid_field_length && *length < struct_field_align_threshold { + max_valid_field_length = *length; + } + }); + + let mut value_pairs_iter = value_pairs.iter().enumerate().peekable(); + for (field_index, field) in value_pairs_iter.clone() { + formatted_code.push_str(&formatter.shape.indent.to_string(formatter)); + + let type_field = &field.0; + // Add name + formatted_code.push_str(type_field.name.as_str()); + + // `current_field_length`: the length of the current field that we are trying to format. + let current_field_length = field_length[field_index]; + if current_field_length < max_valid_field_length { + // We need to add alignment between `:` and `ty` + let mut required_alignment = max_valid_field_length - current_field_length; + while required_alignment != 0 { + formatted_code.push(' '); + required_alignment -= 1; + } + } + // Add `:`, `ty` & `CommaToken` + // + // TODO(#2101): We are currently converting ty to string directly but + // we will probably need to format `ty` before adding. + write!( + formatted_code, + " {} {}", + type_field.colon_token.ident().as_str(), + type_field.ty.span().as_str(), + )?; + if value_pairs_iter.peek().is_some() { + writeln!(formatted_code, "{}", field.1.span().as_str())?; + } else if let Some(final_value) = &fields.final_value_opt { + formatted_code.push_str(final_value.span().as_str()); + } + } + } + FieldAlignment::Off => { + let mut value_pairs_iter = fields.value_separator_pairs.iter().peekable(); + for field in value_pairs_iter.clone() { + formatted_code.push_str(&formatter.shape.indent.to_string(formatter)); + let item_field = &field.0; + item_field.format(formatted_code, formatter)?; + + if value_pairs_iter.peek().is_some() { + writeln!(formatted_code, "{}", field.1.span().as_str())?; + } + } + if let Some(final_value) = &fields.final_value_opt { + formatted_code.push_str(&formatter.shape.indent.to_string(formatter)); + final_value.format(formatted_code, formatter)?; + writeln!(formatted_code, "{}", PunctKind::Comma.as_char())?; + } + } + } + } else { + // non-multiline formatting + formatted_code.push(' '); + let mut value_pairs_iter = fields.value_separator_pairs.iter().peekable(); + for field in value_pairs_iter.clone() { + let type_field = &field.0; + type_field.format(formatted_code, formatter)?; + + if value_pairs_iter.peek().is_some() { + write!(formatted_code, "{} ", field.1.span().as_str())?; + } + } + if let Some(final_value) = &fields.final_value_opt { + final_value.format(formatted_code, formatter)?; + formatted_code.push(' '); + } else { + formatted_code.pop(); + formatted_code.pop(); + formatted_code.push(' '); + } + } + + // Handle closing brace + ItemStruct::close_curly_brace(formatted_code, formatter)?; + Ok(()) +} + +impl ItemLen for ItemStruct { + fn get_formatted_len(&self) -> usize { + // TODO while determininig the length we may want to format to some degree and take length. + let str_item = &self.span().as_str().len(); + *str_item as usize + } +} + +impl CurlyBrace for ItemStruct { + fn open_curly_brace( + line: &mut String, + formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + let brace_style = formatter.config.items.item_brace_style; + let extra_width = formatter.config.whitespace.tab_spaces; + let mut shape = formatter.shape; + match brace_style { + ItemBraceStyle::AlwaysNextLine => { + // Add openning brace to the next line. + write!(line, "\n{}", Delimiter::Brace.as_open_char())?; + shape = shape.block_indent(extra_width); + } + _ => { + // Add opening brace to the same line + write!(line, " {}", Delimiter::Brace.as_open_char())?; + shape = shape.block_indent(extra_width); + } + } + + formatter.shape = shape; + Ok(()) + } + + fn close_curly_brace( + line: &mut String, + formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + line.push(Delimiter::Brace.as_close_char()); + // If shape is becoming left-most alligned or - indent just have the defualt shape + formatter.shape = formatter + .shape + .shrink_left(formatter.config.whitespace.tab_spaces) + .unwrap_or_default(); + Ok(()) } } diff --git a/sway-fmt-v2/src/items/item_trait.rs b/sway-fmt-v2/src/items/item_trait.rs index 4b28ba62089..d6d0010da47 100644 --- a/sway-fmt-v2/src/items/item_trait.rs +++ b/sway-fmt-v2/src/items/item_trait.rs @@ -1,8 +1,15 @@ -use crate::fmt::{Format, FormattedCode, Formatter}; +use crate::{ + fmt::{Format, FormattedCode, Formatter}, + FormatterError, +}; use sway_parse::ItemTrait; impl Format for ItemTrait { - fn format(&self, _formatter: &mut Formatter) -> FormattedCode { + fn format( + &self, + _formatted_code: &mut FormattedCode, + _formatter: &mut Formatter, + ) -> Result<(), FormatterError> { todo!() } } diff --git a/sway-fmt-v2/src/items/item_use.rs b/sway-fmt-v2/src/items/item_use.rs index 895cf2e7722..cf23ebc89dd 100644 --- a/sway-fmt-v2/src/items/item_use.rs +++ b/sway-fmt-v2/src/items/item_use.rs @@ -1,8 +1,15 @@ -use crate::fmt::{Format, FormattedCode, Formatter}; +use crate::{ + fmt::{Format, FormattedCode, Formatter}, + FormatterError, +}; use sway_parse::ItemUse; impl Format for ItemUse { - fn format(&self, _formatter: &mut Formatter) -> FormattedCode { + fn format( + &self, + _formatted_code: &mut FormattedCode, + _formatter: &mut Formatter, + ) -> Result<(), FormatterError> { todo!() } } diff --git a/sway-fmt-v2/src/utils.rs b/sway-fmt-v2/src/utils.rs index 96bd73c8b3e..8d97a0a8c28 100644 --- a/sway-fmt-v2/src/utils.rs +++ b/sway-fmt-v2/src/utils.rs @@ -1,4 +1,9 @@ -pub mod attributes; -pub mod bracket; -pub mod indent_style; -pub mod newline_style; +pub(crate) mod attribute; +pub(crate) mod bracket; +pub(crate) mod generics; +pub(crate) mod indent_style; +pub(crate) mod item; +pub(crate) mod item_len; +pub(crate) mod newline_style; +pub(crate) mod program_type; +pub(crate) mod punctuated; diff --git a/sway-fmt-v2/src/utils/attribute.rs b/sway-fmt-v2/src/utils/attribute.rs new file mode 100644 index 00000000000..dbceaaa918d --- /dev/null +++ b/sway-fmt-v2/src/utils/attribute.rs @@ -0,0 +1,93 @@ +use crate::{ + fmt::{Format, FormattedCode, Formatter}, + FormatterError, +}; +use std::fmt::Write; +use sway_parse::{ + attribute::{Annotated, AttributeDecl}, + token::Delimiter, + Parse, +}; +use sway_types::Spanned; + +use super::bracket::{Parenthesis, SquareBracket}; + +impl Format for Annotated { + fn format( + &self, + formatted_code: &mut FormattedCode, + formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + // format each `Attribute` + for attr in &self.attribute_list { + attr.format(formatted_code, formatter)?; + } + // format `ItemKind` + self.value.format(formatted_code, formatter) + } +} + +pub trait FormatDecl { + fn format(&self, line: &mut String, formatter: &mut Formatter) -> Result<(), FormatterError>; +} + +impl FormatDecl for AttributeDecl { + fn format(&self, line: &mut String, formatter: &mut Formatter) -> Result<(), FormatterError> { + // At some point there will be enough attributes to warrant the need + // of formatting the list according to `config::lists::ListTactic`. + // For now the default implementation will be `Horizontal`. + // + // `#` + line.push_str(self.hash_token.span().as_str()); + // `[` + Self::open_square_bracket(line, formatter)?; + let attr = self.attribute.clone().into_inner(); + // name e.g. `storage` + line.push_str(attr.name.span().as_str()); + // `(` + Self::open_parenthesis(line, formatter)?; + // format and add args e.g. `read, write` + if let Some(args) = attr.args { + args.into_inner().format(line, formatter)?; + } + // ')' + Self::close_parenthesis(line, formatter)?; + // `]\n` + Self::close_square_bracket(line, formatter)?; + Ok(()) + } +} + +impl SquareBracket for AttributeDecl { + fn open_square_bracket( + line: &mut String, + _formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + line.push(Delimiter::Bracket.as_open_char()); + Ok(()) + } + fn close_square_bracket( + line: &mut String, + _formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + writeln!(line, "{}", Delimiter::Bracket.as_close_char())?; + Ok(()) + } +} + +impl Parenthesis for AttributeDecl { + fn open_parenthesis( + line: &mut String, + _formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + line.push(Delimiter::Parenthesis.as_open_char()); + Ok(()) + } + fn close_parenthesis( + line: &mut String, + _formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + line.push(Delimiter::Parenthesis.as_close_char()); + Ok(()) + } +} diff --git a/sway-fmt-v2/src/utils/attributes.rs b/sway-fmt-v2/src/utils/attributes.rs deleted file mode 100644 index 4700c41b7f6..00000000000 --- a/sway-fmt-v2/src/utils/attributes.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::Formatter; -use sway_parse::AttributeDecl; -use sway_types::Spanned; -pub fn format_attributes(attributes: Vec, _formatter: &mut Formatter) -> String { - // TODO format attributes - attributes - .into_iter() - .map(|x| x.span().as_str().to_string()) - .collect::>() - .join("\n") -} diff --git a/sway-fmt-v2/src/utils/bracket.rs b/sway-fmt-v2/src/utils/bracket.rs index 906d2bcb50f..c5ca4e5884b 100644 --- a/sway-fmt-v2/src/utils/bracket.rs +++ b/sway-fmt-v2/src/utils/bracket.rs @@ -1,35 +1,61 @@ //! The purpose of this file is to house the traits and associated functions for formatting opening and closing delimiters. //! This allows us to avoid matching a second time for the `ItemKind` and keeps the code pertaining to individual formatting //! contained to each item's file. -use crate::Formatter; +use crate::{fmt::FormattedCode, Formatter, FormatterError}; -pub trait CurlyBrace { +pub(crate) trait CurlyBrace { /// Handles brace open scenerio. Checks the config for the placement of the brace. /// Modifies the current shape of the formatter. - fn open_curly_brace(line: &mut String, formatter: &mut Formatter); + fn open_curly_brace( + line: &mut FormattedCode, + formatter: &mut Formatter, + ) -> Result<(), FormatterError>; /// Handles brace close scenerio. /// Currently it simply pushes a `}` and modifies the shape. - fn close_curly_brace(line: &mut String, formatter: &mut Formatter); + fn close_curly_brace( + line: &mut FormattedCode, + formatter: &mut Formatter, + ) -> Result<(), FormatterError>; } -pub trait SquareBracket { - fn open_square_bracket(line: &mut String, formatter: &mut Formatter); +pub(crate) trait SquareBracket { + fn open_square_bracket( + line: &mut FormattedCode, + formatter: &mut Formatter, + ) -> Result<(), FormatterError>; - fn close_square_bracket(line: &mut String, formatter: &mut Formatter); + fn close_square_bracket( + line: &mut FormattedCode, + formatter: &mut Formatter, + ) -> Result<(), FormatterError>; } -pub trait Parenthesis { +pub(crate) trait Parenthesis { /// Handles open parenthesis scenarios, checking the config for placement /// and modifying the shape of the formatter where necessary. - fn open_parenthesis(line: &mut String, formatter: &mut Formatter); + fn open_parenthesis( + line: &mut FormattedCode, + formatter: &mut Formatter, + ) -> Result<(), FormatterError>; /// Handles the closing parenthesis scenario. - fn close_parenthesis(line: &mut String, formatter: &mut Formatter); + fn close_parenthesis( + line: &mut FormattedCode, + formatter: &mut Formatter, + ) -> Result<(), FormatterError>; } pub trait AngleBracket { - fn open_angle_bracket(line: &mut String, formatter: &mut Formatter); - - fn close_angle_bracket(line: &mut String, formatter: &mut Formatter); + fn open_angle_bracket( + self, + line: &mut FormattedCode, + formatter: &mut Formatter, + ) -> Result<(), FormatterError>; + + fn close_angle_bracket( + self, + line: &mut FormattedCode, + formatter: &mut Formatter, + ) -> Result<(), FormatterError>; } diff --git a/sway-fmt-v2/src/utils/generics.rs b/sway-fmt-v2/src/utils/generics.rs new file mode 100644 index 00000000000..0b69758476d --- /dev/null +++ b/sway-fmt-v2/src/utils/generics.rs @@ -0,0 +1,48 @@ +use crate::{ + fmt::{Format, FormattedCode, Formatter, FormatterError}, + utils::bracket::AngleBracket, +}; +use sway_parse::GenericParams; +use sway_types::Spanned; + +// In the future we will need to determine whether the generic arguments +// are better suited with a `where` clause. At present they will be +// formatted in line. +// +impl Format for GenericParams { + fn format( + &self, + formatted_code: &mut FormattedCode, + formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + let params = self.parameters.clone().into_inner(); + + // `<` + Self::open_angle_bracket(self.clone(), formatted_code, formatter)?; + // format and add parameters + params.format(formatted_code, formatter)?; + // `>` + Self::close_angle_bracket(self.clone(), formatted_code, formatter)?; + + Ok(()) + } +} + +impl AngleBracket for GenericParams { + fn open_angle_bracket( + self, + line: &mut String, + _formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + line.push_str(self.parameters.open_angle_bracket_token.span().as_str()); + Ok(()) + } + fn close_angle_bracket( + self, + line: &mut String, + _formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + line.push_str(self.parameters.close_angle_bracket_token.span().as_str()); + Ok(()) + } +} diff --git a/sway-fmt-v2/src/utils/item.rs b/sway-fmt-v2/src/utils/item.rs new file mode 100644 index 00000000000..e6e8bd00de0 --- /dev/null +++ b/sway-fmt-v2/src/utils/item.rs @@ -0,0 +1,23 @@ +use sway_parse::{Item, ItemKind::*}; + +use crate::fmt::{Format, FormattedCode, Formatter, FormatterError}; + +impl Format for Item { + fn format( + &self, + formatted_code: &mut FormattedCode, + formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + match &self.value { + Use(item_use) => item_use.format(formatted_code, formatter), + Struct(item_struct) => item_struct.format(formatted_code, formatter), + Enum(item_enum) => item_enum.format(formatted_code, formatter), + Fn(item_fn) => item_fn.format(formatted_code, formatter), + Trait(item_trait) => item_trait.format(formatted_code, formatter), + Impl(item_impl) => item_impl.format(formatted_code, formatter), + Abi(item_abi) => item_abi.format(formatted_code, formatter), + Const(item_const) => item_const.format(formatted_code, formatter), + Storage(item_storage) => item_storage.format(formatted_code, formatter), + } + } +} diff --git a/sway-fmt-v2/src/utils/item_len.rs b/sway-fmt-v2/src/utils/item_len.rs new file mode 100644 index 00000000000..4a4279b7440 --- /dev/null +++ b/sway-fmt-v2/src/utils/item_len.rs @@ -0,0 +1,3 @@ +pub trait ItemLen { + fn get_formatted_len(&self) -> usize; +} diff --git a/sway-fmt-v2/src/utils/program_type.rs b/sway-fmt-v2/src/utils/program_type.rs new file mode 100644 index 00000000000..13b7b6e796f --- /dev/null +++ b/sway-fmt-v2/src/utils/program_type.rs @@ -0,0 +1,29 @@ +use sway_parse::{token::PunctKind, ModuleKind}; +use sway_types::Spanned; + +/// Insert the program type without applying a formatting to it. +/// +/// Possible list of program types: +/// - Script +/// - Contract +/// - Predicate +/// - Library +pub(crate) fn insert_program_type(push_to: &mut String, module_kind: ModuleKind) { + match module_kind { + ModuleKind::Script { script_token } => push_to.push_str(script_token.span().as_str()), + ModuleKind::Contract { contract_token } => push_to.push_str(contract_token.span().as_str()), + ModuleKind::Predicate { predicate_token } => { + push_to.push_str(predicate_token.span().as_str()) + } + ModuleKind::Library { + library_token, + name, + } => { + push_to.push_str(library_token.span().as_str()); + push_to.push(' '); + push_to.push_str(name.as_str()); + } + }; + push_to.push(PunctKind::Semicolon.as_char()); + push_to.push_str("\n\n"); +} diff --git a/sway-fmt-v2/src/utils/punctuated.rs b/sway-fmt-v2/src/utils/punctuated.rs new file mode 100644 index 00000000000..96ba1d501da --- /dev/null +++ b/sway-fmt-v2/src/utils/punctuated.rs @@ -0,0 +1,57 @@ +use crate::{ + fmt::{Format, FormattedCode, Formatter}, + FormatterError, +}; +use std::fmt::Write; +use sway_parse::{punctuated::Punctuated, TypeField}; +use sway_types::Spanned; + +impl Format for Punctuated +where + T: Spanned, + P: Spanned, +{ + fn format( + &self, + formatted_code: &mut FormattedCode, + _formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + // format and add Type & Punct + let value_pairs = &self.value_separator_pairs; + + // Later on we may want to handle instances + // where the user wants to keep the trailing commas. + for pair in value_pairs.iter() { + write!( + formatted_code, + "{}{} ", + pair.0.span().as_str(), + pair.1.span().as_str(), + )?; + } + + // add final value, if any + if let Some(final_value) = &self.final_value_opt { + formatted_code.push_str(final_value.span().as_str()); + } + + Ok(()) + } +} + +impl Format for TypeField { + fn format( + &self, + formatted_code: &mut FormattedCode, + _formatter: &mut Formatter, + ) -> Result<(), FormatterError> { + write!( + formatted_code, + "{}{} {}", + self.name.span().as_str(), + self.colon_token.span().as_str(), + self.ty.span().as_str() + )?; + Ok(()) + } +} diff --git a/sway-fmt/Cargo.toml b/sway-fmt/Cargo.toml index 72f4967f3c9..c7016da9e7a 100644 --- a/sway-fmt/Cargo.toml +++ b/sway-fmt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sway-fmt" -version = "0.16.1" +version = "0.16.2" authors = ["Fuel Labs "] edition = "2021" homepage = "https://fuel.network/" @@ -10,6 +10,6 @@ description = "Sway sway-fmt." [dependencies] ropey = "1.2" -sway-core = { version = "0.16.1", path = "../sway-core" } -sway-types = { version = "0.16.1", path = "../sway-types" } +sway-core = { version = "0.16.2", path = "../sway-core" } +sway-types = { version = "0.16.2", path = "../sway-types" } diff --git a/sway-ir/Cargo.toml b/sway-ir/Cargo.toml index 43759b163c5..2af5a31b5a9 100644 --- a/sway-ir/Cargo.toml +++ b/sway-ir/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sway-ir" -version = "0.16.1" +version = "0.16.2" authors = ["Fuel Labs "] edition = "2021" homepage = "https://fuel.network/" @@ -12,5 +12,5 @@ description = "Sway intermediate representation." filecheck = "0.5" generational-arena = "0.2" peg = "0.7" -sway-types = { version = "0.16.1", path = "../sway-types" } +sway-types = { version = "0.16.2", path = "../sway-types" } tracing = "0.1" diff --git a/sway-ir/src/block.rs b/sway-ir/src/block.rs index b7dd20307ee..d2a8f85efee 100644 --- a/sway-ir/src/block.rs +++ b/sway-ir/src/block.rs @@ -149,6 +149,11 @@ impl Block { }) } + pub fn is_terminated_by_ret(&self, context: &Context) -> bool { + self.get_term_inst(context) + .map_or(false, |i| matches!(i, Instruction::Ret { .. })) + } + /// Replace a value within this block. /// /// For every instruction within the block, any reference to `old_val` is replaced with diff --git a/sway-ir/src/verify.rs b/sway-ir/src/verify.rs index cdde4d1b024..592a52cbcbd 100644 --- a/sway-ir/src/verify.rs +++ b/sway-ir/src/verify.rs @@ -173,10 +173,9 @@ impl<'a> InstructionVerifier<'a> { fn verify_bitcast(&self, value: &Value, ty: &Type) -> Result<(), IrError> { // The to and from types must be copy-types, excluding short strings, and the same size. - let val_ty = match value.get_type(self.context) { - None => return Err(IrError::VerifyBitcastUnknownSourceType), - Some(ty) => ty, - }; + let val_ty = value + .get_type(self.context) + .ok_or(IrError::VerifyBitcastUnknownSourceType)?; if !val_ty.is_copy_type() { return Err(IrError::VerifyBitcastFromNonCopyType( val_ty.as_string(self.context), @@ -305,6 +304,7 @@ impl<'a> InstructionVerifier<'a> { Ok(()) } } + (Type::Bool, Type::Bool) => Ok(()), _otherwise => Err(IrError::VerifyCmpBadTypes( lhs_ty.as_string(self.context), rhs_ty.as_string(self.context), diff --git a/sway-lib-core/src/ops.sw b/sway-lib-core/src/ops.sw index 4d3382caf31..d009e8d4ce3 100644 --- a/sway-lib-core/src/ops.sw +++ b/sway-lib-core/src/ops.sw @@ -210,46 +210,31 @@ pub trait Eq { impl Eq for bool { fn eq(self, other: Self) -> bool { - asm(r1: self, r2: other, r3) { - eq r3 r1 r2; - r3: bool - } + __eq(self, other) } } impl Eq for u64 { fn eq(self, other: Self) -> bool { - asm(r1: self, r2: other, r3) { - eq r3 r1 r2; - r3: bool - } + __eq(self, other) } } impl Eq for u32 { fn eq(self, other: Self) -> bool { - asm(r1: self, r2: other, r3) { - eq r3 r1 r2; - r3: bool - } + __eq(self, other) } } impl Eq for u16 { fn eq(self, other: Self) -> bool { - asm(r1: self, r2: other, r3) { - eq r3 r1 r2; - r3: bool - } + __eq(self, other) } } impl Eq for u8 { fn eq(self, other: Self) -> bool { - asm(r1: self, r2: other, r3) { - eq r3 r1 r2; - r3: bool - } + __eq(self, other) } } diff --git a/sway-lib-std/src/constants.sw b/sway-lib-std/src/constants.sw index b73e6cadf25..ea16cbda0cf 100644 --- a/sway-lib-std/src/constants.sw +++ b/sway-lib-std/src/constants.sw @@ -1,3 +1,5 @@ library constants; -const BASE_ASSET_ID = 0x0000000000000000000000000000000000000000000000000000000000000000; +use ::contract_id::ContractId; + +const BASE_ASSET_ID = ~ContractId::from(0x0000000000000000000000000000000000000000000000000000000000000000); diff --git a/sway-lib-std/src/identity.sw b/sway-lib-std/src/identity.sw index d39c1ac5ce1..64143d047fd 100644 --- a/sway-lib-std/src/identity.sw +++ b/sway-lib-std/src/identity.sw @@ -4,10 +4,12 @@ library identity; use ::address::Address; use ::contract_id::ContractId; +// ANCHOR: docs_identity pub enum Identity { Address: Address, ContractId: ContractId, } +// ANCHOR_END: docs_identity impl core::ops::Eq for Identity { fn eq(self, other: Self) -> bool { diff --git a/sway-lib-std/src/lib.sw b/sway-lib-std/src/lib.sw index e3b9be440d2..aa54fcd82ac 100644 --- a/sway-lib-std/src/lib.sw +++ b/sway-lib-std/src/lib.sw @@ -8,11 +8,11 @@ dep option; dep result; dep mem; dep alloc; -dep constants; dep contract_id; +dep constants; dep context; dep hash; -dep storage; +dep r#storage; dep b512; dep address; dep identity; @@ -26,6 +26,7 @@ dep reentrancy; dep vm/mod; dep flags; dep u128; +dep u256; dep vec; use core::*; diff --git a/sway-lib-std/src/storage.sw b/sway-lib-std/src/storage.sw index 1ec7160fc9b..f48b71534c3 100644 --- a/sway-lib-std/src/storage.sw +++ b/sway-lib-std/src/storage.sw @@ -1,4 +1,4 @@ -library storage; +library r#storage; use ::hash::sha256; use ::context::registers::stack_ptr; @@ -16,7 +16,7 @@ use ::context::registers::stack_ptr; let mut size_left = __size_of::(); let mut local_key = key; - // Cast the the pointer to `value` to a u64. This lets us increment + // Cast the pointer to `value` to a u64. This lets us increment // this pointer later on to iterate over 32 byte chunks of `value`. let mut ptr_to_value = asm(v: value) { v diff --git a/sway-lib-std/src/u128.sw b/sway-lib-std/src/u128.sw index 51f0ba28033..82a7ce2dfaf 100644 --- a/sway-lib-std/src/u128.sw +++ b/sway-lib-std/src/u128.sw @@ -12,6 +12,10 @@ pub struct U128 { lower: u64, } +pub enum U128Error { + LossOfPrecision: (), +} + pub trait From { /// Function for creating U128 from its u64 components. pub fn from(upper: u64, lower: u64) -> Self; @@ -24,7 +28,7 @@ pub trait From { impl From for U128 { pub fn from(upper: u64, lower: u64) -> U128 { U128 { - upper, lower, + upper, lower, } } } @@ -102,14 +106,15 @@ impl U128 { } } - /// Downcast to `u64`. Err if precision would be lost, Ok otherwise. - pub fn to_u64(self) -> Result { + /// Safely downcast to `u64` without loss of precision. + /// Returns Err if the number > ~u64::max() + pub fn as_u64(self) -> Result { match self.upper { 0 => { Result::Ok(self.lower) }, _ => { - Result::Err(()) + Result::Err(U128Error::LossOfPrecision) }, } } @@ -245,7 +250,7 @@ impl core::ops::Subtract for U128 { } U128 { - upper, lower, + upper, lower, } } } diff --git a/sway-lib-std/src/u256.sw b/sway-lib-std/src/u256.sw new file mode 100644 index 00000000000..5d9547c3deb --- /dev/null +++ b/sway-lib-std/src/u256.sw @@ -0,0 +1,90 @@ +library u256; + +use core::num::*; +use ::result::Result; + +/// The 256-bit unsigned integer type. +/// Represented as four 64-bit components: `(a, b, c, d)`, where `value = (a << 192) + (b << 128) + (c << 64) + d`. +pub struct U256 { + a: u64, + b: u64, + c: u64, + d: u64, +} + +pub enum U256Error { + LossOfPrecision: (), +} + +pub trait From { + /// Function for creating a U256 from its u64 components. + pub fn from(a: u64, b: u64, c: u64, d: u64) -> Self; +} { + /// Function for extracting 4 u64s from a U256. + fn into(val: U256) -> (u64, u64, u64, u64) { + (val.a, val.b, val.c, val.d) + } +} + +impl From for U256 { + pub fn from(a: u64, b: u64, c: u64, d: u64) -> U256 { + U256 { + a, b, c, d, + } + } +} + +impl core::ops::Eq for U256 { + /// Function for comparing 2 U256s for equality + pub fn eq(self, other: Self) -> bool { + self.a == other.a && self.b == other.b && self.c == other.c && self.d == other.d + } +} + +impl U256 { + /// Initializes a new, zeroed U256. + pub fn new() -> U256 { + U256 { + a: 0, + b: 0, + c: 0, + d: 0, + } + } + + /// Safely downcast to `u64` without loss of precision. + /// Returns Err if the number > ~u64::max() + pub fn as_u64(self) -> Result { + if self.a == 0 && self.b == 0 && self.c == 0 { + Result::Ok(self.d) + } else { + Result::Err(U256Error::LossOfPrecision) + } + } + + /// The smallest value that can be represented by this integer type. + pub fn min() -> U256 { + U256 { + a: 0, + b: 0, + c: 0, + d: 0, + } + } + + /// The largest value that can be represented by this type, + /// 2256 - 1. + pub fn max() -> U256 { + U256 { + a: ~u64::max(), + b: ~u64::max(), + c: ~u64::max(), + d: ~u64::max(), + } + } + + /// The size of this type in bits. + pub fn bits() -> u32 { + 256 + } +} diff --git a/sway-lsp/Cargo.toml b/sway-lsp/Cargo.toml index d156c9fd2dd..fd4ae6cd023 100644 --- a/sway-lsp/Cargo.toml +++ b/sway-lsp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sway-lsp" -version = "0.16.1" +version = "0.16.2" authors = ["Fuel Labs "] edition = "2021" homepage = "https://fuel.network/" @@ -9,14 +9,16 @@ repository = "https://github.com/FuelLabs/sway" description = "LSP server for Sway." [dependencies] -dashmap = "4.0.2" -forc-util = { version = "0.16.1", path = "../forc-util" } +dashmap = "5.3.4" +forc = { version = "0.16.2", path = "../forc" } +forc-pkg = { version = "0.16.2", path = "../forc-pkg" } +forc-util = { version = "0.16.2", path = "../forc-util" } ropey = "1.2" serde_json = "1.0.60" -sway-core = { version = "0.16.1", path = "../sway-core" } -sway-fmt = { version = "0.16.1", path = "../sway-fmt" } -sway-types = { version = "0.16.1", path = "../sway-types" } -sway-utils = { version = "0.16.1", path = "../sway-utils" } +sway-core = { version = "0.16.2", path = "../sway-core" } +sway-fmt = { version = "0.16.2", path = "../sway-fmt" } +sway-types = { version = "0.16.2", path = "../sway-types" } +sway-utils = { version = "0.16.2", path = "../sway-utils" } tokio = { version = "1.3", features = ["io-std", "io-util", "macros", "net", "rt-multi-thread", "sync", "time"] } tower-lsp = "0.16.0" tracing = "0.1" diff --git a/sway-lsp/src/core/document.rs b/sway-lsp/src/core/document.rs index 8137bd89fec..3eea41621a5 100644 --- a/sway-lsp/src/core/document.rs +++ b/sway-lsp/src/core/document.rs @@ -6,14 +6,10 @@ use super::traverse_typed_tree; use super::typed_token_type::TokenMap; use crate::{capabilities, core::token::traverse_node, utils}; +use forc_pkg::{self as pkg}; use ropey::Rope; -use std::collections::HashMap; -use std::sync::Arc; -use sway_core::{ - parse, - semantic_analysis::{ast_node::TypedAstNode, namespace}, - CompileAstResult, TreeType, -}; +use std::{collections::HashMap, path::PathBuf, sync::Arc}; +use sway_core::{parse, semantic_analysis::ast_node::TypedAstNode, CompileAstResult, TreeType}; use tower_lsp::lsp_types::{Diagnostic, Position, Range, TextDocumentContentChangeEvent}; #[derive(Debug)] @@ -160,10 +156,15 @@ impl TextDocument { // private methods impl TextDocument { fn parse_typed_tokens_from_text(&self) -> Option> { - let text = Arc::from(self.get_text()); - let namespace = namespace::Module::default(); - let ast_res = sway_core::compile_to_ast(text, namespace, None); - match ast_res { + let manifest_dir = PathBuf::from(self.get_uri()); + let silent_mode = true; + let manifest = + pkg::ManifestFile::from_dir(&manifest_dir, forc::utils::SWAY_GIT_TAG).unwrap(); + let lock_path = forc_util::lock_path(manifest.dir()); + let plan = pkg::BuildPlan::from_lock_file(&lock_path, forc::utils::SWAY_GIT_TAG).unwrap(); + let res = pkg::check(&plan, silent_mode, forc::utils::SWAY_GIT_TAG).unwrap(); + + match res { CompileAstResult::Failure { .. } => None, CompileAstResult::Success { typed_program, .. } => Some(typed_program.root.all_nodes), } diff --git a/sway-lsp/src/core/token.rs b/sway-lsp/src/core/token.rs index a7d3dd231bf..c03f83ef9b5 100644 --- a/sway-lsp/src/core/token.rs +++ b/sway-lsp/src/core/token.rs @@ -8,7 +8,7 @@ use crate::{ use sway_core::{ constants::TUPLE_NAME_PREFIX, parse_tree::MethodName, type_engine::TypeInfo, AstNode, AstNodeContent, Declaration, Expression, FunctionDeclaration, FunctionParameter, - IntrinsicFunctionKind, VariableDeclaration, WhileLoop, + VariableDeclaration, WhileLoop, }; use sway_types::{ident::Ident, span::Span, Spanned}; use tower_lsp::lsp_types::Range; @@ -441,20 +441,15 @@ fn handle_expression(exp: Expression, tokens: &mut Vec) { tokens.push(token); } } - Expression::IntrinsicFunction { kind, .. } => { - handle_intrinsic_function(kind, tokens); + Expression::IntrinsicFunction { arguments, .. } => { + handle_intrinsic_function(arguments, tokens); } } } -fn handle_intrinsic_function(kind: IntrinsicFunctionKind, tokens: &mut Vec) { - match kind { - IntrinsicFunctionKind::SizeOfVal { exp } => { - handle_expression(*exp, tokens); - } - IntrinsicFunctionKind::SizeOfType { .. } => {} - IntrinsicFunctionKind::IsRefType { .. } => {} - IntrinsicFunctionKind::GetStorageKey => {} +fn handle_intrinsic_function(arguments: Vec, tokens: &mut Vec) { + for arg in arguments { + handle_expression(arg, tokens); } } diff --git a/sway-lsp/src/core/traverse_typed_tree.rs b/sway-lsp/src/core/traverse_typed_tree.rs index fa676176e5e..ee23d9b9cb2 100644 --- a/sway-lsp/src/core/traverse_typed_tree.rs +++ b/sway-lsp/src/core/traverse_typed_tree.rs @@ -332,14 +332,12 @@ fn handle_expression(expression: &TypedExpression, tokens: &mut TokenMap) { } } -fn handle_intrinsic_function(kind: &TypedIntrinsicFunctionKind, tokens: &mut TokenMap) { - match kind { - TypedIntrinsicFunctionKind::SizeOfVal { exp } => { - handle_expression(exp, tokens); - } - TypedIntrinsicFunctionKind::SizeOfType { .. } => {} - TypedIntrinsicFunctionKind::IsRefType { .. } => {} - TypedIntrinsicFunctionKind::GetStorageKey => {} +fn handle_intrinsic_function( + TypedIntrinsicFunctionKind { arguments, .. }: &TypedIntrinsicFunctionKind, + tokens: &mut TokenMap, +) { + for arg in arguments { + handle_expression(arg, tokens); } } diff --git a/sway-lsp/src/server.rs b/sway-lsp/src/server.rs index f167fa4af4f..5a66c1d9182 100644 --- a/sway-lsp/src/server.rs +++ b/sway-lsp/src/server.rs @@ -242,7 +242,7 @@ impl LanguageServer for Backend { #[cfg(test)] mod tests { use serde_json::json; - use std::{env, fs::File, io::Write}; + use std::{env, fs, io::Read}; use tower::{Service, ServiceExt}; use super::*; @@ -250,46 +250,20 @@ mod tests { use tower_lsp::jsonrpc::{self, Request, Response}; use tower_lsp::LspService; - // Simple sway script used for testing LSP capabilites - const SWAY_PROGRAM: &str = r#"script; - -//use std::*; - -/// A simple Particle struct -struct Particle { - position: [u64; 3], - velocity: [u64; 3], - acceleration: [u64; 3], - mass: u64, -} - -impl Particle { - /// Creates a new Particle with the given position, velocity, acceleration, and mass - fn new(position: [u64; 3], velocity: [u64; 3], acceleration: [u64; 3], mass: u64) -> Particle { - Particle { - position: position, - velocity: velocity, - acceleration: acceleration, - mass: mass, - } - } -} + fn load_sway_example() -> (Url, String) { + let manifest_dir = env::current_dir() + .unwrap() + .parent() + .unwrap() + .join("examples/liquidity_pool"); + let src_path = manifest_dir.join("src/main.sw"); + let mut file = fs::File::open(&src_path).unwrap(); + let mut sway_program = String::new(); + file.read_to_string(&mut sway_program).unwrap(); -fn main() { - let position = [0, 0, 0]; - let velocity = [0, 1, 0]; - let acceleration = [1, 1, 0]; - let mass = 10; - let p = ~Particle::new(position, velocity, acceleration, mass); -} -"#; + let uri = Url::from_file_path(src_path).unwrap(); - fn load_test_sway_file(sway_file: &str) -> Url { - let file_name = "tmp_sway_test_file.sw"; - let dir = env::temp_dir().join(file_name); - let mut file = File::create(&dir).unwrap(); - file.write_all(sway_file.as_bytes()).unwrap(); - Url::from_file_path(dir.as_os_str().to_str().unwrap()).unwrap() + (uri, sway_program) } async fn initialize_request(service: &mut LspService) -> Request { @@ -440,10 +414,10 @@ fn main() { // ignore the "window/logMessage" notification: "Initializing the Sway Language Server" messages.next().await.unwrap(); - let uri = load_test_sway_file(SWAY_PROGRAM); + let (uri, sway_program) = load_sway_example(); // send "textDocument/didOpen" notification for `uri` - did_open_notification(&mut service, &uri, SWAY_PROGRAM).await; + did_open_notification(&mut service, &uri, &sway_program).await; // ignore the "textDocument/publishDiagnostics" notification messages.next().await.unwrap(); @@ -465,10 +439,10 @@ fn main() { // send "initialized" notification initialized_notification(&mut service).await; - let uri = load_test_sway_file(SWAY_PROGRAM); + let (uri, sway_program) = load_sway_example(); // send "textDocument/didOpen" notification for `uri` - did_open_notification(&mut service, &uri, SWAY_PROGRAM).await; + did_open_notification(&mut service, &uri, &sway_program).await; // send "textDocument/didClose" notification for `uri` did_close_notification(&mut service).await; diff --git a/sway-parse/Cargo.toml b/sway-parse/Cargo.toml index 0c867c4489a..004735fb86a 100644 --- a/sway-parse/Cargo.toml +++ b/sway-parse/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sway-parse" -version = "0.16.1" +version = "0.16.2" authors = ["Fuel Labs "] edition = "2021" homepage = "https://fuel.network/" @@ -12,7 +12,7 @@ description = "Sway's parser" extension-trait = "1.0.1" num-bigint = "0.4.3" num-traits = "0.2.14" -sway-types = { version = "0.16.1", path = "../sway-types" } +phf = { version = "0.10.1", features = ["macros"] } +sway-types = { version = "0.16.2", path = "../sway-types" } thiserror = "1.0" unicode-xid = "0.2.2" - diff --git a/sway-parse/src/attribute.rs b/sway-parse/src/attribute.rs index d3851ece214..851e911cede 100644 --- a/sway-parse/src/attribute.rs +++ b/sway-parse/src/attribute.rs @@ -71,7 +71,12 @@ impl Spanned for Attribute { impl Parse for Attribute { fn parse(parser: &mut Parser) -> ParseResult { - let name = parser.parse()?; + let storage = parser.take::(); + let name = if let Some(storage) = storage { + Ident::from(storage) + } else { + parser.parse()? + }; let args = Parens::try_parse(parser)?; Ok(Attribute { name, args }) } diff --git a/sway-parse/src/error.rs b/sway-parse/src/error.rs index d356c777c18..07cf0d0d5f0 100644 --- a/sway-parse/src/error.rs +++ b/sway-parse/src/error.rs @@ -64,6 +64,10 @@ pub enum ParseErrorKind { UnexpectedTokenAfterAttribute, #[error("Identifiers cannot begin with a double underscore, as that naming convention is reserved for compiler intrinsics.")] InvalidDoubleUnderscore, + #[error("Unexpected rest token, must be at the end of pattern.")] + UnexpectedRestPattern, + #[error("Identifiers cannot be a reserved keyword.")] + ReservedKeywordIdentifier, } #[derive(Debug, Error, Clone, PartialEq, Hash)] diff --git a/sway-parse/src/expr/asm.rs b/sway-parse/src/expr/asm.rs index 46ac7692b19..0805d29ab2d 100644 --- a/sway-parse/src/expr/asm.rs +++ b/sway-parse/src/expr/asm.rs @@ -121,14 +121,13 @@ impl ParseToEnd for AsmBlockContents { impl Parse for AsmImmediate { fn parse(parser: &mut Parser) -> ParseResult { let ident = parser.parse::()?; - let digits = match ident.as_str().strip_prefix('i') { - Some(digits) => digits, - None => return Err(parser.emit_error(ParseErrorKind::MalformedAsmImmediate)), - }; - let parsed = match BigUint::from_str(digits).ok() { - Some(parsed) => parsed, - None => return Err(parser.emit_error(ParseErrorKind::MalformedAsmImmediate)), - }; + let digits = ident + .as_str() + .strip_prefix('i') + .ok_or_else(|| parser.emit_error(ParseErrorKind::MalformedAsmImmediate))?; + let parsed = BigUint::from_str(digits) + .ok() + .ok_or_else(|| parser.emit_error(ParseErrorKind::MalformedAsmImmediate))?; Ok(AsmImmediate { span: ident.span(), parsed, diff --git a/sway-parse/src/expr/mod.rs b/sway-parse/src/expr/mod.rs index 67b322c418d..38864df33bd 100644 --- a/sway-parse/src/expr/mod.rs +++ b/sway-parse/src/expr/mod.rs @@ -1056,14 +1056,14 @@ fn parse_atom(parser: &mut Parser, ctx: ParseExprCtx) -> ParseResult { ); } if parser.peek::().is_some() { - let ident = parser.parse::()?; + let ident = parser.parse::()?; return Ok(Expr::Literal(Literal::Bool(LitBool { span: ident.span(), kind: LitBoolType::True, }))); } if parser.peek::().is_some() { - let ident = parser.parse::()?; + let ident = parser.parse::()?; return Ok(Expr::Literal(Literal::Bool(LitBool { span: ident.span(), kind: LitBoolType::False, diff --git a/sway-parse/src/intrinsics.rs b/sway-parse/src/intrinsics.rs index 7271d8adcc1..ac2640fb455 100644 --- a/sway-parse/src/intrinsics.rs +++ b/sway-parse/src/intrinsics.rs @@ -1,9 +1,25 @@ -#[derive(Eq, PartialEq)] +use std::fmt; + +#[derive(Eq, PartialEq, Debug, Clone)] pub enum Intrinsic { GetStorageKey, IsReferenceType, - SizeOf, + SizeOfType, SizeOfVal, + Eq, +} + +impl fmt::Display for Intrinsic { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s = match self { + Intrinsic::GetStorageKey => "get_storage_key", + Intrinsic::IsReferenceType => "is_reference_type", + Intrinsic::SizeOfType => "size_of", + Intrinsic::SizeOfVal => "size_of_val", + Intrinsic::Eq => "eq", + }; + write!(f, "{}", s) + } } impl Intrinsic { @@ -12,8 +28,9 @@ impl Intrinsic { Some(match raw { "__get_storage_key" => GetStorageKey, "__is_reference_type" => IsReferenceType, - "__size_of" => SizeOf, + "__size_of" => SizeOfType, "__size_of_val" => SizeOfVal, + "__eq" => Eq, _ => return None, }) } diff --git a/sway-parse/src/item/item_use.rs b/sway-parse/src/item/item_use.rs index fe7262a184e..5b4e4629fc7 100644 --- a/sway-parse/src/item/item_use.rs +++ b/sway-parse/src/item/item_use.rs @@ -51,10 +51,9 @@ impl Parse for UseTree { if let Some(star_token) = parser.take() { return Ok(UseTree::Glob { star_token }); } - let name = match parser.take() { - Some(name) => name, - None => return Err(parser.emit_error(ParseErrorKind::ExpectedImportNameGroupOrGlob)), - }; + let name = parser + .take() + .ok_or_else(|| parser.emit_error(ParseErrorKind::ExpectedImportNameGroupOrGlob))?; if let Some(as_token) = parser.take() { let alias = parser.parse()?; return Ok(UseTree::Rename { diff --git a/sway-parse/src/keywords.rs b/sway-parse/src/keywords.rs index c3789febea2..9cafb2fba1d 100644 --- a/sway-parse/src/keywords.rs +++ b/sway-parse/src/keywords.rs @@ -13,6 +13,18 @@ macro_rules! define_keyword ( } } + impl $ty_name { + pub fn ident(&self) -> Ident { + Ident::new(self.span()) + } + } + + impl From<$ty_name> for Ident { + fn from(o: $ty_name) -> Ident { + o.ident() + } + } + impl Peek for $ty_name { fn peek(peeker: Peeker<'_>) -> Option<$ty_name> { let ident = peeker.peek_ident().ok()?; @@ -119,6 +131,42 @@ macro_rules! define_token ( }; ); +// Keep this in sync with the list above defined by define_keyword! +pub(crate) const RESERVED_KEYWORDS: phf::Set<&'static str> = phf::phf_set! { + "script", + "contract", + "predicate", + "library", + "dep", + "pub", + "use", + "as", + "struct", + "enum", + "self", + "fn", + "trait", + "impl", + "for", + "abi", + "const", + "storage", + "str", + "asm", + "return", + "if", + "else", + "match", + "mut", + "let", + "while", + "where", + "ref", + "deref", + "true", + "false", +}; + define_token!(SemicolonToken, "a semicolon", [Semicolon], []); define_token!( ForwardSlashToken, @@ -165,6 +213,7 @@ define_token!( [GreaterThan, Equals] ); define_token!(DotToken, "`.`", [Dot], []); +define_token!(DoubleDotToken, "`..`", [Dot, Dot], [Dot]); define_token!(BangToken, "`!`", [Bang], [Equals]); define_token!(PercentToken, "`%`", [Percent], []); define_token!(AddToken, "`+`", [Add], [Equals]); diff --git a/sway-parse/src/lib.rs b/sway-parse/src/lib.rs index c453b005d01..a5b71a2bef7 100644 --- a/sway-parse/src/lib.rs +++ b/sway-parse/src/lib.rs @@ -17,7 +17,7 @@ pub mod pattern; mod priv_prelude; pub mod punctuated; pub mod statement; -mod token; +pub mod token; pub mod ty; pub mod where_clause; @@ -55,8 +55,8 @@ pub use crate::{ path::{PathExpr, PathExprSegment, PathType, PathTypeSegment, QualifiedPathRoot}, pattern::{Pattern, PatternStructField}, statement::{Statement, StatementLet}, - token::lex, token::LexError, + token::{lex, lex_commented}, ty::Ty, where_clause::{WhereBound, WhereClause}, }; diff --git a/sway-parse/src/literal.rs b/sway-parse/src/literal.rs index 9e911887c17..efe76f4954b 100644 --- a/sway-parse/src/literal.rs +++ b/sway-parse/src/literal.rs @@ -68,10 +68,9 @@ impl Peek for Literal { impl Parse for Literal { fn parse(parser: &mut Parser) -> ParseResult { - match parser.take() { - Some(literal) => Ok(literal), - None => Err(parser.emit_error(ParseErrorKind::ExpectedLiteral)), - } + parser + .take() + .ok_or_else(|| parser.emit_error(ParseErrorKind::ExpectedLiteral)) } } diff --git a/sway-parse/src/parse.rs b/sway-parse/src/parse.rs index d91023aeb9a..c3aaa5c4bb6 100644 --- a/sway-parse/src/parse.rs +++ b/sway-parse/src/parse.rs @@ -97,6 +97,13 @@ impl Parse for Ident { )); } + if !ident.is_raw_ident() && RESERVED_KEYWORDS.contains(ident_str) { + return Err(parser.emit_error_with_span( + ParseErrorKind::ReservedKeywordIdentifier, + ident.span(), + )); + } + Ok(ident) } None => Err(parser.emit_error(ParseErrorKind::ExpectedIdent)), diff --git a/sway-parse/src/path.rs b/sway-parse/src/path.rs index 60e73d809c1..6038c5a9b4e 100644 --- a/sway-parse/src/path.rs +++ b/sway-parse/src/path.rs @@ -90,10 +90,24 @@ impl Parse for PathExpr { } } +fn parse_ident(parser: &mut Parser) -> ParseResult { + if parser.peek::().is_some() { + let token = parser.parse::()?; + let ident: Ident = Ident::from(token); + Ok(ident) + } else if parser.peek::().is_some() { + let token = parser.parse::()?; + let ident: Ident = Ident::from(token); + Ok(ident) + } else { + parser.parse::() + } +} + impl Parse for PathExprSegment { fn parse(parser: &mut Parser) -> ParseResult { let fully_qualified = parser.take(); - let name = parser.parse()?; + let name = parse_ident(parser)?; let generics_opt = if parser .peek2::() .is_some() @@ -198,7 +212,7 @@ impl Parse for PathType { impl Parse for PathTypeSegment { fn parse(parser: &mut Parser) -> ParseResult { let fully_qualified = parser.take(); - let name = parser.parse()?; + let name = parse_ident(parser)?; let generics_opt = if parser.peek::().is_some() { let generics = parser.parse()?; Some((None, generics)) diff --git a/sway-parse/src/pattern.rs b/sway-parse/src/pattern.rs index c7d2ef3814f..c5f3f1e6f29 100644 --- a/sway-parse/src/pattern.rs +++ b/sway-parse/src/pattern.rs @@ -47,14 +47,14 @@ impl Parse for Pattern { return Ok(Pattern::Var { mutable, name }); } if parser.peek::().is_some() { - let ident = parser.parse::()?; + let ident = parser.parse::()?; return Ok(Pattern::Literal(Literal::Bool(LitBool { span: ident.span(), kind: LitBoolType::True, }))); } if parser.peek::().is_some() { - let ident = parser.parse::()?; + let ident = parser.parse::()?; return Ok(Pattern::Literal(Literal::Bool(LitBool { span: ident.span(), kind: LitBoolType::False, @@ -75,6 +75,19 @@ impl Parse for Pattern { return Ok(Pattern::Constructor { path, args }); } if let Some(fields) = Braces::try_parse(parser)? { + let inner_fields: &Punctuated<_, _> = fields.get(); + let rest_pattern = inner_fields + .value_separator_pairs + .iter() + .find(|(p, _)| matches!(p, PatternStructField::Rest { token: _ })); + + if let Some((rest_pattern, _)) = rest_pattern { + return Err(parser.emit_error_with_span( + ParseErrorKind::UnexpectedRestPattern, + rest_pattern.span(), + )); + } + return Ok(Pattern::Struct { path, fields }); } match path.try_into_ident() { @@ -88,22 +101,39 @@ impl Parse for Pattern { } #[derive(Clone, Debug)] -pub struct PatternStructField { - pub field_name: Ident, - pub pattern_opt: Option<(ColonToken, Box)>, +pub enum PatternStructField { + Rest { + token: DoubleDotToken, + }, + Field { + field_name: Ident, + pattern_opt: Option<(ColonToken, Box)>, + }, } impl Spanned for PatternStructField { fn span(&self) -> Span { - match &self.pattern_opt { - Some((_colon_token, pattern)) => Span::join(self.field_name.span(), pattern.span()), - None => self.field_name.span(), + use PatternStructField::*; + match &self { + Rest { token } => token.span(), + Field { + field_name, + pattern_opt, + } => match pattern_opt { + Some((_colon_token, pattern)) => Span::join(field_name.span(), pattern.span()), + None => field_name.span(), + }, } } } impl Parse for PatternStructField { fn parse(parser: &mut Parser) -> ParseResult { + if parser.peek::().is_some() { + let token = parser.parse()?; + return Ok(PatternStructField::Rest { token }); + } + let field_name = parser.parse()?; let pattern_opt = match parser.take() { Some(colon_token) => { @@ -112,7 +142,7 @@ impl Parse for PatternStructField { } None => None, }; - Ok(PatternStructField { + Ok(PatternStructField::Field { field_name, pattern_opt, }) diff --git a/sway-parse/src/token.rs b/sway-parse/src/token.rs index 972ce4dd4f8..3b63a750126 100644 --- a/sway-parse/src/token.rs +++ b/sway-parse/src/token.rs @@ -69,13 +69,16 @@ impl PunctKind { } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)] -pub struct Group { +pub struct GenericGroup { pub delimiter: Delimiter, - pub token_stream: TokenStream, + pub token_stream: T, pub span: Span, } -impl Spanned for Group { +pub type Group = GenericGroup; +pub type CommentedGroup = GenericGroup; + +impl Spanned for GenericGroup { fn span(&self) -> Span { self.span.clone() } @@ -96,33 +99,122 @@ impl Delimiter { Delimiter::Bracket => '[', } } + pub fn as_close_char(self) -> char { + match self { + Delimiter::Parenthesis => ')', + Delimiter::Brace => '}', + Delimiter::Bracket => ']', + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)] +pub struct Comment { + pub span: Span, +} + +impl Spanned for Comment { + fn span(&self) -> Span { + self.span.clone() + } } +/// Allows for generalizing over commented and uncommented token streams. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)] -pub enum TokenTree { +pub enum GenericTokenTree { Punct(Punct), Ident(Ident), - Group(Group), + Group(GenericGroup), Literal(Literal), } -impl Spanned for TokenTree { +pub type TokenTree = GenericTokenTree; +pub type CommentedTree = GenericTokenTree; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)] +pub enum CommentedTokenTree { + Comment(Comment), + Tree(CommentedTree), +} + +impl CommentedGroup { + pub fn strip_comments(self) -> Group { + Group { + delimiter: self.delimiter, + token_stream: self.token_stream.strip_comments(), + span: self.span, + } + } +} + +impl Spanned for GenericTokenTree { + fn span(&self) -> Span { + match self { + Self::Punct(punct) => punct.span(), + Self::Ident(ident) => ident.span(), + Self::Group(group) => group.span(), + Self::Literal(literal) => literal.span(), + } + } +} + +impl Spanned for CommentedTokenTree { fn span(&self) -> Span { match self { - TokenTree::Punct(punct) => punct.span(), - TokenTree::Ident(ident) => ident.span(), - TokenTree::Group(group) => group.span(), - TokenTree::Literal(literal) => literal.span(), + Self::Comment(cmt) => cmt.span(), + Self::Tree(tt) => tt.span(), } } } +impl From for GenericTokenTree { + fn from(punct: Punct) -> Self { + Self::Punct(punct) + } +} + +impl From for GenericTokenTree { + fn from(ident: Ident) -> Self { + Self::Ident(ident) + } +} + +impl From> for GenericTokenTree { + fn from(group: GenericGroup) -> Self { + Self::Group(group) + } +} + +impl From for GenericTokenTree { + fn from(lit: Literal) -> Self { + Self::Literal(lit) + } +} + +impl From for CommentedTokenTree { + fn from(comment: Comment) -> Self { + Self::Comment(comment) + } +} + +impl From for CommentedTokenTree { + fn from(tree: CommentedTree) -> Self { + Self::Tree(tree) + } +} + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)] pub struct TokenStream { token_trees: Vec, full_span: Span, } +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Hash)] +pub struct CommentedTokenStream { + token_trees: Vec, + full_span: Span, +} + #[derive(Error, Debug, Clone, PartialEq, Eq, PartialOrd, Hash)] #[error("{}", kind)] pub struct LexError { @@ -270,14 +362,23 @@ pub fn lex( end: usize, path: Option>, ) -> Result { + lex_commented(src, start, end, path).map(|stream| stream.strip_comments()) +} + +pub fn lex_commented( + src: &Arc, + start: usize, + end: usize, + path: Option>, +) -> Result { let mut char_indices = CharIndicesInner { src: &src[..end], position: start, } .peekable(); let mut parent_token_trees = Vec::new(); - let mut token_trees = Vec::new(); - while let Some((index, character)) = char_indices.next() { + let mut token_trees: Vec = Vec::new(); + while let Some((mut index, mut character)) = char_indices.next() { if character.is_whitespace() { continue; } @@ -285,8 +386,11 @@ pub fn lex( match char_indices.peek() { Some((_, '/')) => { let _ = char_indices.next(); - for (_, character) in char_indices.by_ref() { + for (end, character) in char_indices.by_ref() { if character == '\n' { + let span = Span::new(src.clone(), index, end, path.clone()).unwrap(); + let comment = Comment { span }; + token_trees.push(comment.into()); break; } } @@ -294,42 +398,33 @@ pub fn lex( Some((_, '*')) => { let _ = char_indices.next(); let mut unclosed_indices = vec![index]; + + let unclosed_multiline_comment = |unclosed_indices: Vec<_>| { + let span = Span::new( + src.clone(), + *unclosed_indices.last().unwrap(), + src.len(), + path.clone(), + ) + .unwrap(); + LexError { + kind: LexErrorKind::UnclosedMultilineComment { unclosed_indices }, + span, + } + }; + loop { match char_indices.next() { - None => { - let span = Span::new( - src.clone(), - *unclosed_indices.last().unwrap(), - src.len(), - path.clone(), - ) - .unwrap(); - return Err(LexError { - kind: LexErrorKind::UnclosedMultilineComment { - unclosed_indices, - }, - span, - }); - } + None => return Err(unclosed_multiline_comment(unclosed_indices)), Some((_, '*')) => match char_indices.next() { - None => { - let span = Span::new( - src.clone(), - *unclosed_indices.last().unwrap(), - src.len(), - path.clone(), - ) - .unwrap(); - return Err(LexError { - kind: LexErrorKind::UnclosedMultilineComment { - unclosed_indices, - }, - span, - }); - } - Some((_, '/')) => { + None => return Err(unclosed_multiline_comment(unclosed_indices)), + Some((end, '/')) => { let _ = char_indices.next(); - unclosed_indices.pop(); + let start = unclosed_indices.pop().unwrap(); + let span = + Span::new(src.clone(), start, end, path.clone()).unwrap(); + let comment = Comment { span }; + token_trees.push(comment.into()); if unclosed_indices.is_empty() { break; } @@ -337,21 +432,7 @@ pub fn lex( Some((_, _)) => (), }, Some((next_index, '/')) => match char_indices.next() { - None => { - let span = Span::new( - src.clone(), - *unclosed_indices.last().unwrap(), - src.len(), - path.clone(), - ) - .unwrap(); - return Err(LexError { - kind: LexErrorKind::UnclosedMultilineComment { - unclosed_indices, - }, - span, - }); - } + None => return Err(unclosed_multiline_comment(unclosed_indices)), Some((_, '*')) => { unclosed_indices.push(next_index); } @@ -373,7 +454,7 @@ pub fn lex( spacing, span, }; - token_trees.push(TokenTree::Punct(punct)); + token_trees.push(CommentedTokenTree::Tree(punct.into())); } None => { let span = Span::new(src.clone(), start, end, path.clone()).unwrap(); @@ -382,12 +463,22 @@ pub fn lex( spacing: Spacing::Alone, span, }; - token_trees.push(TokenTree::Punct(punct)); + token_trees.push(CommentedTokenTree::Tree(punct.into())); } } continue; } if character.is_xid_start() || character == '_' { + let is_raw_ident = character == 'r' && matches!(char_indices.peek(), Some((_, '#'))); + if is_raw_ident { + char_indices.next(); + if let Some((_, next_character)) = char_indices.peek() { + character = *next_character; + if let Some((next_index, _)) = char_indices.next() { + index = next_index; + } + } + } let is_single_underscore = character == '_' && match char_indices.peek() { Some((_, next_character)) => !next_character.is_xid_continue(), @@ -401,8 +492,8 @@ pub fn lex( let _ = char_indices.next(); } let span = span_until(src, index, &mut char_indices, &path); - let ident = Ident::new(span); - token_trees.push(TokenTree::Ident(ident)); + let ident = Ident::new_with_raw(span, is_raw_ident); + token_trees.push(CommentedTokenTree::Tree(ident.into())); continue; } } @@ -450,15 +541,15 @@ pub fn lex( let start_index = open_index + open_delimiter.as_open_char().len_utf8(); let full_span = Span::new(src.clone(), start_index, index, path.clone()).unwrap(); - let group = Group { - token_stream: TokenStream { + let group = CommentedGroup { + token_stream: CommentedTokenStream { token_trees: parent, full_span, }, delimiter: close_delimiter, span: span_until(src, open_index, &mut char_indices, &path), }; - token_trees.push(TokenTree::Group(group)); + token_trees.push(CommentedTokenTree::Tree(group.into())); } } continue; @@ -506,7 +597,7 @@ pub fn lex( } let span = span_until(src, index, &mut char_indices, &path); let literal = Literal::String(LitString { span, parsed }); - token_trees.push(TokenTree::Literal(literal)); + token_trees.push(CommentedTokenTree::Tree(literal.into())); continue; } if character == '\'' { @@ -558,7 +649,7 @@ pub fn lex( } let span = span_until(src, index, &mut char_indices, &path); let literal = Literal::Char(LitChar { span, parsed }); - token_trees.push(TokenTree::Literal(literal)); + token_trees.push(CommentedTokenTree::Tree(literal.into())); continue; } if let Some(digit) = character.to_digit(10) { @@ -738,7 +829,7 @@ pub fn lex( parsed: big_uint, ty_opt, }); - token_trees.push(TokenTree::Literal(literal)); + token_trees.push(CommentedTokenTree::Tree(literal.into())); continue; } if let Some(kind) = character.as_punct_kind() { @@ -754,7 +845,7 @@ pub fn lex( spacing, span, }; - token_trees.push(TokenTree::Punct(punct)); + token_trees.push(CommentedTokenTree::Tree(punct.into())); continue; } return Err(LexError { @@ -787,7 +878,7 @@ pub fn lex( }); } let full_span = Span::new(src.clone(), start, end, path).unwrap(); - let token_stream = TokenStream { + let token_stream = CommentedTokenStream { token_trees, full_span, }; @@ -966,3 +1057,91 @@ impl Spanned for TokenStream { self.full_span.clone() } } + +impl CommentedTokenTree { + pub fn strip_comments(self) -> Option { + let commented_tt = match self { + Self::Comment(_) => return None, + Self::Tree(commented_tt) => commented_tt, + }; + let tt = match commented_tt { + CommentedTree::Punct(punct) => punct.into(), + CommentedTree::Ident(ident) => ident.into(), + CommentedTree::Group(group) => group.strip_comments().into(), + CommentedTree::Literal(lit) => lit.into(), + }; + Some(tt) + } +} + +impl CommentedTokenStream { + pub fn token_trees(&self) -> &[CommentedTokenTree] { + &self.token_trees + } + + pub fn strip_comments(self) -> TokenStream { + let token_trees = self + .token_trees + .into_iter() + .filter_map(|tree| tree.strip_comments()) + .collect(); + TokenStream { + token_trees, + full_span: self.full_span, + } + } +} + +impl Spanned for CommentedTokenStream { + fn span(&self) -> Span { + self.full_span.clone() + } +} + +#[cfg(test)] +mod tests { + use super::{lex_commented, CommentedTokenTree, CommentedTree}; + use crate::priv_prelude::*; + use std::sync::Arc; + + #[test] + fn lex_commented_token_stream() { + let input = r#" + // Single-line comment. + struct Foo { + /* multi- + * line- + * comment */ + bar: i32, + } + "#; + let start = 0; + let end = input.len(); + let path = None; + let stream = lex_commented(&Arc::from(input), start, end, path).unwrap(); + let mut tts = stream.token_trees().iter(); + assert_eq!( + tts.next().unwrap().span().as_str(), + "// Single-line comment." + ); + assert_eq!(tts.next().unwrap().span().as_str(), "struct"); + assert_eq!(tts.next().unwrap().span().as_str(), "Foo"); + { + let group = match tts.next() { + Some(CommentedTokenTree::Tree(CommentedTree::Group(group))) => group, + _ => panic!("expected group"), + }; + let mut tts = group.token_stream.token_trees().iter(); + assert_eq!( + tts.next().unwrap().span().as_str(), + "/* multi-\n * line-\n * comment *", + ); + assert_eq!(tts.next().unwrap().span().as_str(), "bar"); + assert_eq!(tts.next().unwrap().span().as_str(), ":"); + assert_eq!(tts.next().unwrap().span().as_str(), "i32"); + assert_eq!(tts.next().unwrap().span().as_str(), ","); + assert!(tts.next().is_none()); + } + assert!(tts.next().is_none()); + } +} diff --git a/sway-types/Cargo.toml b/sway-types/Cargo.toml index 7ad4ed637da..005fa01ad9c 100644 --- a/sway-types/Cargo.toml +++ b/sway-types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sway-types" -version = "0.16.1" +version = "0.16.2" authors = ["Fuel Labs "] edition = "2021" homepage = "https://fuel.network/" @@ -11,6 +11,6 @@ description = "Sway core types." [dependencies] fuel-asm = "0.5" fuel-crypto = "0.5" -fuel-tx = "0.12" +fuel-tx = "0.13" lazy_static = "1.4" serde = { version = "1.0", features = ["derive"] } diff --git a/sway-types/src/ident.rs b/sway-types/src/ident.rs index fda2a7c466e..c0382ab21ac 100644 --- a/sway-types/src/ident.rs +++ b/sway-types/src/ident.rs @@ -11,6 +11,7 @@ use std::{ pub struct Ident { name_override_opt: Option<&'static str>, span: Span, + is_raw_ident: bool, } // custom implementation of Hash so that namespacing isn't reliant on the span itself, which will @@ -61,11 +62,25 @@ impl Ident { } } + pub fn is_raw_ident(&self) -> bool { + self.is_raw_ident + } + pub fn new(span: Span) -> Ident { let span = span.trim(); Ident { name_override_opt: None, span, + is_raw_ident: false, + } + } + + pub fn new_with_raw(span: Span, is_raw_ident: bool) -> Ident { + let span = span.trim(); + Ident { + name_override_opt: None, + span, + is_raw_ident, } } @@ -73,6 +88,7 @@ impl Ident { Ident { name_override_opt: Some(name_override), span, + is_raw_ident: false, } } @@ -80,6 +96,7 @@ impl Ident { Ident { name_override_opt: Some(name), span: Span::dummy(), + is_raw_ident: false, } } } diff --git a/sway-types/src/span.rs b/sway-types/src/span.rs index 6ac02a066f2..bae29f8a5fb 100644 --- a/sway-types/src/span.rs +++ b/sway-types/src/span.rs @@ -96,6 +96,11 @@ impl Span { }) } + pub fn from_string(source: String) -> Span { + let len = source.len(); + Span::new(Arc::from(source), 0, len, None).unwrap() + } + pub fn src(&self) -> &Arc { &self.src } @@ -156,12 +161,6 @@ impl Span { /// This panics if the spans are not from the same file. This should /// only be used on spans that are actually next to each other. pub fn join(s1: Span, s2: Span) -> Span { - // FIXME(canndrew): This is horrifying. Where did it come from and why is it needed? - // FIXME(sezna): I don't know, but I'm on the blame for it. Ah. We should remove this. - if s1.as_str() == "core" { - return s2; - } - assert!( Arc::ptr_eq(&s1.src, &s2.src) && s1.path == s2.path, "Spans from different files cannot be joined.", diff --git a/sway-utils/Cargo.toml b/sway-utils/Cargo.toml index 7db1685c9db..9571d9e5a07 100644 --- a/sway-utils/Cargo.toml +++ b/sway-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sway-utils" -version = "0.16.1" +version = "0.16.2" authors = ["Fuel Labs "] edition = "2021" homepage = "https://fuel.network/" diff --git a/test-sig-gen-util/Cargo.toml b/test-sig-gen-util/Cargo.toml index c8a176f8bea..677818e1ae1 100644 --- a/test-sig-gen-util/Cargo.toml +++ b/test-sig-gen-util/Cargo.toml @@ -7,9 +7,9 @@ publish = false [dependencies] anyhow = "1.0.39" fuel-crypto = "0.5" -fuel-tx = "0.12" +fuel-tx = "0.13" fuel-types = { version = "0.5", default-features = false } -fuel-vm = "0.11" +fuel-vm = "0.12" hex = "0.4" secp256k1 = { version = "0.20", features = ["recovery"] } sha3 = "0.10" diff --git a/test/Cargo.toml b/test/Cargo.toml index 190d3e68921..f6758a9b495 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -7,14 +7,17 @@ publish = false [dependencies] anyhow = "1.0.41" -assert_matches = "1.5.0" +filecheck = "0.5" forc = { path = "../forc", features = ["test"], default-features = false } +forc-pkg = { path = "../forc-pkg" } forc-util = { path = "../forc-util" } fuel-asm = "0.5" -fuel-tx = "0.12" -fuel-vm = { version = "0.11", features = ["random"] } +fuel-tx = "0.13" +fuel-vm = { version = "0.12", features = ["random"] } +gag = "1.0" rand = "0.8" regex = "1" serde_json = "1.0.73" tokio = "1.12" +toml = "0.5" tracing = "0.1" diff --git a/test/src/e2e_vm_tests/harness.rs b/test/src/e2e_vm_tests/harness.rs index 023d89105a9..c1e5e83fc5d 100644 --- a/test/src/e2e_vm_tests/harness.rs +++ b/test/src/e2e_vm_tests/harness.rs @@ -1,12 +1,9 @@ use anyhow::{bail, Result}; -use forc::test::{ - forc_abi_json, forc_build, forc_deploy, forc_run, BuildCommand, DeployCommand, JsonAbiCommand, - RunCommand, -}; +use forc::test::{forc_build, forc_deploy, forc_run, BuildCommand, DeployCommand, RunCommand}; +use forc_pkg::Compiled; use fuel_tx::Transaction; use fuel_vm::interpreter::Interpreter; use fuel_vm::prelude::*; -use serde_json::Value; use std::fs; pub(crate) fn deploy_contract(file_name: &str, locked: bool) -> ContractId { @@ -67,12 +64,12 @@ pub(crate) fn runs_on_node( /// Very basic check that code does indeed run in the VM. /// `true` if it does, `false` if not. -pub(crate) fn runs_in_vm(file_name: &str, locked: bool) -> ProgramState { +pub(crate) fn runs_in_vm(file_name: &str, locked: bool) -> (ProgramState, Compiled) { let storage = MemoryStorage::default(); let script = compile_to_bytes(file_name, locked).unwrap(); let gas_price = 10; - let gas_limit = fuel_tx::default_parameters::MAX_GAS_PER_TX; + let gas_limit = fuel_tx::ConsensusParameters::DEFAULT.max_gas_per_tx; let byte_price = 0; let maturity = 0; let script_data = vec![]; @@ -84,7 +81,7 @@ pub(crate) fn runs_in_vm(file_name: &str, locked: bool) -> ProgramState { gas_limit, byte_price, maturity, - script, + script.bytecode.clone(), script_data, inputs, outputs, @@ -95,25 +92,54 @@ pub(crate) fn runs_in_vm(file_name: &str, locked: bool) -> ProgramState { .validate(block_height, &Default::default()) .unwrap(); let mut i = Interpreter::with_storage(storage, Default::default()); - *i.transact(tx_to_test).unwrap().state() + (*i.transact(tx_to_test).unwrap().state(), script) } -/// Panics if code _does_ compile, used for test cases where the source -/// code should have been rejected by the compiler. -pub(crate) fn does_not_compile(file_name: &str, locked: bool) { - assert!( - compile_to_bytes(file_name, locked).is_err(), - "{} should not have compiled.", - file_name, - ) +/// Returns Err(()) if code _does_ compile, used for test cases where the source +/// code should have been rejected by the compiler. When it fails to compile the +/// captured stdout is returned. +pub(crate) fn does_not_compile(file_name: &str, locked: bool) -> Result { + use std::io::Read; + + tracing::info!(" Compiling {}", file_name); + + // Capture stdout to a buffer, compile the test and save stdout to a string. + let mut buf = gag::BufferRedirect::stdout().unwrap(); + let result = compile_to_bytes_verbose(file_name, locked, true); + let mut output = String::new(); + buf.read_to_string(&mut output).unwrap(); + drop(buf); + + // If verbosity is requested then print it out. + if get_test_config_from_env() { + tracing::info!("{output}"); + } + + // Invert the result; if it succeeds then return an Err. + match result { + Ok(_) => Err(()), + Err(e) => { + // Capture the result of the compilation (i.e., any errors Forc produces) and append to + // the stdout from the compiler. + output.push_str(&format!("\n{e}")); + Ok(output) + } + } } /// Returns `true` if a file compiled without any errors or warnings, /// and `false` if it did not. -pub(crate) fn compile_to_bytes(file_name: &str, locked: bool) -> Result> { +pub(crate) fn compile_to_bytes(file_name: &str, locked: bool) -> Result { + compile_to_bytes_verbose(file_name, locked, get_test_config_from_env()) +} + +pub(crate) fn compile_to_bytes_verbose( + file_name: &str, + locked: bool, + verbose: bool, +) -> Result { tracing::info!(" Compiling {}", file_name); let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let verbose = get_test_config_from_env(); forc_build::build(BuildCommand { path: Some(format!( "{}/src/e2e_vm_tests/test_programs/{}", @@ -123,11 +149,10 @@ pub(crate) fn compile_to_bytes(file_name: &str, locked: bool) -> Result> silent_mode: !verbose, ..Default::default() }) - .map(|compiled| compiled.bytecode) } -pub(crate) fn test_json_abi(file_name: &str) -> Result<()> { - let _compiled_res = compile_to_json_abi(file_name)?; +pub(crate) fn test_json_abi(file_name: &str, compiled: &Compiled) -> Result<()> { + emit_json_abi(file_name, compiled)?; let manifest_dir = env!("CARGO_MANIFEST_DIR"); let oracle_path = format!( "{}/src/e2e_vm_tests/test_programs/{}/{}", @@ -153,21 +178,59 @@ pub(crate) fn test_json_abi(file_name: &str) -> Result<()> { Ok(()) } -fn compile_to_json_abi(file_name: &str) -> Result { +fn emit_json_abi(file_name: &str, compiled: &Compiled) -> Result<()> { tracing::info!(" ABI gen {}", file_name); + let json_abi = serde_json::json!(compiled.json_abi); let manifest_dir = env!("CARGO_MANIFEST_DIR"); - forc_abi_json::build(JsonAbiCommand { - path: Some(format!( - "{}/src/e2e_vm_tests/test_programs/{}", - manifest_dir, file_name - )), - json_outfile: Some(format!( - "{}/src/e2e_vm_tests/test_programs/{}/{}", - manifest_dir, file_name, "json_abi_output.json" - )), - silent_mode: true, - ..Default::default() - }) + let file = std::fs::File::create(format!( + "{}/src/e2e_vm_tests/test_programs/{}/{}", + manifest_dir, file_name, "json_abi_output.json" + )) + .map_err(|e| e)?; + let res = serde_json::to_writer_pretty(&file, &json_abi); + res.map_err(|e| e)?; + Ok(()) +} + +pub(crate) fn test_json_storage_slots(file_name: &str, compiled: &Compiled) -> Result<()> { + emit_json_storage_slots(file_name, compiled)?; + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let oracle_path = format!( + "{}/src/e2e_vm_tests/test_programs/{}/{}", + manifest_dir, file_name, "json_storage_slots_oracle.json" + ); + let output_path = format!( + "{}/src/e2e_vm_tests/test_programs/{}/{}", + manifest_dir, file_name, "json_storage_slots_output.json" + ); + if fs::metadata(oracle_path.clone()).is_err() { + bail!("JSON storage slots oracle file does not exist for this test."); + } + if fs::metadata(output_path.clone()).is_err() { + bail!("JSON storage slots output file does not exist for this test."); + } + let oracle_contents = + fs::read_to_string(oracle_path).expect("Something went wrong reading the file."); + let output_contents = + fs::read_to_string(output_path).expect("Something went wrong reading the file."); + if oracle_contents != output_contents { + bail!("Mismatched storage slots JSON output."); + } + Ok(()) +} + +fn emit_json_storage_slots(file_name: &str, compiled: &Compiled) -> Result<()> { + tracing::info!(" storage slots JSON gen {}", file_name); + let json_storage_slots = serde_json::json!(compiled.storage_slots); + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let file = std::fs::File::create(format!( + "{}/src/e2e_vm_tests/test_programs/{}/{}", + manifest_dir, file_name, "json_storage_slots_output.json" + )) + .map_err(|e| e)?; + let res = serde_json::to_writer_pretty(&file, &json_storage_slots); + res.map_err(|e| e)?; + Ok(()) } fn get_test_config_from_env() -> bool { diff --git a/test/src/e2e_vm_tests/mod.rs b/test/src/e2e_vm_tests/mod.rs index b528ad411cb..4df29596d7d 100644 --- a/test/src/e2e_vm_tests/mod.rs +++ b/test/src/e2e_vm_tests/mod.rs @@ -1,730 +1,396 @@ +// Please take a look in test_programs/README.md for details on how these tests work. + mod harness; -use assert_matches::assert_matches; + use forc_util::init_tracing_subscriber; use fuel_vm::prelude::*; + +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; + +#[derive(PartialEq)] +enum TestCategory { + Compiles, + FailsToCompile, + Runs, + RunsWithContract, + Disabled, +} + +#[derive(Debug)] +enum TestResult { + Result(Word), + Return(u64), + ReturnData(Bytes32), + Revert(u64), +} + +struct TestDescription { + name: String, + category: TestCategory, + expected_result: Option, + contract_paths: Vec, + validate_abi: bool, + validate_storage_slots: bool, + checker: filecheck::Checker, +} + pub fn run(locked: bool, filter_regex: Option) { init_tracing_subscriber(); - let filter = |name| { - filter_regex + + let configured_tests = discover_test_configs().unwrap_or_else(|e| { + panic!("Discovering tests {e}"); + }); + + let total_number_of_tests = configured_tests.len(); + let mut number_of_tests_executed = 0; + let mut number_of_disabled_tests = 0; + + let mut deployed_contracts = HashMap::::new(); + + for TestDescription { + name, + category, + expected_result, + contract_paths, + validate_abi, + validate_storage_slots, + checker, + } in configured_tests + { + if !filter_regex .as_ref() - .map(|regex| regex.is_match(name)) + .map(|regex| regex.is_match(&name)) .unwrap_or(true) - }; + { + continue; + } - // Predicate programs that should successfully compile and terminate - // with some known state. Note that if you are adding a non-predicate, it may pass by mistake. - // Please add non-predicates to `positive_project_names_with_abi`. - let positive_project_names_no_abi = vec![( - "should_pass/language/basic_predicate", - ProgramState::Return(1), - )]; - - let mut number_of_tests_run = - positive_project_names_no_abi - .iter() - .fold(0, |acc, (name, res)| { - if filter(name) { - assert_eq!(crate::e2e_vm_tests::harness::runs_in_vm(name, locked), *res); - acc + 1 - } else { - acc + match category { + TestCategory::Runs => { + let res = match expected_result { + Some(TestResult::Return(v)) => ProgramState::Return(v), + Some(TestResult::ReturnData(bytes)) => ProgramState::ReturnData(bytes), + Some(TestResult::Revert(v)) => ProgramState::Revert(v), + + _ => panic!( + "For {name}:\n\ + Invalid expected result for a 'runs' test: {expected_result:?}." + ), + }; + + let result = crate::e2e_vm_tests::harness::runs_in_vm(&name, locked); + assert_eq!(result.0, res); + if validate_abi { + assert!(crate::e2e_vm_tests::harness::test_json_abi(&name, &result.1).is_ok()); } - }); - - // Programs that should successfully compile, include abi and terminate - // with some known state. Note that if a predicate is included - // it will be rejected during assertion. Please move it to - // `positive_project_names_no_abi` above. - let positive_project_names_with_abi = vec![ - ( - "should_pass/forc/dependency_package_field", - ProgramState::Return(0), - ), - ( - "should_pass/language/asm_expr_basic", - ProgramState::Return(6), - ), - ( - "should_pass/language/basic_func_decl", - ProgramState::Return(1), // 1 == true - ), - ( - "should_pass/language/builtin_type_method_call", - ProgramState::Return(3), - ), - ("should_pass/language/dependencies", ProgramState::Return(0)), // 0 == false - ( - "should_pass/language/if_elseif_enum", - ProgramState::Return(10), - ), - ( - "should_pass/language/tuple_types", - ProgramState::Return(123), - ), - ( - "should_pass/language/out_of_order_decl", - ProgramState::Return(1), - ), - ( - "should_pass/language/struct_field_reassignment", - ProgramState::Return(0), - ), - ( - "should_pass/language/tuple_field_reassignment", - ProgramState::Return(320), - ), - ( - "should_pass/language/enum_in_fn_decl", - ProgramState::Return(255), - ), - ( - "should_pass/language/main_returns_unit", - ProgramState::Return(0), - ), - ( - "should_pass/language/unary_not_basic", - ProgramState::Return(1), // 1 == true - ), - ( - "should_pass/language/unary_not_basic_2", - ProgramState::Return(1), // 1 == true - ), - ( - "should_pass/language/fix_opcode_bug", - ProgramState::Return(30), - ), - ( - "should_pass/language/retd_b256", - ProgramState::ReturnData(Bytes32::from([ - 102, 104, 122, 173, 248, 98, 189, 119, 108, 143, 193, 139, 142, 159, 142, 32, 8, - 151, 20, 133, 110, 226, 51, 179, 144, 42, 89, 29, 13, 95, 41, 37, - ])), - ), - ( - "should_pass/language/retd_struct", - ProgramState::ReturnData(Bytes32::from([ - 251, 57, 24, 241, 63, 94, 17, 102, 252, 182, 8, 110, 140, 105, 102, 105, 138, 202, - 155, 39, 97, 32, 94, 129, 141, 144, 190, 142, 33, 32, 33, 75, - ])), - ), - ( - "should_pass/language/op_precedence", - ProgramState::Return(0), - ), - ( - "should_pass/language/asm_without_return", - ProgramState::Return(0), - ), - ( - "should_pass/language/b256_bad_jumps", - ProgramState::Return(1), - ), - ( - "should_pass/language/b256_bitwise_ops", - ProgramState::Return(1), - ), - ("should_pass/language/b256_ops", ProgramState::Return(100)), - ( - "should_pass/language/struct_field_access", - ProgramState::Return(43), - ), - ("should_pass/language/bool_and_or", ProgramState::Return(42)), - ("should_pass/language/eq_and_neq", ProgramState::Return(1)), - ( - "should_pass/language/local_impl_for_ord", - ProgramState::Return(1), // true - ), - ("should_pass/language/const_decl", ProgramState::Return(100)), - ( - "should_pass/language/const_decl_in_library", - ProgramState::Return(1), // true - ), - ( - "should_pass/language/aliased_imports", - ProgramState::Return(42), - ), - ( - "should_pass/language/empty_method_initializer", - ProgramState::Return(1), // true - ), - ( - "should_pass/stdlib/b512_struct_alignment", - ProgramState::Return(1), // true - ), - ( - "should_pass/stdlib/contract_id_type", - ProgramState::Return(1), - ), // true - ("should_pass/stdlib/evm_ecr", ProgramState::Return(1)), // true - ( - "should_pass/stdlib/exponentiation_test", - ProgramState::Return(1), - ), // true - ("should_pass/stdlib/ge_test", ProgramState::Return(1)), // true - ("should_pass/stdlib/identity_eq", ProgramState::Return(1)), // true - ("should_pass/stdlib/intrinsics", ProgramState::Return(1)), // true - ("should_pass/stdlib/option", ProgramState::Return(1)), // true - ("should_pass/stdlib/require", ProgramState::Return(1)), // true - ("should_pass/stdlib/result", ProgramState::Return(1)), // true - ("should_pass/stdlib/u128_test", ProgramState::Return(1)), // true - ("should_pass/stdlib/u128_div_test", ProgramState::Return(1)), // true - ("should_pass/stdlib/u128_mul_test", ProgramState::Return(1)), // true - ("should_pass/stdlib/alloc", ProgramState::Return(1)), // true - ("should_pass/stdlib/mem", ProgramState::Return(1)), // true - ( - "should_pass/language/generic_structs", - ProgramState::Return(1), // true - ), - ( - "should_pass/language/generic_functions", - ProgramState::Return(1), // true - ), - ("should_pass/language/generic_enum", ProgramState::Return(1)), // true - ( - "should_pass/language/numeric_constants", - ProgramState::Return(1), - ), // true - ("should_pass/language/u64_ops", ProgramState::Return(1)), // true - ( - "should_pass/language/import_method_from_other_file", - ProgramState::Return(10), // true - ), - ( - "should_pass/stdlib/ec_recover_test", - ProgramState::Return(1), // true - ), - ("should_pass/stdlib/address_test", ProgramState::Return(1)), // true - ( - "should_pass/language/generic_struct", - ProgramState::Return(1), // true - ), - ( - "should_pass/language/zero_field_types", - ProgramState::Return(10), // true - ), - ("should_pass/stdlib/assert_test", ProgramState::Return(1)), // true - ( - "should_pass/language/match_expressions", - ProgramState::Return(42), - ), - ("should_pass/language/array_basics", ProgramState::Return(1)), // true - // Disabled, pending decision on runtime OOB checks. ("array_dynamic_oob", ProgramState::Revert(1)), - ( - "should_pass/language/abort_control_flow_good", - ProgramState::Revert(42), - ), - ( - "should_pass/language/array_generics", - ProgramState::Return(1), // true - ), - ( - "should_pass/language/match_expressions_structs", - ProgramState::Return(4), - ), - ("should_pass/stdlib/b512_test", ProgramState::Return(1)), // true - ("should_pass/stdlib/block_height", ProgramState::Return(1)), // true - ("should_pass/stdlib/vec", ProgramState::Return(1)), // true - ( - "should_pass/language/trait_override_bug", - ProgramState::Return(7), - ), - ( - "should_pass/language/if_implicit_unit", - ProgramState::Return(0), - ), - ( - "should_pass/language/modulo_uint_test", - ProgramState::Return(1), // true - ), - ( - "should_pass/language/trait_import_with_star", - ProgramState::Return(0), - ), - ( - "should_pass/language/tuple_desugaring", - ProgramState::Return(9), - ), - ( - "should_pass/language/multi_item_import", - ProgramState::Return(0), // false - ), - ( - "should_pass/language/use_full_path_names", - ProgramState::Return(1), - ), - ( - "should_pass/language/tuple_indexing", - ProgramState::Return(1), - ), - ( - "should_pass/language/tuple_access", - ProgramState::Return(42), - ), - ( - "should_pass/language/funcs_with_generic_types", - ProgramState::Return(1), // true - ), - ( - "should_pass/language/enum_if_let", - ProgramState::Return(143), - ), - ( - "should_pass/language/enum_destructuring", - ProgramState::Return(15), - ), - ( - "should_pass/language/enum_if_let_large_type", - ProgramState::Return(15), - ), - ( - "should_pass/language/enum_type_inference", - ProgramState::Return(5), - ), - ("should_pass/language/size_of", ProgramState::Return(1)), - ("should_pass/language/supertraits", ProgramState::Return(1)), - ( - "should_pass/language/new_allocator_test", - ProgramState::Return(42), // true - ), - ( - "should_pass/language/chained_if_let", - ProgramState::Return(5), // true - ), - ( - "should_pass/language/inline_if_expr_const", - ProgramState::Return(0), - ), - ( - "should_pass/language/method_on_empty_struct", - ProgramState::Return(1), - ), - ( - "should_pass/language/tuple_in_struct", - ProgramState::Return(1), - ), - ( - "should_pass/language/nested_structs", - ProgramState::Return(1), - ), - ("should_pass/language/while_loops", ProgramState::Return(1)), - ( - "should_pass/language/retd_small_array", - ProgramState::ReturnData(Bytes32::from([ - 0xcd, 0x26, 0x62, 0x15, 0x4e, 0x6d, 0x76, 0xb2, 0xb2, 0xb9, 0x2e, 0x70, 0xc0, 0xca, - 0xc3, 0xcc, 0xf5, 0x34, 0xf9, 0xb7, 0x4e, 0xb5, 0xb8, 0x98, 0x19, 0xec, 0x50, 0x90, - 0x83, 0xd0, 0x0a, 0x50, - ])), - ), - ( - "should_pass/language/retd_zero_len_array", - ProgramState::ReturnData(Bytes32::from([ - 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, - 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, - 0x78, 0x52, 0xb8, 0x55, - ])), - ), - ("should_pass/language/is_prime", ProgramState::Return(1)), - ( - "should_pass/language/generic_impl_self", - ProgramState::Return(10), - ), - ( - "should_pass/language/enum_init_fn_call", - ProgramState::Return(1), - ), - ( - "should_pass/language/nested_while_and_if", - ProgramState::Return(1), - ), - ( - "should_pass/language/is_reference_type", - ProgramState::Return(1), - ), - ( - "should_pass/language/contract_caller_as_type", - ProgramState::Return(42), - ), - ( - "should_pass/language/non_literal_const_decl", - ProgramState::Return(42), - ), - ( - "should_pass/language/self_impl_reassignment", - ProgramState::Return(1), - ), - ( - "should_pass/language/import_trailing_comma", - ProgramState::Return(0), - ), - ( - "should_pass/language/primitive_type_argument", - ProgramState::Return(5), - ), - ( - "should_pass/language/generic-type-inference", - ProgramState::Return(0), - ), - ( - "should_pass/language/ret_small_string", - ProgramState::ReturnData(Bytes32::from([ - 0x6a, 0x4e, 0x01, 0xe9, 0x40, 0xab, 0xc0, 0x04, 0x30, 0xfe, 0x21, 0x62, 0xed, 0x69, - 0xc0, 0xe2, 0x31, 0x04, 0xf9, 0xfd, 0xa7, 0x81, 0x59, 0x09, 0x2f, 0xea, 0x8f, 0x7e, - 0xcb, 0x7f, 0x6d, 0xd4, - ])), - ), - ( - "should_pass/language/many_stack_variables", - ProgramState::Return(10), - ), - ( - "should_pass/language/ret_string_in_struct", - ProgramState::ReturnData(Bytes32::from([ - 0xaa, 0x5e, 0xfc, 0xa8, 0xda, 0xaf, 0x6e, 0xe6, 0x3f, 0x44, 0x93, 0xb2, 0x88, 0xb3, - 0x85, 0xd7, 0x60, 0xb8, 0xef, 0x93, 0xdc, 0x70, 0xe0, 0xfb, 0xc3, 0x06, 0xed, 0x9b, - 0x67, 0x6e, 0x5f, 0x13, - ])), - ), - ( - "should_pass/language/match_expressions_simple", - ProgramState::Return(42), - ), - ( - "should_pass/language/multi_impl_self", - ProgramState::Return(42), - ), - ( - "should_pass/language/implicit_return", - ProgramState::Return(42), - ), - ( - "should_pass/language/match_expressions_enums", - ProgramState::Return(42), - ), - ( - "should_pass/language/match_expressions_nested", - ProgramState::Return(123), - ), - ( - "should_pass/language/match_expressions_mismatched", - ProgramState::Return(5), - ), - ( - "should_pass/language/match_expressions_inside_generic_functions", - ProgramState::Return(1), - ), - ( - "should_pass/language/generic_inside_generic", - ProgramState::Return(7), - ), - ( - "should_pass/language/tuple_single_element", - ProgramState::Return(1), - ), - ( - "should_pass/language/reassignment_operators", - ProgramState::Return(1), - ), - ( - "should_pass/language/valid_impurity", - ProgramState::Revert(0), // false - ), - ("should_pass/language/const_inits", ProgramState::Return(1)), - ( - "should_pass/language/enum_padding", - ProgramState::ReturnData(Bytes32::from([ - 0xce, 0x55, 0xff, 0x05, 0x11, 0x3a, 0x24, 0x2e, 0xc7, 0x9a, 0x23, 0x75, 0x0c, 0x7e, - 0x2b, 0xab, 0xaf, 0x98, 0xa8, 0xdc, 0x41, 0x66, 0x90, 0xc8, 0x57, 0xdd, 0x31, 0x72, - 0x0c, 0x74, 0x82, 0xb6, - ])), - ), - ( - "should_pass/language/unit_type_variants", - ProgramState::ReturnData(Bytes32::from([ - 0xcd, 0x04, 0xa4, 0x75, 0x44, 0x98, 0xe0, 0x6d, 0xb5, 0xa1, 0x3c, 0x5f, 0x37, 0x1f, - 0x1f, 0x04, 0xff, 0x6d, 0x24, 0x70, 0xf2, 0x4a, 0xa9, 0xbd, 0x88, 0x65, 0x40, 0xe5, - 0xdc, 0xe7, 0x7f, 0x70, - ])), // "ReturnData":{"data":"0000000000000002", .. } - ), - ( - "should_pass/language/match_expressions_with_self", - ProgramState::Return(1), - ), - ( - "should_pass/test_contracts/auth_testing_contract", - ProgramState::Revert(0), - ), - ( - "should_pass/test_contracts/balance_test_contract", - ProgramState::Revert(0), - ), - ( - "should_pass/test_contracts/basic_storage", - ProgramState::Revert(0), - ), - ( - "should_pass/test_contracts/context_testing_contract", - ProgramState::Revert(0), - ), - ( - "should_pass/test_contracts/increment_contract", - ProgramState::Revert(0), - ), - ( - "should_pass/test_contracts/storage_access_contract", - ProgramState::Revert(0), - ), - ( - "should_pass/test_contracts/test_fuel_coin_contract", - ProgramState::Revert(0), - ), - ( - "should_pass/test_contracts/nested_struct_args_contract", - ProgramState::Revert(0), - ), - ( - "should_pass/test_contracts/issue_1512_repro", - ProgramState::Revert(0), - ), - ( - "should_pass/test_contracts/array_of_structs_contract", - ProgramState::Revert(0), - ), - ( - "should_pass/test_contracts/abi_with_tuples_contract", - ProgramState::Revert(0), - ), - ( - "should_pass/test_contracts/get_storage_key_contract", - ProgramState::Revert(0), - ), - ( - "should_pass/test_contracts/multiple_impl", - ProgramState::Revert(0), - ), - ]; - - number_of_tests_run += positive_project_names_with_abi - .iter() - .fold(0, |acc, (name, res)| { - if filter(name) { - assert_eq!(crate::e2e_vm_tests::harness::runs_in_vm(name, locked), *res); - assert_matches!(crate::e2e_vm_tests::harness::test_json_abi(name), Ok(_)); - acc + 1 - } else { - acc + number_of_tests_executed += 1; } - }); - - // source code that should _not_ compile - let negative_project_names = vec![ - "should_fail/cyclic_dependency/dependency_a", - "should_fail/recursive_calls", - "should_fail/asm_missing_return", - "should_fail/asm_should_not_have_return", - "should_fail/missing_fn_arguments", - "should_fail/excess_fn_arguments", - // the feature for the below test, detecting inf deps, was reverted - // when that is re-implemented we should reenable this test - //"should_fail/infinite_dependencies", - "should_fail/top_level_vars", - "should_fail/dependency_parsing_error", - "should_fail/disallowed_gm", - "should_fail/bad_generic_annotation", - "should_fail/bad_generic_var_annotation", - "should_fail/unify_identical_unknowns", - "should_fail/array_oob", - "should_fail/array_bad_index", - "should_fail/name_shadowing", - "should_fail/match_expressions_wrong_struct", - "should_fail/match_expressions_enums", - "should_fail/pure_calls_impure", - "should_fail/nested_impure", - "should_fail/predicate_calls_impure", - "should_fail/script_calls_impure", - "should_fail/contract_pure_calls_impure", - "should_fail/literal_too_large_for_type", - "should_fail/star_import_alias", - "should_fail/item_used_without_import", - "should_fail/shadow_import", - "should_fail/missing_supertrait_impl", - "should_fail/enum_if_let_invalid_variable", - "should_fail/enum_bad_type_inference", - "should_fail/missing_func_from_supertrait_impl", - "should_fail/supertrait_does_not_exist", - "should_fail/chained_if_let_missing_branch", - "should_fail/abort_control_flow_bad", - "should_fail/match_expressions_non_exhaustive", - "should_fail/empty_impl", - "should_fail/disallow_turbofish", - "should_fail/generics_unhelpful_error", - "should_fail/generic_shadows_generic", - "should_fail/different_contract_caller_types", - "should_fail/insufficient_type_info", - "should_fail/primitive_type_argument", - "should_fail/double_underscore_fn", - "should_fail/double_underscore_trait_fn", - "should_fail/double_underscore_impl_self_fn", - "should_fail/double_underscore_var", - "should_fail/double_underscore_struct", - "should_fail/double_underscore_enum", - "should_fail/assign_to_field_of_non_mutable_struct", - "should_fail/abi_method_signature_mismatch", - "should_fail/trait_method_signature_mismatch", - "should_fail/impure_read_calls_impure_write", - "should_fail/abi_impl_purity_mismatch", - "should_fail/abi_pure_calls_impure", - "should_fail/impure_abi_read_calls_impure_write", - "should_fail/impure_trait_read_calls_impure_write", - "should_fail/trait_impl_purity_mismatch", - "should_fail/trait_pure_calls_impure", - "should_fail/match_expressions_empty_arms", - "should_fail/type_mismatch_error_message", - "should_fail/recursive_enum", - "should_fail/recursive_struct", - "should_fail/recursive_type_chain", - "should_fail/better_type_error_message", - "should_fail/storage_in_library", - "should_fail/storage_in_predicate", - "should_fail/storage_in_script", - "should_fail/multiple_impl_abi", - "should_fail/multiple_impl_fns", - "should_fail/repeated_enum_variant", - "should_fail/repeated_storage_field", - "should_fail/repeated_struct_field", - "should_fail/method_requires_mut_var", - "should_fail/impl_with_bad_generic", - "should_fail/storage_conflict", - ]; - number_of_tests_run += negative_project_names.iter().fold(0, |acc, name| { - if filter(name) { - crate::e2e_vm_tests::harness::does_not_compile(name, locked); - acc + 1 - } else { - acc - } - }); - // ---- Tests paired with contracts upon which they depend which must be pre-deployed. - let contract_and_project_names = &[ - ( - ( - "should_pass/test_contracts/basic_storage", - "should_pass/require_contract_deployment/call_basic_storage", - ), - 4242, - ), - ( - ( - "should_pass/test_contracts/increment_contract", - "should_pass/require_contract_deployment/call_increment_contract", - ), - 1, // true - ), - ( - ( - "should_pass/test_contracts/auth_testing_contract", - "should_pass/require_contract_deployment/caller_auth_test", - ), - 1, // true - ), - ( - ( - "should_pass/test_contracts/context_testing_contract", - "should_pass/require_contract_deployment/caller_context_test", - ), - 1, // true - ), - ( - ( - "should_pass/test_contracts/balance_test_contract", - "should_pass/require_contract_deployment/bal_opcode", - ), - 1, // true - ), - ( - ( - "should_pass/test_contracts/test_fuel_coin_contract", - "should_pass/require_contract_deployment/token_ops_test", - ), - 1, // true - ), - ( - ( - "should_pass/test_contracts/storage_access_contract", - "should_pass/require_contract_deployment/storage_access_caller", - ), - 1, // true - ), - ( - ( - "should_pass/test_contracts/get_storage_key_contract", - "should_pass/require_contract_deployment/get_storage_key_caller", - ), - 1, - ), - ( - ( - "should_pass/test_contracts/array_of_structs_contract", - "should_pass/require_contract_deployment/array_of_structs_caller", - ), - 1, - ), - ( - ( - "should_pass/test_contracts/abi_with_tuples_contract", - "should_pass/require_contract_deployment/call_abi_with_tuples", - ), - 1, - ), - ]; - - let total_number_of_tests = positive_project_names_no_abi.len() - + positive_project_names_with_abi.len() - + negative_project_names.len() - + contract_and_project_names.len(); - - // Filter them first. - let (contracts_and_projects, vals): (Vec<_>, Vec<_>) = contract_and_project_names - .iter() - .filter(|names| filter(names.0 .1)) - .cloned() - .unzip(); - - let (contracts, projects): (Vec<_>, Vec<_>) = contracts_and_projects.iter().cloned().unzip(); - - // Deploy and then test. - number_of_tests_run += projects.len(); - let mut contract_ids = Vec::::with_capacity(contracts.len()); - for name in contracts { - let contract_id = harness::deploy_contract(name, locked); - contract_ids.push(contract_id); - } + TestCategory::Compiles => { + let result = crate::e2e_vm_tests::harness::compile_to_bytes(&name, locked); + assert!(result.is_ok()); + let compiled = result.unwrap(); + if validate_abi { + assert!(crate::e2e_vm_tests::harness::test_json_abi(&name, &compiled).is_ok()); + } + if validate_storage_slots { + assert!(crate::e2e_vm_tests::harness::test_json_storage_slots( + &name, &compiled + ) + .is_ok()); + } + number_of_tests_executed += 1; + } - for (name, val) in projects.iter().zip(vals.iter()) { - let result = harness::runs_on_node(name, locked, &contract_ids); - assert!(result.iter().all(|r| !matches!( - r, - fuel_tx::Receipt::Revert { .. } | fuel_tx::Receipt::Panic { .. } - ))); - assert!( - result.len() >= 2 - && matches!(result[result.len() - 2], fuel_tx::Receipt::Return { .. }) - && result[result.len() - 2].val().unwrap() == *val - ); + TestCategory::FailsToCompile => { + match crate::e2e_vm_tests::harness::does_not_compile(&name, locked) { + Ok(output) => match checker.explain(&output, filecheck::NO_VARIABLES) { + Ok((success, report)) if !success => { + panic!("For {name}:\nFilecheck failed:\n{report}"); + } + Err(e) => { + panic!("For {name}:\nFilecheck directive error: {e}"); + } + _ => (), + }, + Err(_) => { + panic!("For {name}:\nFailing test did not fail."); + } + } + number_of_tests_executed += 1; + } + + TestCategory::RunsWithContract => { + let val = if let Some(TestResult::Result(val)) = expected_result { + val + } else { + panic!( + "For {name}:\nExpecting a 'result' action for a 'run_on_node' test, \ + found: {expected_result:?}." + ) + }; + + if contract_paths.is_empty() { + panic!( + "For {name}\n\ + One or more ontract paths are required for 'run_on_node' tests." + ); + } + + let contract_ids = contract_paths + .into_iter() + .map(|contract_path| { + *deployed_contracts + .entry(contract_path.clone()) + .or_insert_with(|| { + harness::deploy_contract(contract_path.as_str(), locked) + }) + }) + .collect::>(); + + let result = harness::runs_on_node(&name, locked, &contract_ids); + assert!(result.iter().all(|res| !matches!( + res, + fuel_tx::Receipt::Revert { .. } | fuel_tx::Receipt::Panic { .. } + ))); + assert!( + result.len() >= 2 + && matches!(result[result.len() - 2], fuel_tx::Receipt::Return { .. }) + && result[result.len() - 2].val().unwrap() == val + ); + + number_of_tests_executed += 1; + } + + TestCategory::Disabled => { + number_of_disabled_tests += 1; + } + } } - if number_of_tests_run == 0 { + if number_of_tests_executed == 0 { tracing::info!( "No tests were run. Regex filter \"{}\" filtered out all {} tests.", - filter_regex.map(|x| x.to_string()).unwrap_or_default(), + filter_regex + .map(|regex| regex.to_string()) + .unwrap_or_default(), total_number_of_tests ); } else { tracing::info!("_________________________________\nTests passed."); tracing::info!( - "{} tests run ({} skipped)", - number_of_tests_run, - total_number_of_tests - number_of_tests_run + "{} tests run ({} skipped, {} disabled)", + number_of_tests_executed, + total_number_of_tests - number_of_tests_executed - number_of_disabled_tests, + number_of_disabled_tests, ); } } + +fn discover_test_configs() -> Result, String> { + fn recursive_search(path: &Path, configs: &mut Vec) -> Result<(), String> { + let wrap_err = |e| { + let relative_path = path + .iter() + .skip_while(|part| part.to_string_lossy() != "test_programs") + .skip(1) + .collect::(); + format!("{}: {}", relative_path.display(), e) + }; + if path.is_dir() { + for entry in std::fs::read_dir(path).unwrap() { + recursive_search(&entry.unwrap().path(), configs)?; + } + } else if path.is_file() && path.file_name() == Some(std::ffi::OsStr::new("test.toml")) { + configs.push(parse_test_toml(path).map_err(wrap_err)?); + } + Ok(()) + } + + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let tests_root_dir = format!("{manifest_dir}/src/e2e_vm_tests/test_programs"); + + let mut configs = Vec::new(); + recursive_search(&PathBuf::from(tests_root_dir), &mut configs)?; + Ok(configs) +} + +fn parse_test_toml(path: &Path) -> Result { + let (toml_content, checker) = std::fs::read_to_string(path) + .map_err(|e| e.to_string()) + .and_then(|toml_content_str| { + // Parse the file for FileCheck directives and_then parse the file into TOML. + filecheck::CheckerBuilder::new() + .text(&toml_content_str) + .map_err(|e| e.to_string()) + .and_then(|checker| { + toml_content_str + .parse::() + .map_err(|e| e.to_string()) + .map(|toml_content| (toml_content, checker.finish())) + }) + }) + .map_err(|e| format!("Failed to parse: {e}"))?; + + if !toml_content.is_table() { + return Err("Malformed test description.".to_owned()); + } + + let category = toml_content + .get("category") + .ok_or_else(|| "Missing mandatory 'category' entry.".to_owned()) + .and_then(|category_val| match category_val.as_str() { + Some("run") => Ok(TestCategory::Runs), + Some("run_on_node") => Ok(TestCategory::RunsWithContract), + Some("fail") => Ok(TestCategory::FailsToCompile), + Some("compile") => Ok(TestCategory::Compiles), + Some("disabled") => Ok(TestCategory::Disabled), + None => Err(format!( + "Malformed category '{category_val}', should be a string." + )), + Some(other) => Err(format!("Unknown category '{}'.", other,)), + })?; + + // Abort early if we find a FailsToCompile test without any Checker directives. + if category == TestCategory::FailsToCompile && checker.is_empty() { + return Err("'fail' tests must contain some FileCheck verification directives.".to_owned()); + } + + let expected_result = match &category { + TestCategory::Runs | TestCategory::RunsWithContract => { + Some(get_expected_result(&toml_content)?) + } + TestCategory::Compiles | TestCategory::FailsToCompile | TestCategory::Disabled => None, + }; + + let contract_paths = match toml_content.get("contracts") { + None => Vec::new(), + Some(contracts) => contracts + .as_array() + .ok_or_else(|| "Contracts must be an array of strings.".to_owned()) + .and_then(|vals| { + vals.iter() + .map(|val| { + val.as_str() + .ok_or_else(|| "Contracts must be path strings.".to_owned()) + .map(|path_str| path_str.to_owned()) + }) + .collect::, _>>() + })?, + }; + + let validate_abi = toml_content + .get("validate_abi") + .map(|v| v.as_bool().unwrap_or(false)) + .unwrap_or(false); + + let validate_storage_slots = toml_content + .get("validate_storage_slots") + .map(|v| v.as_bool().unwrap_or(false)) + .unwrap_or(false); + + // We need to adjust the path to start relative to `test_programs`. + let name = path + .iter() + .skip_while(|part| part.to_string_lossy() != "test_programs") + .skip(1) + .collect::(); + + // And it needs to chop off the `test.toml` and convert to a String. + let name = name + .parent() + .unwrap() + .to_str() + .map(|s| s.to_owned()) + .unwrap(); + + Ok(TestDescription { + name, + category, + expected_result, + contract_paths, + validate_abi, + validate_storage_slots, + checker, + }) +} + +fn get_expected_result(toml_content: &toml::Value) -> Result { + fn get_action_value( + action: &toml::Value, + expected_value: &toml::Value, + ) -> Result { + match (action.as_str(), expected_value) { + // A simple integer value. + (Some("return"), toml::Value::Integer(v)) => Ok(TestResult::Return(*v as u64)), + + // Also a simple integer value, but is a result from a contract call. + (Some("result"), toml::Value::Integer(v)) => Ok(TestResult::Result(*v as Word)), + + // A bytes32 value. + (Some("return_data"), toml::Value::Array(ary)) => ary + .iter() + .map(|byte_val| { + byte_val.as_integer().ok_or_else(|| { + format!( + "Return data must only contain integer values; \ + found {byte_val}." + ) + }) + }) + .collect::, _>>() + .and_then(|bytes| { + if bytes.iter().any(|byte| *byte < 0 || *byte > 255) { + Err("Return data byte values must be less than 256.".to_owned()) + } else if bytes.len() != 32 { + Err(format!( + "Return data must be a 32 byte array; \ + found {} values.", + bytes.len() + )) + } else { + Ok(bytes.iter().map(|byte| *byte as u8).collect()) + } + }) + .map(|bytes: Vec| { + let fixed_byte_array = + bytes + .iter() + .enumerate() + .fold([0_u8; 32], |mut ary, (idx, byte)| { + ary[idx] = *byte; + ary + }); + TestResult::ReturnData(Bytes32::from(fixed_byte_array)) + }), + + // Revert with a specific code. + (Some("revert"), toml::Value::Integer(v)) => Ok(TestResult::Revert(*v as u64)), + + _otherwise => Err(format!("Malformed action value: {action} {expected_value}")), + } + } + + toml_content + .get("expected_result") + .ok_or_else(|| "Could not find mandatory 'expected_result' entry.".to_owned()) + .and_then(|expected_result_table| { + expected_result_table + .get("action") + .ok_or_else(|| { + "Could not find mandatory 'action' field in 'expected_result' entry.".to_owned() + }) + .and_then(|action| { + expected_result_table + .get("value") + .ok_or_else(|| { + "Could not find mandatory 'value' field in 'expected_result' entry." + .to_owned() + }) + .and_then(|expected_value| get_action_value(action, expected_value)) + }) + }) +} diff --git a/test/src/e2e_vm_tests/test_programs/README.md b/test/src/e2e_vm_tests/test_programs/README.md new file mode 100644 index 00000000000..3f3e734b054 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/README.md @@ -0,0 +1,115 @@ +# Config Driven End To End Testing. + +Each of the tests in this suite are controlled by a TOML descriptor file which describes how the +test should be run and what result to expect if any. + +## test.toml + +To add a new test to the E2E suite place a `test.toml` file at the root of the test Forc package, +i.e., next to the `Forc.toml` file. This file may contain a few basic fields. + +## category + +The `category` field is mandatory and must be one of the following strings: + +* `"run"` - The test is compiled and run in a VM. +* `"run_on_node"` - The test is compiled and run on a local Fuel Core node. +* `"compile"` - The test is expected to succeed compiling, but isn't run in any way. +* `"fail"` - The test is expected to fail to compile. +* `"disabled"` - The test is disabled. + +## expected_result + +The `expected_result` field is mandatory for `"run"` and `"run_on_node"` tests. It is a table with +two fields, `action` and `value`. + +The `action` field describe what sort of result to expect: + +* `"return"` - An integer value returned by success in the VM. +* `"return_data"` - An array of bytes returned by the VM. +* `"result"` - An integer word returned by the Fuel Core node. +* `"revert"` - An integer value returned by failure in the VM. + +The `value` field is the actual expected value. For `"return"`, `"result"` and `"revert"` actions +it must be an integer. + +For `"return_data"` actions it must be an array of byte values, each an integer between 0 and 255. + +## contracts + +Tests in the `"run_on_node"` category will usually specify one or more contracts which must be +deployed to the node prior to deploying and running the test code. These are specified with the +`contracts` field. + +It must be an array of strings each containing only the path to the Forc project for the contract to +be compiled and deployed. It is important that these paths remain relative to the +`test/src/e2e_vm_tests/test_programs` directory. + +## validate_abi + +Some tests also require their ABI is verified. To indicate this the `validate_abi` field may be +specified, as a boolean value. + +# FileCheck for 'fail' tests + +The tests in the `fail` category _must_ employ verification using pattern matching via the [FileCheck](https://docs.rs/filecheck/latest/filecheck/) +crate. The checker directives are specified in comments (lines beginning with `#`) in the `test.toml` +file. + +Typically this is as simple as just adding a `# check: ...` line to the line specifying the full +error or warning message expected from compiling the test. `FileCheck` also has other directives for +fancier pattern matching, as specified in the [FileCheck docs](https://docs.rs/filecheck/latest/filecheck/). + +**Note:** The output from the compiler is colorized, usually to red or yellow, and this involves +printing ANSI escape sequences to the terminal. These sequences can confuse `FileCheck` as it tries +to match patterns on 'word' boundaries. The first word in an error message is most likely prefixed +with an escape sequence and can cause the check to fail. + +To avoid this problem one may either not use the first word in the error message, or use the 'empty +string' pattern `$()` to direct the matcher as to where the pattern starts. + +E.g, `# check: $()The name "S" shadows another symbol with the same name.` + +# Examples + +The following is a common example for tests in the `should_pass/language` directory. The test +should be compiled, run on the VM, should expect a return value of 42 and should also validate the +ABI generation. + +```toml +category = "run" +expected_result = { action = "return", value = 42 } +validate_abi = true +``` + +And this example is similar but expects return data. + +```toml +category = "run" +expected_result = { action = "return_data", value = [ 102, 104, 122, 173, 248, 98, 189, 119, 108, 143, 193, 139, 142, 159, 142, 32, 8, 151, 20, 133, 110, 226, 51, 179, 144, 42, 89, 29, 13, 95, 41, 37 ] } +validate_abi = true +``` + +The following tests a contract on a Fuel Core node. + +```toml +category = "run_on_node" +expected_result = { action = "result", value = 11 } +contracts = ["should_pass/test_contracts/test_contract_a", "should_pass/test_contracts/test_contract_b"] +``` + +Tests which fail can have fairly elaborate checks. + +```toml +category = "fail" + +# check: // this asm block should return unit, i.e. nothing +# nextln: asm(r1: 5) { +# check: $()Mismatched types. +# nextln: $()expected: () +# nextln: $()found: u64. +# nextln: $()help: Implicit return must match up with block's type. +``` + +And there are already hundreds of existing tests with `test.toml` descriptors which may be consulted +when adding a new test. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/abi_impl_purity_mismatch/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/abi_impl_purity_mismatch/test.toml new file mode 100644 index 00000000000..e2cad27ba68 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/abi_impl_purity_mismatch/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +# check: $()Storage attribute access mismatch. The trait function "test_function" in trait "MyContract" requires the storage attribute(s) #[storage(read)]. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/abi_method_signature_mismatch/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/abi_method_signature_mismatch/test.toml new file mode 100644 index 00000000000..7e008750474 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/abi_method_signature_mismatch/test.toml @@ -0,0 +1,5 @@ +category = "fail" + +# check: fn foo(s: str[7]) -> str[7] { +# nextln: $()Expected: u64 +# nextln: $()found: str[7]. The definition of this function must match the one in the trait declaration. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/abi_pure_calls_impure/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/abi_pure_calls_impure/test.toml new file mode 100644 index 00000000000..03bbc908cd3 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/abi_pure_calls_impure/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: f() +# nextln: $()Storage attribute access mismatch. Try giving the surrounding function more access by adding "#[storage(read)]" to the function declaration. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/abort_control_flow_bad/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/abort_control_flow_bad/test.toml new file mode 100644 index 00000000000..95441264e4e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/abort_control_flow_bad/test.toml @@ -0,0 +1,16 @@ +category = "fail" + +# check: return 42; +# nextln: $()Mismatched types. +# nextln: $()expected: () +# nextln: $()found: u64. +# nextln: $()help: Return statement must return the declared function return type. + +# This 'return true' line appears in both error messages.. +# check: return true; + +# check: return true; +# nextln: $()Mismatched types. +# nextln: $()expected: () +# nextln: $()found: bool. +# nextln: $()help: Return statement must return the declared function return type. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/array_bad_index/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/array_bad_index/test.toml new file mode 100644 index 00000000000..366684b2f51 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/array_bad_index/test.toml @@ -0,0 +1,6 @@ +category = "fail" + +# check: ary[false] +# nextln: $()Mismatched types. +# nextln: $()expected: u64 +# nextln: $()found: bool. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/array_oob/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/array_oob/test.toml new file mode 100644 index 00000000000..f4fb799b640 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/array_oob/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +# check: $()Array index out of bounds; the length is 3 but the index is 4. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/asm_missing_return/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/asm_missing_return/test.toml new file mode 100644 index 00000000000..81f51f41dc3 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/asm_missing_return/test.toml @@ -0,0 +1,13 @@ +category = "fail" + +# check: asm(r1: 5) { +# check: $()Mismatched types. +# nextln: $()expected: u64 +# nextln: $()found: (). +# nextln: $()help: Implicit return must match up with block's type. + +# check: asm(r1: 5) { +# check: $()Mismatched types. +# nextln: $()expected: u64 +# nextln: $()found: (). +# nextln: $()help: Function body's return type does not match up with its return type annotation. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/asm_should_not_have_return/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/asm_should_not_have_return/test.toml new file mode 100644 index 00000000000..fd0513db60e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/asm_should_not_have_return/test.toml @@ -0,0 +1,15 @@ +category = "fail" + +# check: // this asm block should return unit, i.e. nothing +# nextln: asm(r1: 5) { +# check: $()Mismatched types. +# nextln: $()expected: () +# nextln: $()found: u64. +# nextln: $()help: Implicit return must match up with block's type. + +# check: // this asm block should return unit, i.e. nothing +# nextln: asm(r1: 5) { +# check: $()Mismatched types. +# nextln: $()expected: () +# nextln: $()found: u64. +# nextln: $()help: Function body's return type does not match up with its return type annotation. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/assign_to_field_of_non_mutable_struct/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/assign_to_field_of_non_mutable_struct/test.toml new file mode 100644 index 00000000000..2cdba2605b9 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/assign_to_field_of_non_mutable_struct/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +# check: $()Assignment to immutable variable. Variable thing is not declared as mutable. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/bad_generic_annotation/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/bad_generic_annotation/test.toml new file mode 100644 index 00000000000..f76942c087b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/bad_generic_annotation/test.toml @@ -0,0 +1,7 @@ +category = "fail" + +# check: let g: u32 = three_generics(true, "foo", 10); +# nextln: $()Mismatched types. +# nextln: $()expected: u32 +# nextln: $()found: str[3]. +# nextln: $()help: Variable declaration's type annotation does not match up with the assigned expression's type. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/bad_generic_var_annotation/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/bad_generic_var_annotation/test.toml new file mode 100644 index 00000000000..8e710db7414 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/bad_generic_var_annotation/test.toml @@ -0,0 +1,13 @@ +category = "fail" + +# check: let g: str[3] = three_generics(true, "foo", 10); +# nextln: $()Mismatched types. +# nextln: $()expected: bool +# nextln: $()found: str[3]. +# nextln: $()help: The argument that has been provided to this function's type does not match the declared type of the parameter in the function declaration. + +# check: let g: str[3] = three_generics(true, "foo", 10); +# nextln: $()Mismatched types. +# nextln: $()expected: str[3] +# nextln: $()found: bool. +# nextln: $()help: Variable declaration's type annotation does not match up with the assigned expression's type. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/better_type_error_message/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/better_type_error_message/test.toml new file mode 100644 index 00000000000..50c676d74ab --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/better_type_error_message/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: $()Could not find symbol "Result" in this scope. +# check: $()Unknown type name "Result". diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/chained_if_let_missing_branch/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/chained_if_let_missing_branch/test.toml new file mode 100644 index 00000000000..6a45df6f7c4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/chained_if_let_missing_branch/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +# check: $()Variable "num" does not exist in this scope. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/Forc.lock new file mode 100644 index 00000000000..fce30ef9643 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/Forc.lock @@ -0,0 +1,9 @@ +[[package]] +name = 'const_nonconst_init' +source = 'root' +dependencies = ['core'] + +[[package]] +name = 'core' +source = 'path+from-root-E57A3612ABF8CF11' +dependencies = [] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/Forc.toml new file mode 100644 index 00000000000..8c981ae7e1b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "const_nonconst_init" + +[dependencies] +core = { path = "../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/src/main.sw new file mode 100644 index 00000000000..207339315f9 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/src/main.sw @@ -0,0 +1,10 @@ +script; + +fn bla(x: u64) -> u64 { + x + 1 +} + +fn main() -> u64 { + const X = bla(0); + X +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/test.toml new file mode 100644 index 00000000000..e92e4b0b7ee --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/const_nonconst_init/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: $()const X = bla(0); +# nextln: $()Could not evaluate initializer to a const declaration. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/contract_pure_calls_impure/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/contract_pure_calls_impure/test.toml new file mode 100644 index 00000000000..27781afe7eb --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/contract_pure_calls_impure/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: foo(); +# nextln: $()Storage attribute access mismatch. Try giving the surrounding function more access by adding "#[storage(read, write)]" to the function declaration. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/cyclic_dependency/dependency_a/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/cyclic_dependency/dependency_a/test.toml new file mode 100644 index 00000000000..51bdfacac1a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/cyclic_dependency/dependency_a/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +# check: dependency cycle detected: dependency_a -> dependency_b -> dependency_a diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/dependency_parsing_error/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/dependency_parsing_error/test.toml new file mode 100644 index 00000000000..bc59bcc1856 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/dependency_parsing_error/test.toml @@ -0,0 +1,5 @@ +category = "fail" + +# Not sure if this is the originally expected error: +# check: let baz = 43; +# nextln: $()Expected an item. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/different_contract_caller_types/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/different_contract_caller_types/test.toml new file mode 100644 index 00000000000..355316b3291 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/different_contract_caller_types/test.toml @@ -0,0 +1,15 @@ +category = "fail" + +# check: let caller = abi(OtherAbi, ADDRESS); +# nextln: caller +# nextln: $()Mismatched types. +# nextln: $()expected: contract caller SomeAbi +# nextln: $()found: contract caller OtherAbi. +# nextln: $()help: Implicit return must match up with block's type. + +# check: let caller = abi(OtherAbi, ADDRESS); +# nextln: caller +# nextln: $()Mismatched types. +# nextln: $()expected: contract caller SomeAbi +# nextln: $()found: contract caller OtherAbi. +# nextln: $()help: Function body's return type does not match up with its return type annotation. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/disallow_turbofish/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/disallow_turbofish/test.toml new file mode 100644 index 00000000000..a4948439baa --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/disallow_turbofish/test.toml @@ -0,0 +1,10 @@ +category = "fail" + +# check: let data = Data:: { +# nextln: $()"Data" does not take type arguments. + +# check: let res1 = Result:: { +# nextln: $()Expected 2 type arguments, but instead found 1. + +# check: foo::(); +# nextln: $()"foo" does not take type arguments. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/disallowed_gm/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/disallowed_gm/test.toml new file mode 100644 index 00000000000..bb1c25bdea2 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/disallowed_gm/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: gm r1 i1; +# nextln: $()The GM (get-metadata) opcode, when called from an external context, will cause the VM to panic. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/double_underscore_enum/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/double_underscore_enum/test.toml new file mode 100644 index 00000000000..7154cc6c101 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/double_underscore_enum/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: enum __MyEnum { +# nextln: $()Identifiers cannot begin with a double underscore, as that naming convention is reserved for compiler intrinsics. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/double_underscore_fn/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/double_underscore_fn/test.toml new file mode 100644 index 00000000000..4543ad5a87d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/double_underscore_fn/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: __test() +# nextln: $()Identifiers cannot begin with a double underscore, as that naming convention is reserved for compiler intrinsics. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/double_underscore_impl_self_fn/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/double_underscore_impl_self_fn/test.toml new file mode 100644 index 00000000000..951e04dfc1f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/double_underscore_impl_self_fn/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: fn __double_underscore(self, x: bool) -> bool { +# nextln: $()Identifiers cannot begin with a double underscore, as that naming convention is reserved for compiler intrinsics. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/double_underscore_struct/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/double_underscore_struct/test.toml new file mode 100644 index 00000000000..ebbda21f9d3 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/double_underscore_struct/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: struct __MyStruct { +# nextln: $()Identifiers cannot begin with a double underscore, as that naming convention is reserved for compiler intrinsics. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/double_underscore_trait_fn/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/double_underscore_trait_fn/test.toml new file mode 100644 index 00000000000..4543ad5a87d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/double_underscore_trait_fn/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: __test() +# nextln: $()Identifiers cannot begin with a double underscore, as that naming convention is reserved for compiler intrinsics. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/double_underscore_var/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/double_underscore_var/test.toml new file mode 100644 index 00000000000..a083e9620bb --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/double_underscore_var/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: __a +# nextln: $()Identifiers cannot begin with a double underscore, as that naming convention is reserved for compiler intrinsics. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/empty_impl/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/empty_impl/test.toml new file mode 100644 index 00000000000..60d4ed90b21 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/empty_impl/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +# check: $()Could not find symbol "Coin" in this scope. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/enum_bad_type_inference/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/enum_bad_type_inference/Forc.toml index f47a8d75a5b..fdf0ae59d95 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/enum_bad_type_inference/Forc.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/enum_bad_type_inference/Forc.toml @@ -5,4 +5,4 @@ license = "Apache-2.0" name = "enum_bad_type_inference" [dependencies] -core = { path = "../../../../../../../sway-lib-core" } +core = { path = "../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/enum_bad_type_inference/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/enum_bad_type_inference/test.toml new file mode 100644 index 00000000000..c0a3049c75d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/enum_bad_type_inference/test.toml @@ -0,0 +1,12 @@ +category = "fail" + + +# check: Ok: T, +# nextln: $()Mismatched types. +# nextln: $()expected: bool +# nextln: $()found: u64. + +# check: enum Result { +# nextln: $()Mismatched types. +# nextln: $()expected: bool +# nextln: $()found: u64. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/enum_if_let_invalid_variable/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/enum_if_let_invalid_variable/test.toml new file mode 100644 index 00000000000..48fb7c32872 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/enum_if_let_invalid_variable/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: if let +# nextln: $()Variable "y" does not exist in this scope. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/Forc.lock new file mode 100644 index 00000000000..63ae381216c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/Forc.lock @@ -0,0 +1,14 @@ +[[package]] +name = 'core' +source = 'path+from-root-987CA0C143EB9BCC' +dependencies = [] + +[[package]] +name = 'eq_intrinsic' +source = 'root' +dependencies = ['std'] + +[[package]] +name = 'std' +source = 'path+from-root-987CA0C143EB9BCC' +dependencies = ['core'] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/Forc.toml new file mode 100644 index 00000000000..e84dbdc4d76 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "eq_intrinsic" + +[dependencies] +std = { path = "../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/json_abi_oracle.json new file mode 100644 index 00000000000..905c3bd1034 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/json_abi_oracle.json @@ -0,0 +1,14 @@ +[ + { + "inputs": [], + "name": "main", + "outputs": [ + { + "components": null, + "name": "", + "type": "u64" + } + ], + "type": "function" + } +] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/src/main.sw new file mode 100644 index 00000000000..f0af6b19049 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/src/main.sw @@ -0,0 +1,23 @@ +script; + +struct A { + a: u64, +} + +enum B { + First: (), + Second: u64 +} + +fn main() { + let _ = __eq("hi", "ho"); + let _ = __eq(false, 11); + let _ = __eq(A { a: 1 }, B { a: 1 }); + let _ = __eq((1, 2), (1, 2)); + let _ = __eq([1, 2], [1, 2]); + let _ = __eq(B::First, B::First); + let _ = __eq(B::Second(1), B::Second(1)); + let my_number1: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A; + let my_number2: b256 = 0x000000000000000000000000000000000000000000000000000000000000002A; + let _ = __eq(my_number1, my_number1); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/test.toml new file mode 100644 index 00000000000..299b8ccd092 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/eq_intrinsic/test.toml @@ -0,0 +1,25 @@ +category = "fail" + +# check: $()__eq("hi", "ho"); +# nextln: $()Unsupported argument type to intrinsic "eq". + +# check: $()__eq(false, 11); +# check: $()Mismatched types. + +# check: $()__eq(A { a: 1 }, B { a: 1 }); +# check: $()Unsupported argument type to intrinsic "eq". + +# check: $()__eq((1, 2), (1, 2)); +# check: $()Unsupported argument type to intrinsic "eq". + +# check: $()__eq([1, 2], [1, 2]); +# check: $()Unsupported argument type to intrinsic "eq". + +# check: $()__eq(B::First, B::First); +# check: $()Unsupported argument type to intrinsic "eq". + +# check: $()__eq(B::Second(1), B::Second(1)); +# check: $()Unsupported argument type to intrinsic "eq". + +# check: $()__eq(my_number1, my_number1); +# check: $()Unsupported argument type to intrinsic "eq". \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/excess_fn_arguments/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/excess_fn_arguments/Forc.lock index c4169651b66..94668d10be6 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/excess_fn_arguments/Forc.lock +++ b/test/src/e2e_vm_tests/test_programs/should_fail/excess_fn_arguments/Forc.lock @@ -1,4 +1,9 @@ +[[package]] +name = 'core' +source = 'path+from-root-8EECC8A749908BB3' +dependencies = [] + [[package]] name = 'excess_fn_arguments' source = 'root' -dependencies = [] +dependencies = ['core'] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/excess_fn_arguments/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/excess_fn_arguments/Forc.toml index 52df234df91..71302867fd4 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/excess_fn_arguments/Forc.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/excess_fn_arguments/Forc.toml @@ -3,4 +3,6 @@ authors = ["Fuel Labs "] license = "Apache-2.0" name = "excess_fn_arguments" entry = "main.sw" -implicit-std = false + +[dependencies] +core = { path = "../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/excess_fn_arguments/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/excess_fn_arguments/test.toml new file mode 100644 index 00000000000..a220620ac5d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/excess_fn_arguments/test.toml @@ -0,0 +1,5 @@ +# This test should be a "fail" but it's currently passing. +# See https://github.com/FuelLabs/sway/issues/2081 +category = "compile" + +# check: ... something about too many args ... diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/generic_shadows_generic/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/generic_shadows_generic/test.toml new file mode 100644 index 00000000000..f76f70db80e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/generic_shadows_generic/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +# check: $()The name "T" is already used for a generic parameter in this scope. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/generics_unhelpful_error/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/generics_unhelpful_error/test.toml new file mode 100644 index 00000000000..a41fe303372 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/generics_unhelpful_error/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: fn f(a: DoubleIdentity) { +# nextln: $()Expected 2 type arguments, but instead found 1. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_bad_generic/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_bad_generic/test.toml new file mode 100644 index 00000000000..0550b590a9d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_bad_generic/test.toml @@ -0,0 +1,7 @@ +category = "fail" + +# check: impl S { +# nextln: $()The generic type parameter "T" is unconstrained. + +# check: (S{}).f(true); +# nextln: $()No method named "f" found for type "S". diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/.gitignore b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/.gitignore new file mode 100644 index 00000000000..77d3844f58c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/.gitignore @@ -0,0 +1,2 @@ +out +target diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/Forc.lock new file mode 100644 index 00000000000..39a70d597e7 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/Forc.lock @@ -0,0 +1,14 @@ +[[package]] +name = 'core' +source = 'path+from-root-C569FB5C26218A1B' +dependencies = [] + +[[package]] +name = 'impl_with_semantic_type_constraints' +source = 'root' +dependencies = ['std'] + +[[package]] +name = 'std' +source = 'path+from-root-C569FB5C26218A1B' +dependencies = ['core'] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/Forc.toml new file mode 100644 index 00000000000..e21f59c2882 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "impl_with_semantic_type_constraints" + +[dependencies] +std = { path = "../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/src/main.sw new file mode 100644 index 00000000000..bfdc1398d1a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/src/main.sw @@ -0,0 +1,54 @@ +script; + +struct DoubleIdentity { + first: T, + second: F, +} + +impl DoubleIdentity { + fn get_first(self) -> T { + self.first + } +} + +impl DoubleIdentity { + fn get_second(self) -> F { + self.second + } +} + +impl DoubleIdentity { + fn add(self) -> u8 { + self.first + self.second + } +} + +fn main() { + let a = DoubleIdentity { + first: 0u8, + second: 1u8 + }; + let b = DoubleIdentity { + first: true, + second: false, + }; + let c = DoubleIdentity { + first: 0u64, + second: "hi" + }; + + let d = a.get_first(); + let e = a.get_second(); + let f = a.add(); + + let g = b.get_first(); + let h = b.get_second(); + // should fail + let i = b.add(); + + // should fail + let j = c.get_first(); + let k = c.get_second(); + // should fail + let l = c.add(); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/test.toml new file mode 100644 index 00000000000..70f231d938e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/impl_with_semantic_type_constraints/test.toml @@ -0,0 +1,10 @@ +category = "fail" + +# check: let i = b.add(); +# nextln: $()No method named "add" found for type "DoubleIdentity". + +# check: let j = c.get_first(); +# nextln: $()No method named "get_first" found for type "DoubleIdentity". + +# check: let l = c.add(); +# nextln: $()No method named "add" found for type "DoubleIdentity". diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/impure_abi_read_calls_impure_write/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/impure_abi_read_calls_impure_write/test.toml new file mode 100644 index 00000000000..d4b070cc54f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/impure_abi_read_calls_impure_write/test.toml @@ -0,0 +1,5 @@ +category = "fail" + +# check: fn test_function() -> bool { +# nextln: f() +# nextln: $()Storage attribute access mismatch. Try giving the surrounding function more access by adding "#[storage(read, write)]" to the function declaration. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/impure_read_calls_impure_write/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/impure_read_calls_impure_write/test.toml new file mode 100644 index 00000000000..1c9fcacfc24 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/impure_read_calls_impure_write/test.toml @@ -0,0 +1,5 @@ +category = "fail" + +# check: fn can_read() { +# nextln: can_write(); +# nextln: $()Storage attribute access mismatch. Try giving the surrounding function more access by adding "#[storage(read, write)]" to the function declaration. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/impure_trait_read_calls_impure_write/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/impure_trait_read_calls_impure_write/test.toml new file mode 100644 index 00000000000..59172c85dfc --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/impure_trait_read_calls_impure_write/test.toml @@ -0,0 +1,5 @@ +category = "fail" + +# check: pub fn g() -> bool { +# nextln: true.f() +# nextln: $()Storage attribute access mismatch. Try giving the surrounding function more access by adding "#[storage(read, write)]" to the function declaration. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/infinite_dependencies/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/infinite_dependencies/Forc.lock index 2e6d2d8ead1..3dd8c426e89 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/infinite_dependencies/Forc.lock +++ b/test/src/e2e_vm_tests/test_programs/should_fail/infinite_dependencies/Forc.lock @@ -1,3 +1,4 @@ [[package]] name = 'infinite_dependencies' +source = 'root' dependencies = [] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/infinite_dependencies/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/infinite_dependencies/test.toml new file mode 100644 index 00000000000..73cf88a0b5b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/infinite_dependencies/test.toml @@ -0,0 +1,5 @@ +# The feature for this test, detecting infinite deps, was reverted. When that is re-implemented we +# should re-enable this test. + +#category = "fail" +category = "disabled" diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/insufficient_type_info/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/insufficient_type_info/src/main.sw index 92aaab678c8..ea156f19381 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/insufficient_type_info/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/insufficient_type_info/src/main.sw @@ -1,7 +1,7 @@ script; fn foo() { - let x = size_of::(); + let x = __size_of::(); } fn main() { diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/insufficient_type_info/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/insufficient_type_info/test.toml new file mode 100644 index 00000000000..2c9e7e9e3ed --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/insufficient_type_info/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: __size_of::(); +# nextln: $()Cannot infer type for type parameter "T". Insufficient type information provided. Try annotating its type. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/item_used_without_import/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/item_used_without_import/test.toml new file mode 100644 index 00000000000..1d634fdaae6 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/item_used_without_import/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +# check: $()Could not find symbol "Bar" in this scope. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/literal_too_large_for_type/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/literal_too_large_for_type/test.toml new file mode 100644 index 00000000000..2b0c9358bb7 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/literal_too_large_for_type/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: let x:u8 = 256; +# nextln: $()Literal value is too large for type u8. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_empty_arms/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_empty_arms/test.toml new file mode 100644 index 00000000000..b6567b12686 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_empty_arms/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: match foo { +# check: $()Non-exhaustive match expression. Missing patterns `_` diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_multiple_rest/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_multiple_rest/Forc.lock new file mode 100644 index 00000000000..9d09ce0f0ce --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_multiple_rest/Forc.lock @@ -0,0 +1,4 @@ +[[package]] +name = 'match_expressions_multiple_rest' +source = 'root' +dependencies = [] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_multiple_rest/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_multiple_rest/Forc.toml new file mode 100644 index 00000000000..4fa8087584a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_multiple_rest/Forc.toml @@ -0,0 +1,6 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "match_expressions_multiple_rest" +entry = "main.sw" +implicit-std = false diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_multiple_rest/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_multiple_rest/src/main.sw new file mode 100644 index 00000000000..435271e4ea8 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_multiple_rest/src/main.sw @@ -0,0 +1,21 @@ +script; + +struct Point { + x: u64, + y: u64 +} + +// this should fail because of multiple rest patterns + +fn main() -> u64 { + let p = Point { + x: 1u64, + y: 2u64, + }; + + match p { + Point { x, .., .. } => { x }, + }; + + 0 +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_multiple_rest/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_multiple_rest/test.toml new file mode 100644 index 00000000000..04dc0251c08 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_multiple_rest/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: Point { x, .., .. } => { x }, +# nextln: $()Unexpected rest token, must be at the end of pattern. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_non_exhaustive/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_non_exhaustive/Forc.toml index afca886f469..fcbca473c18 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_non_exhaustive/Forc.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_non_exhaustive/Forc.toml @@ -6,4 +6,4 @@ entry = "main.sw" implicit-std = false [dependencies] -core = { path = "../../../../../../../sway-lib-core" } +core = { path = "../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_non_exhaustive/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_non_exhaustive/test.toml new file mode 100644 index 00000000000..dc269da044e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_non_exhaustive/test.toml @@ -0,0 +1,34 @@ +category = "fail" + +# check: match_expressions_non_exhaustive/src/main.sw:41:9 +# check: $()This match arm is unreachable. + +# check: match_expressions_non_exhaustive/src/main.sw:61:9 +# check: $()This match arm is unreachable. + +# check: match_expressions_non_exhaustive/src/main.sw:90:9 +# check: $()This match arm is unreachable. + +# check: match_expressions_non_exhaustive/src/main.sw:95:9 +# check: $()This match arm is unreachable. + +# check: match_expressions_non_exhaustive/src/main.sw:101:9 +# check: $()This match arm is unreachable. + +# check: match_expressions_non_exhaustive/src/main.sw:107:9 +# check: $()This match arm is unreachable. + +# check: match_expressions_non_exhaustive/src/main.sw:37:13 +# check: $()Non-exhaustive match expression. Missing patterns `[1...4]`, `[6...9]`, `[11...MAX]` + +# check: match_expressions_non_exhaustive/src/main.sw:58:13 +# check: $()Non-exhaustive match expression. Missing patterns `(_, [2...MAX])` + +# check: match_expressions_non_exhaustive/src/main.sw:88:15 +# check: $()Non-exhaustive match expression. Missing patterns `Point { x: [MIN...2], ... }`, `Point { x: [4...MAX], ... }` + +# check: match_expressions_non_exhaustive/src/main.sw:122:15 +# check: $()Non-exhaustive match expression. Missing patterns `CrazyPoint { p1: Point { x: [1...MAX], ... }, ... }` + +# check: match_expressions_non_exhaustive/src/main.sw:128:16 +# check: $()Variable "newvariable" does not exist in this scope. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_rest/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_rest/Forc.lock new file mode 100644 index 00000000000..6ac4d6904b7 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_rest/Forc.lock @@ -0,0 +1,4 @@ +[[package]] +name = 'match_expressions_rest' +source = 'root' +dependencies = [] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_rest/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_rest/Forc.toml new file mode 100644 index 00000000000..45f46589afa --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_rest/Forc.toml @@ -0,0 +1,6 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "match_expressions_rest" +entry = "main.sw" +implicit-std = false \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_rest/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_rest/src/main.sw new file mode 100644 index 00000000000..c5e59de34ba --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_rest/src/main.sw @@ -0,0 +1,21 @@ +script; + +struct Point { + x: u64, + y: u64 +} + +// this should fail because rest patterns must appear at the end + +fn main() -> u64 { + let p = Point { + x: 1u64, + y: 2u64, + }; + + match p { + Point { .., x } => { x }, + }; + + 0 +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_rest/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_rest/test.toml new file mode 100644 index 00000000000..7501cdefb60 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_rest/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: Point { .., x } => { x }, +# nextnl: $()Unexpected rest token, must be at the end of pattern. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_struct_missing_fields/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_struct_missing_fields/Forc.lock new file mode 100644 index 00000000000..8e5e18d3779 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_struct_missing_fields/Forc.lock @@ -0,0 +1,4 @@ +[[package]] +name = 'match_expressions_struct_missing_fields' +source = 'root' +dependencies = [] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_struct_missing_fields/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_struct_missing_fields/Forc.toml new file mode 100644 index 00000000000..d1567bb5d7f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_struct_missing_fields/Forc.toml @@ -0,0 +1,6 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "match_expressions_struct_missing_fields" +entry = "main.sw" +implicit-std = false diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_struct_missing_fields/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_struct_missing_fields/src/main.sw new file mode 100644 index 00000000000..e02dfadc65f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_struct_missing_fields/src/main.sw @@ -0,0 +1,21 @@ +script; + +struct Point { + x: u64, + y: u64 +} + +// this should fail because the pattern is missing the y field + +fn main() -> u64 { + let p = Point { + x: 1u64, + y: 2u64, + }; + + match p { + Point { x } => { x }, + }; + + 0 +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_struct_missing_fields/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_struct_missing_fields/test.toml new file mode 100644 index 00000000000..ccbb454563e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_struct_missing_fields/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: Point { x } => { x }, +# nextln: $()Pattern does not mention field: y diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_wrong_struct/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_wrong_struct/test.toml new file mode 100644 index 00000000000..e252282fdeb --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/match_expressions_wrong_struct/test.toml @@ -0,0 +1,21 @@ +category = "fail" + +# check: Point { x: 3, y } => { y }, +# nextln: $()Mismatched types. +# nextln: $()expected: u64 +# nextln: $()found: Point. + +# check: Point { x: 3, y: 4 } => { 24 }, + +# check: Point { x: 3, y: 4 } => { 24 }, +# nextln: $()Mismatched types. +# nextln: $()expected: u64 +# nextln: $()found: Point. + +# check: Data { value: 1u64 } => { false }, +# nextln: $()Mismatched types. +# nextln: $()expected: bool +# nextln: $()found: u64. + +# check: Bar(x) => x, +# nextln: $()Enum with name "Bar" could not be found in this scope. Perhaps you need to import it? diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/method_requires_mut_var/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/method_requires_mut_var/test.toml new file mode 100644 index 00000000000..3aed4430d63 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/method_requires_mut_var/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +# check: $()Cannot call method "f" on variable "a" because "a" is not declared as mutable. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/missing_fn_arguments/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/missing_fn_arguments/Forc.lock index e70083b0957..8251330264b 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/missing_fn_arguments/Forc.lock +++ b/test/src/e2e_vm_tests/test_programs/should_fail/missing_fn_arguments/Forc.lock @@ -1,4 +1,9 @@ +[[package]] +name = 'core' +source = 'path+from-root-535272951E66DE82' +dependencies = [] + [[package]] name = 'missing_fn_arguments' source = 'root' -dependencies = [] +dependencies = ['core'] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/missing_fn_arguments/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/missing_fn_arguments/Forc.toml index 29c68a014d5..9884bd8cdf0 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/missing_fn_arguments/Forc.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/missing_fn_arguments/Forc.toml @@ -3,4 +3,6 @@ authors = ["Fuel Labs "] license = "Apache-2.0" name = "missing_fn_arguments" entry = "main.sw" -implicit-std = false + +[dependencies] +core = { path = "../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/missing_fn_arguments/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/missing_fn_arguments/test.toml new file mode 100644 index 00000000000..78706fc66b8 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/missing_fn_arguments/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: fn bar(a: bool, b: u64) -> u64 { +# check: $()Function "bar" expects 2 arguments but you provided 1. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/missing_func_from_supertrait_impl/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/missing_func_from_supertrait_impl/test.toml new file mode 100644 index 00000000000..58d89c982d4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/missing_func_from_supertrait_impl/test.toml @@ -0,0 +1,6 @@ +category = "fail" + +# check: impl A for Y { +# nextln: fn a1() { } +# nextln: } +# nextln: $()Functions are missing from this trait implementation: a2 diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/missing_supertrait_impl/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/missing_supertrait_impl/test.toml new file mode 100644 index 00000000000..767b0f221fb --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/missing_supertrait_impl/test.toml @@ -0,0 +1,7 @@ +category = "fail" + +# check: missing_supertrait_impl/src/main.sw:38:1 +# check: $()The trait "A" is not implemented for type "Y" + +# check: missing_supertrait_impl/src/main.sw:7:7 +# check: $()Implementation of trait "A" is required by this bound in "B" diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/multiple_impl_abi/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/multiple_impl_abi/test.toml new file mode 100644 index 00000000000..7cefb4fc483 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/multiple_impl_abi/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +# check: $()Function "test_function" was already defined in scope. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/multiple_impl_fns/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/multiple_impl_fns/test.toml new file mode 100644 index 00000000000..44eed002998 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/multiple_impl_fns/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: $()Function "foo" was already defined in scope. +# check: $()Function "bar" was already defined in scope. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/name_shadowing/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/name_shadowing/test.toml new file mode 100644 index 00000000000..5fe291769f2 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/name_shadowing/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: struct S +# nextln: $()The name "S" shadows another symbol with the same name. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/nested_impure/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/nested_impure/test.toml new file mode 100644 index 00000000000..3d74ee999ca --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/nested_impure/test.toml @@ -0,0 +1,10 @@ +category = "fail" + +# check: baz(); +# nextln: $()This returns a value of type u64, which is not assigned to anything and is ignored. + +# check: let z = baz(); +# nextln: $()Storage attribute access mismatch. Try giving the surrounding function more access by adding "#[storage(read)]" to the function declaration. + +# check: baz(); +# nextln: $()Storage attribute access mismatch. Try giving the surrounding function more access by adding "#[storage(read)]" to the function declaration. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/Forc.lock new file mode 100644 index 00000000000..a7f26464081 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/Forc.lock @@ -0,0 +1,9 @@ +[[package]] +name = 'core' +source = 'path+from-root-BC4E87DB22234D76' +dependencies = [] + +[[package]] +name = 'repeated_storage_field' +source = 'root' +dependencies = ['core'] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/Forc.toml new file mode 100644 index 00000000000..38cf33b2e6f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "repeated_storage_field" + +[dependencies] +core = { path = "../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/src/main.sw new file mode 100644 index 00000000000..45059447327 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/src/main.sw @@ -0,0 +1,19 @@ +contract; + +use core::*; + +storage { + x: u64 = 5 + 5, + y: u64 = 5 + 5, +} + +abi Test { + fn foo(); +} + +impl Test for Contract { + fn foo() { + storage.x += 1; + storage.y += 1; + } +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/test.toml new file mode 100644 index 00000000000..590c5b515ca --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/nonconst_storage_init/test.toml @@ -0,0 +1,9 @@ +category = "fail" + +# check: x: u64 = 5 + 5 +# nextln: $()Could not evaluate initializer to a const declaration. +# nextln: y: u64 = 5 + 5 + +# check: x: u64 = 5 + 5 +# nextln: y: u64 = 5 + 5 +# nextln: $()Could not evaluate initializer to a const declaration. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/predicate_calls_impure/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/predicate_calls_impure/test.toml new file mode 100644 index 00000000000..821441f552a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/predicate_calls_impure/test.toml @@ -0,0 +1,9 @@ +category = "fail" + +# check: #[storage(read,write)] +# nextln: fn foo() { +# nextln: $()Impure function inside of non-contract. Contract storage is only accessible from contracts. + +# check: #[storage(read, write)] +# nextln: fn main() -> bool { +# nextln: $()Impure function inside of non-contract. Contract storage is only accessible from contracts. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/test.toml new file mode 100644 index 00000000000..d571364518f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/primitive_type_argument/test.toml @@ -0,0 +1,7 @@ +category = "fail" + +# check: let b = foo::baz::ExampleStruct:: { a_field: 5u64 }; +# nextln: $()Mismatched types. +# nextln: $()expected: bool +# nextln: $()found: u64. +# nextln: $()help: Struct field's type must match up with the type specified in its declaration. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/pure_calls_impure/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/pure_calls_impure/test.toml new file mode 100644 index 00000000000..03b6bb19cd1 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/pure_calls_impure/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: impure_function(); +# nextln: $()Storage attribute access mismatch. Try giving the surrounding function more access by adding "#[storage(write)]" to the function declaration. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/recursive_calls/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/recursive_calls/test.toml new file mode 100644 index 00000000000..a8f5ea67fc5 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/recursive_calls/test.toml @@ -0,0 +1,8 @@ +category = "fail" + +# check: $()Function a is recursive, which is unsupported at this time. +# check: $()Function b is recursive via c, which is unsupported at this time. +# check: $()Function c is recursive via b, which is unsupported at this time. +# check: $()Function d is recursive via e and f, which is unsupported at this time. +# check: $()Function e is recursive via f and d, which is unsupported at this time. +# check: $()Function f is recursive via d and e, which is unsupported at this time. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/recursive_enum/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/recursive_enum/test.toml new file mode 100644 index 00000000000..bd9a57f4949 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/recursive_enum/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +# check: $()recursive types are not supported diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/recursive_struct/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/recursive_struct/test.toml new file mode 100644 index 00000000000..bd9a57f4949 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/recursive_struct/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +# check: $()recursive types are not supported diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/recursive_type_chain/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/recursive_type_chain/test.toml new file mode 100644 index 00000000000..144ac44e200 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/recursive_type_chain/test.toml @@ -0,0 +1,12 @@ +category = "fail" + +# check: $()Type F is recursive via G and E, which is unsupported at this time. +# check: $()Type G is recursive via E and F, which is unsupported at this time. +# check: $()Type E is recursive via F and G, which is unsupported at this time. +# check: $()Type I is recursive via H, which is unsupported at this time. +# check: $()Type H is recursive via I, which is unsupported at this time. +# check: $()Type T is recursive via S, which is unsupported at this time. +# check: $()Type S is recursive via T, which is unsupported at this time. +# check: $()Type Y is recursive via Z and X, which is unsupported at this time. +# check: $()Type Z is recursive via X and Y, which is unsupported at this time. +# check: $()Type X is recursive via Y and Z, which is unsupported at this time. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/repeated_enum_variant/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/repeated_enum_variant/test.toml new file mode 100644 index 00000000000..2a730600ada --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/repeated_enum_variant/test.toml @@ -0,0 +1,10 @@ +category = "fail" + +# check: A: u64, +# nextln: A: b256, +# nextln: $()enum variant "A" already declared +# nextln: A: str[4], + +# check: A: b256, +# nextln: A: str[4], +# nextln: $()enum variant "A" already declared diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/repeated_storage_field/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/repeated_storage_field/test.toml new file mode 100644 index 00000000000..10152bc4100 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/repeated_storage_field/test.toml @@ -0,0 +1,10 @@ +category = "fail" + +# check: x: u64, +# nextln: x: b256, +# nextln: $()storage field "x" already declared +# nextln: x: str[4], + +# check: x: b256, +# nextln: x: str[4], +# nextln: $()storage field "x" already declared diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/repeated_struct_field/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/repeated_struct_field/test.toml new file mode 100644 index 00000000000..30361780425 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/repeated_struct_field/test.toml @@ -0,0 +1,9 @@ +category = "fail" + +# check: x: b256, +# nextln: $()struct field "x" already declared +# nextln: x: str[4], + +# check: x: b256, +# nextln: x: str[4], +# nextln: $()struct field "x" already declared diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/reserved_identifiers/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/reserved_identifiers/Forc.lock new file mode 100644 index 00000000000..d85ecc65184 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/reserved_identifiers/Forc.lock @@ -0,0 +1,4 @@ +[[package]] +name = 'reserved_identifiers' +source = 'root' +dependencies = [] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/reserved_identifiers/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/reserved_identifiers/Forc.toml new file mode 100644 index 00000000000..a5c653793d8 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/reserved_identifiers/Forc.toml @@ -0,0 +1,6 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "reserved_identifiers" +entry = "main.sw" +implicit-std = false diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/reserved_identifiers/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/reserved_identifiers/src/main.sw new file mode 100644 index 00000000000..00f81f2f5a9 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/reserved_identifiers/src/main.sw @@ -0,0 +1,39 @@ +script; + +// this should fail with an `Identifiers cannot be a reserved keyword.` error + +fn main() { + let mut script = 0; + let mut contract = 0; + let mut predicate = 0; + let mut library = 0; + let mut dep = 0; + let mut pub = 0; + let mut use = 0; + let mut as = 0; + let mut struct = 0; + let mut enum = 0; + let mut self = 0; + let mut fn = 0; + let mut trait = 0; + let mut impl = 0; + let mut for = 0; + let mut abi = 0; + let mut const = 0; + let mut storage = 0; + let mut str = 0; + let mut asm = 0; + let mut return = 0; + let mut if = 0; + let mut else = 0; + let mut match = 0; + let mut mut = 0; + let mut let = 0; + let mut while = 0; + let mut where = 0; + let mut ref = 0; + let mut deref = 0; + let mut true = 0; + let mut false = 0; +} + diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/reserved_identifiers/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/reserved_identifiers/test.toml new file mode 100644 index 00000000000..139bed5c848 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/reserved_identifiers/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: let mut script = 0; +# nextln: $()Identifiers cannot be a reserved keyword. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/script_calls_impure/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/script_calls_impure/test.toml new file mode 100644 index 00000000000..6f476a5773d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/script_calls_impure/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: fn main() {} +# nextln: $()Impure function inside of non-contract. Contract storage is only accessible from contracts. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/shadow_import/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/shadow_import/test.toml new file mode 100644 index 00000000000..c7089fb2650 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/shadow_import/test.toml @@ -0,0 +1,6 @@ +category = "fail" + +# check: $()The name "Foo" shadows another symbol with the same name. +# check: $()The name "Bar2" shadows another symbol with the same name. +# check: $()The name "Bar1" imported through `*` shadows another symbol with the same name. +# check: $()The name "Bar2" imported through `*` shadows another symbol with the same name. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/star_import_alias/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/star_import_alias/src/main.sw index 02c9c0381e9..53184b8514f 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/star_import_alias/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_fail/star_import_alias/src/main.sw @@ -3,7 +3,7 @@ script; dep bar; // This should not compile but `use ::bar::*;` should -use ::bar::{* as all); +use ::bar::{* as all}; fn main() -> bool { false diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/star_import_alias/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/star_import_alias/test.toml new file mode 100644 index 00000000000..9cfcdfdfcb2 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/star_import_alias/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: use ::bar::{* as all}; +# nextln: $()Expected `,`. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/storage_conflict/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/storage_conflict/test.toml new file mode 100644 index 00000000000..1e3038ea24f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/storage_conflict/test.toml @@ -0,0 +1,49 @@ +category = "fail" + +# check: storage_conflict/src/main.sw:103:4 +# check: $()This function performs a storage read but does not have the required attribute(s). Try adding "#[storage(read)]" to the function declaration. + +# check: storage_conflict/src/main.sw:46:4 +# check: $()This function performs a storage read but does not have the required attribute(s). Try adding "#[storage(read)]" to the function declaration. + +# check: storage_conflict/src/main.sw:33:4 +# check: $()This function performs a storage read but does not have the required attribute(s). Try adding "#[storage(read)]" to the function declaration. + +# check: storage_conflict/src/main.sw:13:5 +# check: $()This function performs a storage read but does not have the required attribute(s). Try adding "#[storage(read)]" to the function declaration. + +# check: storage_conflict/src/main.sw:112:4 +# check: $()This function performs a storage read but does not have the required attribute(s). Try adding "#[storage(read)]" to the function declaration. + +# check: storage_conflict/src/main.sw:57:4 +# check: $()This function performs a storage read but does not have the required attribute(s). Try adding "#[storage(read)]" to the function declaration. + +# check: storage_conflict/src/main.sw:53:4 +# check: $()This function performs a storage read but does not have the required attribute(s). Try adding "#[storage(read)]" to the function declaration. + +# check: storage_conflict/src/main.sw:16:5 +# check: $()This function performs a storage read but does not have the required attribute(s). Try adding "#[storage(read)]" to the function declaration. + +# check: storage_conflict/src/main.sw:123:4 +# check: $()This function performs a storage write but does not have the required attribute(s). Try adding "#[storage(write)]" to the function declaration. + +# check: storage_conflict/src/main.sw:71:4 +# check: $()This function performs a storage write but does not have the required attribute(s). Try adding "#[storage(write)]" to the function declaration. + +# check: storage_conflict/src/main.sw:64:4 +# check: $()This function performs a storage write but does not have the required attribute(s). Try adding "#[storage(write)]" to the function declaration. + +# check: storage_conflict/src/main.sw:19:5 +# check: $()This function performs a storage write but does not have the required attribute(s). Try adding "#[storage(write)]" to the function declaration. + +# check: storage_conflict/src/main.sw:129:4 +# check: $()This function performs a storage write but does not have the required attribute(s). Try adding "#[storage(write)]" to the function declaration. + +# check: storage_conflict/src/main.sw:87:4 +# check: $()This function performs a storage write but does not have the required attribute(s). Try adding "#[storage(write)]" to the function declaration. + +# check: storage_conflict/src/main.sw:82:4 +# check: $()This function performs a storage write but does not have the required attribute(s). Try adding "#[storage(write)]" to the function declaration. + +# check: storage_conflict/src/main.sw:22:5 +# check: $()This function performs a storage write but does not have the required attribute(s). Try adding "#[storage(write)]" to the function declaration. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/storage_in_library/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/storage_in_library/test.toml new file mode 100644 index 00000000000..7c702fd78b3 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/storage_in_library/test.toml @@ -0,0 +1,9 @@ +category = "fail" + +# Skip the first warning. +# check: storage { + +# check: storage { +# nextln: item: u64, +# nextln: } +# nextln: $()Declaring storage in a library is not allowed. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/storage_in_predicate/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/storage_in_predicate/test.toml new file mode 100644 index 00000000000..35eb83848ea --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/storage_in_predicate/test.toml @@ -0,0 +1,9 @@ +category = "fail" + +# Skip the warning. +# check: storage { + +# check: storage { +# nextln: item: u64, +# nextln: } +# nextln: $()Declaring storage in a predicate is not allowed. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/storage_in_script/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/storage_in_script/test.toml new file mode 100644 index 00000000000..31e74c5d3a9 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/storage_in_script/test.toml @@ -0,0 +1,9 @@ +category = "fail" + +# Skip the warning. +# check: storage { + +# check: storage { +# nextln: item: u64, +# nextln: } +# nextln: $()Declaring storage in a script is not allowed. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/storage_ops_in_library/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/storage_ops_in_library/test.toml new file mode 100644 index 00000000000..118b1b04091 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/storage_ops_in_library/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: do_storage::side_effects(); +# nextln: $()Storage attribute access mismatch. Try giving the surrounding function more access by adding "#[storage(read, write)]" to the function declaration. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/supertrait_does_not_exist/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/supertrait_does_not_exist/test.toml new file mode 100644 index 00000000000..80361299483 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/supertrait_does_not_exist/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +# check: $()Could not find symbol "C" in this scope. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/top_level_vars/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/top_level_vars/test.toml new file mode 100644 index 00000000000..2fe9a82d29e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/top_level_vars/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: let mut forbidden = true; +# nextln: $()Expected an item. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_impl_purity_mismatch/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/trait_impl_purity_mismatch/test.toml new file mode 100644 index 00000000000..b637b2660de --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_impl_purity_mismatch/test.toml @@ -0,0 +1,3 @@ +category = "fail" + +# check: $()Storage attribute access mismatch. The trait function "f" in trait "A" requires the storage attribute(s) #[storage(read)]. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_method_signature_mismatch/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/trait_method_signature_mismatch/test.toml new file mode 100644 index 00000000000..7e008750474 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_method_signature_mismatch/test.toml @@ -0,0 +1,5 @@ +category = "fail" + +# check: fn foo(s: str[7]) -> str[7] { +# nextln: $()Expected: u64 +# nextln: $()found: str[7]. The definition of this function must match the one in the trait declaration. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/trait_pure_calls_impure/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/trait_pure_calls_impure/test.toml new file mode 100644 index 00000000000..4df8d76a952 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/trait_pure_calls_impure/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: self.f() +# nextln: $()Storage attribute access mismatch. Try giving the surrounding function more access by adding "#[storage(read)]" to the function declaration. diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/type_mismatch_error_message/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/type_mismatch_error_message/test.toml new file mode 100644 index 00000000000..4ebee880ac8 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/type_mismatch_error_message/test.toml @@ -0,0 +1,4 @@ +category = "fail" + +# check: foo.does_not_exist(); +# nextln: $()No method named "does_not_exist" found for type "Result, str[4]>". diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/unify_identical_unknowns/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/unify_identical_unknowns/test.toml new file mode 100644 index 00000000000..a7fd835cbe0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/unify_identical_unknowns/test.toml @@ -0,0 +1,8 @@ +category = "fail" + +# check: impl Foo for NonExistant { +# nextln: $()Could not find symbol "NonExistant" in this scope. + +# check: impl Foo for NonExistant { +# nextln: $()Unknown type name "NonExistant". + diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/vec/.gitignore b/test/src/e2e_vm_tests/test_programs/should_fail/vec/.gitignore new file mode 100644 index 00000000000..77d3844f58c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/vec/.gitignore @@ -0,0 +1,2 @@ +out +target diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/vec/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/vec/Forc.lock new file mode 100644 index 00000000000..3916e287d8f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/vec/Forc.lock @@ -0,0 +1,14 @@ +[[package]] +name = 'core' +source = 'path+from-root-E8B33641BFB485A7' +dependencies = [] + +[[package]] +name = 'std' +source = 'path+from-root-E8B33641BFB485A7' +dependencies = ['core'] + +[[package]] +name = 'vec' +source = 'root' +dependencies = ['std'] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/vec/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/vec/Forc.toml new file mode 100644 index 00000000000..13a336e9b6a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/vec/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "vec" + +[dependencies] +std = { path = "../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/vec/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/vec/src/main.sw new file mode 100644 index 00000000000..f8ce94627b0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/vec/src/main.sw @@ -0,0 +1,113 @@ +script; + +use std::{assert::assert, hash::sha256, option::Option, revert::revert, vec::Vec}; + +struct SimpleStruct { + x: u32, + y: b256, +} + +enum SimpleEnum { + X: (), + Y: b256, + Z: (b256, + b256), +} + +fn main() -> bool { + test_vector_new_u8(); + true +} + +fn test_vector_new_u8() { + let mut vector = ~Vec::new(); + + let number0 = 0u8; + let number1 = 1u8; + let number2 = 2u8; + let number3 = 3u8; + let number4 = 4u8; + let number5 = 5u8; + let number6 = 6u8; + let number7 = 7u8; + let number8 = 8u8; + + assert(vector.len() == 0); + assert(vector.capacity() == 0); + assert(vector.is_empty()); + + vector.push(number0); + vector.push(number1); + vector.push(number2); + vector.push(number3); + vector.push(number4); + vector.push(false); + + assert(vector.len() == 5); + assert(vector.capacity() == 8); + assert(vector.is_empty() == false); + + match vector.get(0) { + Option::Some(val) => assert(val == number0), Option::None => revert(0), + } + + // Push after get + vector.push(number5); + vector.push(number6); + vector.push(number7); + vector.push(number8); + vector.push("this should break it 1"); + + match vector.get(4) { + Option::Some(val) => assert(val == number4), Option::None => revert(0), + } + + match vector.get(number6) { + Option::Some(val) => assert(val == number6), Option::None => revert(0), + } + + assert(vector.len() == 9); + assert(vector.capacity() == 16); + assert(!vector.is_empty()); + + // Test after capacity change + match vector.get(4) { + Option::Some(val) => assert(val == number4), Option::None => revert(0), + } + + match vector.get(6) { + Option::Some(val) => assert(val == number6), Option::None => revert(0), + } + + vector.clear(); + + // Empty after clear + assert(vector.len() == 0); + assert(vector.capacity() == 16); + assert(vector.is_empty() == true); + + match vector.get(0) { + Option::Some(val) => revert(0), Option::None => (), + } + + // Make sure pushing again after clear() works + vector.push(number0); + vector.push(number1); + vector.push(number2); + vector.push(number3); + vector.push(number4); + vector.push("this should break it 2"); + + assert(vector.len() == 5); + assert(vector.capacity() == 16); + assert(vector.is_empty() == false); + + match vector.get(4) { + Option::Some(val) => assert(val == number4), Option::None => revert(0), + } + + // Out of bounds access + match vector.get(5) { + Option::Some(val) => revert(0), Option::None => (), + } +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/vec/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/vec/test.toml new file mode 100644 index 00000000000..475acca21e9 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/vec/test.toml @@ -0,0 +1,10 @@ +category = "fail" + +# check: vector.push(false); +# nextln: $()This parameter was declared as type u8, but argument of type bool was provided. + +# check: vector.push("this should break it 1"); +# nextln: $()This parameter was declared as type u8, but argument of type str[22] was provided. + +# check: vector.push("this should break it 2"); +# nextln: $()This parameter was declared as type u8, but argument of type str[22] was provided. diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/forc/dependency_package_field/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/forc/dependency_package_field/test.toml new file mode 100644 index 00000000000..44ca8ea93c4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/forc/dependency_package_field/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 0 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/forc/dependency_patching/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/forc/dependency_patching/Forc.lock new file mode 100644 index 00000000000..e77c645a332 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/forc/dependency_patching/Forc.lock @@ -0,0 +1,14 @@ +[[package]] +name = 'core' +source = 'path+from-root-B0CAE5E4D7DDF437' +dependencies = [] + +[[package]] +name = 'dependency_patching' +source = 'root' +dependencies = ['std'] + +[[package]] +name = 'std' +source = 'path+from-root-B0CAE5E4D7DDF437' +dependencies = ['core'] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/forc/dependency_patching/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/forc/dependency_patching/Forc.toml new file mode 100644 index 00000000000..81c9f831690 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/forc/dependency_patching/Forc.toml @@ -0,0 +1,10 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "dependency_patching" + +[dependencies] + +[patch.'https://github.com/fuellabs/sway'] +std = { path = "../../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/forc/dependency_patching/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/forc/dependency_patching/json_abi_oracle.json new file mode 100644 index 00000000000..905c3bd1034 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/forc/dependency_patching/json_abi_oracle.json @@ -0,0 +1,14 @@ +[ + { + "inputs": [], + "name": "main", + "outputs": [ + { + "components": null, + "name": "", + "type": "u64" + } + ], + "type": "function" + } +] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/forc/dependency_patching/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/forc/dependency_patching/src/main.sw new file mode 100644 index 00000000000..d0db44707ba --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/forc/dependency_patching/src/main.sw @@ -0,0 +1,5 @@ +script; + +fn main() -> u64 { + 0 +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/forc/dependency_patching/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/forc/dependency_patching/test.toml new file mode 100644 index 00000000000..6a5d9de5715 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/forc/dependency_patching/test.toml @@ -0,0 +1 @@ +category = "compile" \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/abort_control_flow/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/abort_control_flow/test.toml new file mode 100644 index 00000000000..83885a3255b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/abort_control_flow/test.toml @@ -0,0 +1 @@ +category = "disabled" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/abort_control_flow_good/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/abort_control_flow_good/test.toml new file mode 100644 index 00000000000..665debb3bf8 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/abort_control_flow_good/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "revert", value = 42 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/aliased_imports/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/aliased_imports/test.toml new file mode 100644 index 00000000000..b7a79f9a8c0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/aliased_imports/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 42 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/array_basics/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/array_basics/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/array_basics/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/array_dynamic_oob/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/array_dynamic_oob/test.toml new file mode 100644 index 00000000000..83885a3255b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/array_dynamic_oob/test.toml @@ -0,0 +1 @@ +category = "disabled" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/array_generics/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/array_generics/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/array_generics/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/asm_expr_basic/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm_expr_basic/test.toml new file mode 100644 index 00000000000..d8186a90d45 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm_expr_basic/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 6 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/asm_without_return/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm_without_return/test.toml new file mode 100644 index 00000000000..44ca8ea93c4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/asm_without_return/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 0 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/b256_bad_jumps/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/b256_bad_jumps/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/b256_bad_jumps/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/b256_bitwise_ops/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/b256_bitwise_ops/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/b256_bitwise_ops/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/b256_ops/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/b256_ops/test.toml new file mode 100644 index 00000000000..2297098e50e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/b256_ops/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 100 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/basic_func_decl/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/basic_func_decl/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/basic_func_decl/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/basic_predicate/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/basic_predicate/test.toml new file mode 100644 index 00000000000..bb916e5c72e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/basic_predicate/test.toml @@ -0,0 +1,2 @@ +category = "run" +expected_result = { action = "return", value = 1 } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/binary_and_hex_literals/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/binary_and_hex_literals/test.toml new file mode 100644 index 00000000000..83885a3255b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/binary_and_hex_literals/test.toml @@ -0,0 +1 @@ +category = "disabled" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/bool_and_or/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/bool_and_or/test.toml new file mode 100644 index 00000000000..b7a79f9a8c0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/bool_and_or/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 42 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/builtin_type_method_call/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/builtin_type_method_call/test.toml new file mode 100644 index 00000000000..a9e475bbda3 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/builtin_type_method_call/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 3 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/chained_if_let/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/chained_if_let/test.toml new file mode 100644 index 00000000000..5338339c8cb --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/chained_if_let/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 5 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/const_decl/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/const_decl/test.toml new file mode 100644 index 00000000000..2297098e50e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/const_decl/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 100 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/const_decl_in_library/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/const_decl_in_library/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/const_decl_in_library/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/const_inits/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/const_inits/src/main.sw index d4119b8f76d..ed15138eea7 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/const_inits/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/const_inits/src/main.sw @@ -36,17 +36,22 @@ const EN1c = En1::NoVal; const ETH_ID0_VALUE = ETH_ID0.value; const TUP1_idx2 = TUP1.2; +const INT1 = 1; + fn main() -> u64 { + const int1 = 1; + assert(int1 == INT1); + // initialization through function applications. - let eth_id0 = ~ContractId::from(0x0000000000000000000000000000000000000000000000000000000000000000); - let eth_id1 = ~ContractId::from(0x0000000000000000000000000000000000000000000000000000000000000001); + const eth_id0 = ~ContractId::from(0x0000000000000000000000000000000000000000000000000000000000000000); + const eth_id1 = ~ContractId::from(0x0000000000000000000000000000000000000000000000000000000000000001); assert(eth_id0 == ETH_ID0 && eth_id1 == ETH_ID1); // tuples and arrays. - let t1 = (2, 1, 21); + const t1 = (2, 1, 21); assert(t1.0 == TUP1.0 && t1.1 == TUP1.1 && t1.2 == TUP1.2); assert(t1.0 == TUP2.0 && t1.1 == TUP2.1 && t1.2 == TUP2.2); - let a1 = [1, 2, 3]; + const a1 = [1, 2, 3]; assert(a1[0] == ARR1[0] && a1[1] == ARR1[1] && a1[2] == ARR1[2]); assert(a1[0] == ARR2[0] && a1[1] == ARR2[1] && a1[2] == ARR2[2]); diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/const_inits/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/const_inits/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/const_inits/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/contract_caller_as_type/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/contract_caller_as_type/test.toml new file mode 100644 index 00000000000..b7a79f9a8c0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/contract_caller_as_type/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 42 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/dependencies/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/dependencies/test.toml new file mode 100644 index 00000000000..44ca8ea93c4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/dependencies/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 0 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/doc_strings/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/doc_strings/test.toml new file mode 100644 index 00000000000..83885a3255b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/doc_strings/test.toml @@ -0,0 +1 @@ +category = "disabled" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/empty_method_initializer/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/empty_method_initializer/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/empty_method_initializer/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_destructuring/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_destructuring/test.toml new file mode 100644 index 00000000000..be3b58f100f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_destructuring/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 15 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_if_let/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_if_let/test.toml new file mode 100644 index 00000000000..365860c79fa --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_if_let/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 143 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_if_let_large_type/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_if_let_large_type/test.toml new file mode 100644 index 00000000000..be3b58f100f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_if_let_large_type/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 15 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/test.toml new file mode 100644 index 00000000000..9820628e6d0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_in_fn_decl/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 255 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_init_fn_call/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_init_fn_call/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_init_fn_call/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_padding/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_padding/test.toml new file mode 100644 index 00000000000..37ba1bb3194 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_padding/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return_data", value = [ 0xce, 0x55, 0xff, 0x05, 0x11, 0x3a, 0x24, 0x2e, 0xc7, 0x9a, 0x23, 0x75, 0x0c, 0x7e, 0x2b, 0xab, 0xaf, 0x98, 0xa8, 0xdc, 0x41, 0x66, 0x90, 0xc8, 0x57, 0xdd, 0x31, 0x72, 0x0c, 0x74, 0x82, 0xb6 ] } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_type_inference/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_type_inference/test.toml new file mode 100644 index 00000000000..5338339c8cb --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/enum_type_inference/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 5 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_and_neq/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_and_neq/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_and_neq/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/Forc.lock new file mode 100644 index 00000000000..63ae381216c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/Forc.lock @@ -0,0 +1,14 @@ +[[package]] +name = 'core' +source = 'path+from-root-987CA0C143EB9BCC' +dependencies = [] + +[[package]] +name = 'eq_intrinsic' +source = 'root' +dependencies = ['std'] + +[[package]] +name = 'std' +source = 'path+from-root-987CA0C143EB9BCC' +dependencies = ['core'] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/Forc.toml new file mode 100644 index 00000000000..9d341be72f6 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "eq_intrinsic" + +[dependencies] +std = { path = "../../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/json_abi_oracle.json new file mode 100644 index 00000000000..905c3bd1034 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/json_abi_oracle.json @@ -0,0 +1,14 @@ +[ + { + "inputs": [], + "name": "main", + "outputs": [ + { + "components": null, + "name": "", + "type": "u64" + } + ], + "type": "function" + } +] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/src/main.sw new file mode 100644 index 00000000000..9a81fd329d6 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/src/main.sw @@ -0,0 +1,39 @@ +script; + +use std::assert::assert; + +fn main() -> u64 { + + assert(__eq(true, true) == (true == true)); + assert(__eq(true, false) != (true != false)); + assert(__eq(true, true) == (true != false)); + + assert(__eq(1, 22) == (1 == 22)); + assert(__eq(1, 1) == (1 == 1)); + + let a: u8 = 1; + let b: u8 = 22; + let c: u8 = 1; + assert(__eq(a, b) == (a == b)); + assert(__eq(a, c) == (a == c)); + + let a: u16 = 1; + let b: u16 = 22; + let c: u16 = 1; + assert(__eq(a, b) == (a == b)); + assert(__eq(a, c) == (a == c)); + + let a: u32 = 1; + let b: u32 = 22; + let c: u32 = 1; + assert(__eq(a, b) == (a == b)); + assert(__eq(a, c) == (a == c)); + + let a: u64 = 1; + let b: u64 = 22; + let c: u64 = 1; + assert(__eq(a, b) == (a == b)); + assert(__eq(a, c) == (a == c)); + + 2 +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/test.toml new file mode 100644 index 00000000000..5617bb5d0a9 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/eq_intrinsic/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 2 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/fix_opcode_bug/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/fix_opcode_bug/test.toml new file mode 100644 index 00000000000..1cb4e3ba6f8 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/fix_opcode_bug/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 30 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/funcs_with_generic_types/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/funcs_with_generic_types/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/funcs_with_generic_types/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/generic-type-inference/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic-type-inference/test.toml new file mode 100644 index 00000000000..44ca8ea93c4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic-type-inference/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 0 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_enum/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_enum/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_enum/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_functions/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_functions/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_functions/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_impl_self/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_impl_self/test.toml new file mode 100644 index 00000000000..1a37cf3ee4d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_impl_self/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 10 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_inside_generic/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_inside_generic/test.toml new file mode 100644 index 00000000000..b87306a6d45 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_inside_generic/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 7 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_struct/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_struct/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_struct/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_structs/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_structs/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/generic_structs/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/if_elseif_enum/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/if_elseif_enum/test.toml new file mode 100644 index 00000000000..1a37cf3ee4d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/if_elseif_enum/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 10 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/if_implicit_unit/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/if_implicit_unit/test.toml new file mode 100644 index 00000000000..44ca8ea93c4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/if_implicit_unit/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 0 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/implicit_return/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/implicit_return/test.toml new file mode 100644 index 00000000000..b7a79f9a8c0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/implicit_return/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 42 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_method_from_other_file/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_method_from_other_file/test.toml new file mode 100644 index 00000000000..1a37cf3ee4d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_method_from_other_file/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 10 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/import_trailing_comma/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_trailing_comma/test.toml new file mode 100644 index 00000000000..44ca8ea93c4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/import_trailing_comma/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 0 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/impure_ifs/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/impure_ifs/Forc.lock new file mode 100644 index 00000000000..be0b990d571 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/impure_ifs/Forc.lock @@ -0,0 +1,14 @@ +[[package]] +name = 'const_inits' +source = 'root' +dependencies = ['std'] + +[[package]] +name = 'core' +source = 'path+from-root-76FD265A93458D61' +dependencies = [] + +[[package]] +name = 'std' +source = 'path+from-root-76FD265A93458D61' +dependencies = ['core'] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/impure_ifs/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/impure_ifs/Forc.toml new file mode 100644 index 00000000000..cc629f01013 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/impure_ifs/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "const_inits" + +[dependencies] +std = { path = "../../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/impure_ifs/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/impure_ifs/json_abi_oracle.json new file mode 100644 index 00000000000..905c3bd1034 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/impure_ifs/json_abi_oracle.json @@ -0,0 +1,14 @@ +[ + { + "inputs": [], + "name": "main", + "outputs": [ + { + "components": null, + "name": "", + "type": "u64" + } + ], + "type": "function" + } +] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/impure_ifs/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/impure_ifs/src/main.sw new file mode 100644 index 00000000000..c2e2e8126f3 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/impure_ifs/src/main.sw @@ -0,0 +1,90 @@ +script; + +use std::{assert::assert, logging::log}; + +enum Bool { + True: (), + False: (), +} + +fn foo(b: bool) -> u64 { + if b { + 101 + } else { + 102 + } +} + +fn bar(b: bool) -> u64 { + if b { + return 101; + } else { + return 102; + } +} + +fn bell(b: bool) -> u64 { + if b { + return 101; + } else { + 102 + } +} + +fn moo(b: bool) -> u64 { + if b { + 101 + } else { + return 102; + } +} + +fn poo(b: Bool) -> u64 { + if let Bool::True = b { + 101 + } else { + return 102; + } +} + +fn ran_out_of_names(b: Bool) -> u64 { + if let Bool::True = b { + return 101; + } else { + return 102; + } +} + +fn another_fn(b: Bool) -> u64 { + if let Bool::True = b { + return 101; + } else { + 102 + } +} + +fn thats_all(b: Bool) -> u64 { + if let Bool::True = b { + 101 + } else { + 102 + } +} + +fn main() -> u64 { + assert(foo(true) == bar(true)); + assert(foo(false) == bar(false)); + assert(foo(true) == bell(true)); + assert(foo(false) == bell(false)); + assert(foo(true) == moo(true)); + assert(foo(false) == moo(false)); + + assert(thats_all(Bool::True) == poo(Bool::True)); + assert(thats_all(Bool::False) == poo(Bool::False)); + assert(thats_all(Bool::True) == ran_out_of_names(Bool::True)); + assert(thats_all(Bool::False) == ran_out_of_names(Bool::False)); + assert(thats_all(Bool::True) == another_fn(Bool::True)); + assert(thats_all(Bool::False) == another_fn(Bool::False)); + + 2 +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/impure_ifs/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/impure_ifs/test.toml new file mode 100644 index 00000000000..5617bb5d0a9 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/impure_ifs/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 2 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/inline_if_expr_const/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/inline_if_expr_const/test.toml new file mode 100644 index 00000000000..44ca8ea93c4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/inline_if_expr_const/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 0 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/is_prime/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/is_prime/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/is_prime/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/is_reference_type/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/is_reference_type/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/is_reference_type/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/local_impl_for_ord/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/local_impl_for_ord/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/local_impl_for_ord/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/main_returns_unit/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/main_returns_unit/test.toml new file mode 100644 index 00000000000..44ca8ea93c4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/main_returns_unit/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 0 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/many_stack_variables/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/many_stack_variables/test.toml new file mode 100644 index 00000000000..1a37cf3ee4d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/many_stack_variables/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 10 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions/test.toml new file mode 100644 index 00000000000..b7a79f9a8c0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 42 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_enums/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_enums/test.toml new file mode 100644 index 00000000000..b7a79f9a8c0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_enums/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 42 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_inside_generic_functions/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_inside_generic_functions/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_inside_generic_functions/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_mismatched/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_mismatched/test.toml new file mode 100644 index 00000000000..5338339c8cb --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_mismatched/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 5 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_nested/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_nested/test.toml new file mode 100644 index 00000000000..2e6705c76d8 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_nested/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 123 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_rest/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_rest/Forc.lock new file mode 100644 index 00000000000..589d3e975fc --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_rest/Forc.lock @@ -0,0 +1,17 @@ +[[package]] +name = 'core' +source = 'path+from-root-E1D1F5F82094B853' +dependencies = [] + +[[package]] +name = 'match_expressions_rest' +source = 'root' +dependencies = [ + 'core', + 'std', +] + +[[package]] +name = 'std' +source = 'path+from-root-E1D1F5F82094B853' +dependencies = ['core'] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_rest/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_rest/Forc.toml new file mode 100644 index 00000000000..03981c9887e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_rest/Forc.toml @@ -0,0 +1,10 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "match_expressions_rest" +implicit-std = false + +[dependencies] +core = { path = "../../../../../../../sway-lib-core" } +std = { path = "../../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_rest/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_rest/json_abi_oracle.json new file mode 100644 index 00000000000..905c3bd1034 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_rest/json_abi_oracle.json @@ -0,0 +1,14 @@ +[ + { + "inputs": [], + "name": "main", + "outputs": [ + { + "components": null, + "name": "", + "type": "u64" + } + ], + "type": "function" + } +] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_rest/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_rest/src/main.sw new file mode 100644 index 00000000000..279e19af513 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_rest/src/main.sw @@ -0,0 +1,64 @@ +script; + +struct Point { + x: u64, + y: u64 +} + +struct Point3D { + x: u64, + y: u64, + z: u64 +} + +struct Line { + p1: Point, + p2: Point +} + +enum Kind { + Point: Point, + Point3D: Point3D, + Line: Line +} + +fn main() -> u64 { + let p = Point { + x: 1u64, + y: 2u64, + }; + + match p { + Point { x, .. } => { x }, + }; + + let p2 = Point3D { + x: 1u64, + y: 2u64, + z: 3u64, + }; + + match p2 { + Point3D { x, .. } => { x }, + }; + + let l = Line { p1: p, p2: p }; + + match l { + Line { p1, .. } => {} + } + + match l { + Line { p1: Point { .. }, p2: Point { .. } } => {} + } + + let k = Kind::Point(p); + + match k { + Kind::Point(Point { x, .. }) => {}, + Kind::Point3D(Point3D { z, .. }) => {}, + Kind::Line(Line { p1: Point { .. }, p2: Point { .. } }) => {}, + } + + 0 +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_rest/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_rest/test.toml new file mode 100644 index 00000000000..44ca8ea93c4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_rest/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 0 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_simple/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_simple/test.toml new file mode 100644 index 00000000000..b7a79f9a8c0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_simple/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 42 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_structs/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_structs/test.toml new file mode 100644 index 00000000000..25d06220ec6 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_structs/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 4 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_with_self/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_with_self/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/match_expressions_with_self/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/method_on_empty_struct/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/method_on_empty_struct/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/method_on_empty_struct/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/missing_type_parameters/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/missing_type_parameters/test.toml new file mode 100644 index 00000000000..83885a3255b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/missing_type_parameters/test.toml @@ -0,0 +1 @@ +category = "disabled" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/modulo_uint_test/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/modulo_uint_test/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/modulo_uint_test/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/multi_impl_self/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/multi_impl_self/test.toml new file mode 100644 index 00000000000..b7a79f9a8c0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/multi_impl_self/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 42 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/multi_item_import/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/multi_item_import/test.toml new file mode 100644 index 00000000000..44ca8ea93c4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/multi_item_import/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 0 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/nested_structs/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/nested_structs/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/nested_structs/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/nested_while_and_if/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/nested_while_and_if/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/nested_while_and_if/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/new_allocator_test/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/new_allocator_test/test.toml new file mode 100644 index 00000000000..b7a79f9a8c0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/new_allocator_test/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 42 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/non_literal_const_decl/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/non_literal_const_decl/test.toml new file mode 100644 index 00000000000..b7a79f9a8c0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/non_literal_const_decl/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 42 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/numeric_constants/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/numeric_constants/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/numeric_constants/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/op_precedence/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/op_precedence/test.toml new file mode 100644 index 00000000000..44ca8ea93c4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/op_precedence/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 0 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/out_of_order_decl/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/out_of_order_decl/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/out_of_order_decl/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/test.toml new file mode 100644 index 00000000000..5338339c8cb --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/primitive_type_argument/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 5 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/raw_identifiers/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/raw_identifiers/Forc.lock new file mode 100644 index 00000000000..71928112b44 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/raw_identifiers/Forc.lock @@ -0,0 +1,9 @@ +[[package]] +name = 'core' +source = 'path+from-root-D97961F846794409' +dependencies = [] + +[[package]] +name = 'simple' +source = 'root' +dependencies = ['core'] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/raw_identifiers/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/raw_identifiers/Forc.toml new file mode 100644 index 00000000000..fada1e39e43 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/raw_identifiers/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +name = "simple" +entry = "main.sw" + +[dependencies] +core = { path = "../../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/raw_identifiers/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/raw_identifiers/json_abi_oracle.json new file mode 100644 index 00000000000..905c3bd1034 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/raw_identifiers/json_abi_oracle.json @@ -0,0 +1,14 @@ +[ + { + "inputs": [], + "name": "main", + "outputs": [ + { + "components": null, + "name": "", + "type": "u64" + } + ], + "type": "function" + } +] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/raw_identifiers/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/raw_identifiers/src/main.sw new file mode 100644 index 00000000000..9487c0610f1 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/raw_identifiers/src/main.sw @@ -0,0 +1,52 @@ +script; + +enum SomeEnum { + B: bool, +} + +fn main() -> u64 { + let mut r#script = 0; + let mut r#contract = 0; + let mut r#predicate = 0; + let mut r#library = 0; + let mut r#dep = 0; + let mut r#pub = 0; + let mut r#use = 0; + let mut r#as = 0; + let mut r#struct = 0; + let mut r#enum = 0; + let mut r#self = 0; + let mut r#fn = 0; + let mut r#trait = 0; + let mut r#impl = 0; + let mut r#for = 0; + let mut r#abi = 0; + let mut r#const = 0; + let mut r#storage = 0; + let mut r#str = 0; + let mut r#asm = 0; + let mut r#return = 0; + let mut r#if = 0; + let mut r#else = 0; + let mut r#match = 0; + let mut r#mut = 0; + let mut r#let = 0; + let mut r#while = 0; + let mut r#where = 0; + let mut r#ref = 0; + let mut r#deref = 0; + let mut r#true = 0; + let mut r#false = 0; + + let e = SomeEnum::B(false); + let v = match e { + SomeEnum::B(true) => { + 1 + }, + SomeEnum::B(false) => { + 0 + }, + }; + + 0 +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/raw_identifiers/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/raw_identifiers/test.toml new file mode 100644 index 00000000000..44ca8ea93c4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/raw_identifiers/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 0 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/reassignment_operators/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/reassignment_operators/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/reassignment_operators/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/ret_small_string/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/ret_small_string/test.toml new file mode 100644 index 00000000000..af031908100 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/ret_small_string/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return_data", value = [ 0x6a, 0x4e, 0x01, 0xe9, 0x40, 0xab, 0xc0, 0x04, 0x30, 0xfe, 0x21, 0x62, 0xed, 0x69, 0xc0, 0xe2, 0x31, 0x04, 0xf9, 0xfd, 0xa7, 0x81, 0x59, 0x09, 0x2f, 0xea, 0x8f, 0x7e, 0xcb, 0x7f, 0x6d, 0xd4 ] } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/ret_string_in_struct/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/ret_string_in_struct/test.toml new file mode 100644 index 00000000000..c731ccb8160 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/ret_string_in_struct/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return_data", value = [ 0xaa, 0x5e, 0xfc, 0xa8, 0xda, 0xaf, 0x6e, 0xe6, 0x3f, 0x44, 0x93, 0xb2, 0x88, 0xb3, 0x85, 0xd7, 0x60, 0xb8, 0xef, 0x93, 0xdc, 0x70, 0xe0, 0xfb, 0xc3, 0x06, 0xed, 0x9b, 0x67, 0x6e, 0x5f, 0x13 ] } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/retd_b256/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/retd_b256/test.toml new file mode 100644 index 00000000000..4e4ff1e7d89 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/retd_b256/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return_data", value = [ 102, 104, 122, 173, 248, 98, 189, 119, 108, 143, 193, 139, 142, 159, 142, 32, 8, 151, 20, 133, 110, 226, 51, 179, 144, 42, 89, 29, 13, 95, 41, 37 ] } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/retd_small_array/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/retd_small_array/test.toml new file mode 100644 index 00000000000..3f5cbab52bc --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/retd_small_array/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return_data", value = [ 0xcd, 0x26, 0x62, 0x15, 0x4e, 0x6d, 0x76, 0xb2, 0xb2, 0xb9, 0x2e, 0x70, 0xc0, 0xca, 0xc3, 0xcc, 0xf5, 0x34, 0xf9, 0xb7, 0x4e, 0xb5, 0xb8, 0x98, 0x19, 0xec, 0x50, 0x90, 0x83, 0xd0, 0x0a, 0x50 ] } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/retd_struct/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/retd_struct/test.toml new file mode 100644 index 00000000000..52c540afc9e --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/retd_struct/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return_data", value = [ 251, 57, 24, 241, 63, 94, 17, 102, 252, 182, 8, 110, 140, 105, 102, 105, 138, 202, 155, 39, 97, 32, 94, 129, 141, 144, 190, 142, 33, 32, 33, 75 ] } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/retd_zero_len_array/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/retd_zero_len_array/test.toml new file mode 100644 index 00000000000..36374311dfd --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/retd_zero_len_array/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return_data", value = [ 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 ] } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/self_impl_reassignment/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/self_impl_reassignment/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/self_impl_reassignment/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/size_of/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/size_of/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/size_of/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/storage_declaration/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/storage_declaration/test.toml new file mode 100644 index 00000000000..83885a3255b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/storage_declaration/test.toml @@ -0,0 +1 @@ +category = "disabled" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/struct_field_access/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/struct_field_access/test.toml new file mode 100644 index 00000000000..652f09bfa94 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/struct_field_access/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 43 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/struct_field_reassignment/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/struct_field_reassignment/test.toml new file mode 100644 index 00000000000..44ca8ea93c4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/struct_field_reassignment/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 0 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/supertraits/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/supertraits/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/supertraits/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_import_with_star/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_import_with_star/test.toml new file mode 100644 index 00000000000..44ca8ea93c4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_import_with_star/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 0 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_override_bug/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_override_bug/test.toml new file mode 100644 index 00000000000..b87306a6d45 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/trait_override_bug/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 7 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_access/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_access/test.toml new file mode 100644 index 00000000000..b7a79f9a8c0 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_access/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 42 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_desugaring/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_desugaring/test.toml new file mode 100644 index 00000000000..a0f62749753 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_desugaring/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 9 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_field_reassignment/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_field_reassignment/test.toml new file mode 100644 index 00000000000..83bfb15bf1d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_field_reassignment/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 320 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_in_struct/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_in_struct/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_in_struct/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_indexing/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_indexing/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_indexing/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_single_element/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_single_element/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_single_element/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_types/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_types/test.toml new file mode 100644 index 00000000000..2e6705c76d8 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/tuple_types/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 123 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/u64_ops/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/u64_ops/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/u64_ops/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/unary_not_basic/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/unary_not_basic/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/unary_not_basic/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/unary_not_basic_2/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/unary_not_basic_2/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/unary_not_basic_2/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/unit_type_variants/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/unit_type_variants/test.toml new file mode 100644 index 00000000000..28d8c97b878 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/unit_type_variants/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return_data", value = [ 0xcd, 0x04, 0xa4, 0x75, 0x44, 0x98, 0xe0, 0x6d, 0xb5, 0xa1, 0x3c, 0x5f, 0x37, 0x1f, 0x1f, 0x04, 0xff, 0x6d, 0x24, 0x70, 0xf2, 0x4a, 0xa9, 0xbd, 0x88, 0x65, 0x40, 0xe5, 0xdc, 0xe7, 0x7f, 0x70 ] } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/use_full_path_names/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/use_full_path_names/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/use_full_path_names/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/valid_impurity/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/valid_impurity/test.toml new file mode 100644 index 00000000000..6691150d9c8 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/valid_impurity/test.toml @@ -0,0 +1,2 @@ +category = "compile" +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/while_loops/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/while_loops/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/while_loops/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/xos_opcode/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/xos_opcode/test.toml new file mode 100644 index 00000000000..83885a3255b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/xos_opcode/test.toml @@ -0,0 +1 @@ +category = "disabled" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/zero_field_types/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/zero_field_types/test.toml new file mode 100644 index 00000000000..1a37cf3ee4d --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/zero_field_types/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 10 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/array_of_structs_caller/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/array_of_structs_caller/test.toml new file mode 100644 index 00000000000..853d71ea545 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/array_of_structs_caller/test.toml @@ -0,0 +1,3 @@ +category = "run_on_node" +expected_result = { action = "result", value = 1 } +contracts = ["should_pass/test_contracts/array_of_structs_contract"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/bal_opcode/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/bal_opcode/test.toml new file mode 100644 index 00000000000..044cb74c7da --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/bal_opcode/test.toml @@ -0,0 +1,3 @@ +category = "run_on_node" +expected_result = { action = "result", value = 1 } +contracts = ["should_pass/test_contracts/balance_test_contract"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_abi_with_tuples/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_abi_with_tuples/test.toml new file mode 100644 index 00000000000..af526e80a7f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_abi_with_tuples/test.toml @@ -0,0 +1,3 @@ +category = "run_on_node" +expected_result = { action = "result", value = 1 } +contracts = ["should_pass/test_contracts/abi_with_tuples_contract"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/test.toml new file mode 100644 index 00000000000..6afc375bd5a --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/test.toml @@ -0,0 +1,3 @@ +category = "run_on_node" +expected_result = { action = "result", value = 4242 } +contracts = ["should_pass/test_contracts/basic_storage"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_increment_contract/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_increment_contract/test.toml new file mode 100644 index 00000000000..4e73dff4cd8 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_increment_contract/test.toml @@ -0,0 +1,3 @@ +category = "run_on_node" +expected_result = { action = "result", value = 1 } +contracts = ["should_pass/test_contracts/increment_contract"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/caller_auth_test/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/caller_auth_test/test.toml new file mode 100644 index 00000000000..258d74ac5fa --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/caller_auth_test/test.toml @@ -0,0 +1,3 @@ +category = "run_on_node" +expected_result = { action = "result", value = 1 } +contracts = ["should_pass/test_contracts/auth_testing_contract"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/caller_context_test/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/caller_context_test/src/main.sw index 8e44f68dbb1..dbfeb032b9b 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/caller_context_test/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/caller_context_test/src/main.sw @@ -8,13 +8,13 @@ fn main() -> bool { let gas: u64 = 1000; let amount: u64 = 11; let other_contract_id = ~ContractId::from(0x189a69c7ffc261ec84769563bdde047e592a33456acc079f189332c2837cba6b); - let base_asset_id = ~ContractId::from(BASE_ASSET_ID); + let base_asset_id = BASE_ASSET_ID; let test_contract = abi(ContextTesting, other_contract_id.into()); // test Context::contract_id(): let returned_contract_id = test_contract.get_id { - gas: gas, coins: 0, asset_id: BASE_ASSET_ID + gas: gas, coins: 0, asset_id: BASE_ASSET_ID.into() } (); assert(returned_contract_id.into() == other_contract_id.into()); @@ -22,14 +22,14 @@ fn main() -> bool { // @todo set up a test contract to mint some tokens for testing balances. // test Context::this_balance(): let returned_this_balance = test_contract.get_this_balance { - gas: gas, coins: 0, asset_id: BASE_ASSET_ID + gas: gas, coins: 0, asset_id: BASE_ASSET_ID.into() } (base_asset_id); assert(returned_this_balance == 0); // test Context::balance_of_contract(): let returned_contract_balance = test_contract.get_balance_of_contract { - gas: gas, coins: 0, asset_id: BASE_ASSET_ID + gas: gas, coins: 0, asset_id: BASE_ASSET_ID.into() } (base_asset_id, other_contract_id); assert(returned_contract_balance == 0); diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/caller_context_test/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/caller_context_test/test.toml new file mode 100644 index 00000000000..093bb1be264 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/caller_context_test/test.toml @@ -0,0 +1,3 @@ +category = "run_on_node" +expected_result = { action = "result", value = 1 } +contracts = ["should_pass/test_contracts/context_testing_contract"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/get_storage_key_caller/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/get_storage_key_caller/test.toml new file mode 100644 index 00000000000..559f02e1da5 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/get_storage_key_caller/test.toml @@ -0,0 +1,3 @@ +category = "run_on_node" +expected_result = { action = "result", value = 1 } +contracts = ["should_pass/test_contracts/get_storage_key_contract"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/nested_struct_args_caller/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/nested_struct_args_caller/test.toml new file mode 100644 index 00000000000..83885a3255b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/nested_struct_args_caller/test.toml @@ -0,0 +1 @@ +category = "disabled" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/storage_access_caller/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/storage_access_caller/src/main.sw index 3b04f6fd4dc..b27bdd4318b 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/storage_access_caller/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/storage_access_caller/src/main.sw @@ -1,11 +1,55 @@ script; use storage_access_abi::*; -use std::{assert::assert, hash::sha256}; +use std::{assert::assert, hash::sha256, revert::revert}; fn main() -> bool { - let contract_id = 0x41b881e842026ed4f607156ddc0f98866944c4c67478ededb48932c578ddd52c; + let contract_id = 0x1c7c76380ef43c048596b4cda60eff2763d7c081b70d8662a3e1d18a9ee1dc2b; let caller = abi(StorageAccess, contract_id); + // Test initializers + assert(caller.get_x() == 64); + assert(caller.get_y() == 0x0101010101010101010101010101010101010101010101010101010101010101); + assert(caller.get_boolean() == true); + assert(caller.get_int8() == 8); + assert(caller.get_int16() == 16); + assert(caller.get_int32() == 32); + let s = caller.get_s(); + assert(s.x == 1); + assert(s.y == 2); + assert(s.z == 0x0000000000000000000000000000000000000000000000000000000000000003); + assert(s.t.x == 4); + assert(s.t.y == 5); + assert(s.t.z == 0x0000000000000000000000000000000000000000000000000000000000000006); + assert(s.t.boolean == true); + assert(s.t.int8 == 7); + assert(s.t.int16 == 8); + assert(s.t.int32 == 9); + let e = caller.get_e(); + match e { + E::B(t) => { + assert(t.x == 1); + assert(t.y == 2); + assert(t.z == 0x0000000000000000000000000000000000000000000000000000000000000003); + assert(t.boolean == true); + assert(t.int8 == 4); + assert(t.int16 == 5); + assert(t.int32 == 6); + } + _ => { + revert(0) + } + } + let e2 = caller.get_e2(); + match e2 { + E::A(val) => { + assert(val == 777); + } + _ => { + revert(0) + } + } + assert(sha256(caller.get_string()) == sha256("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")); + // Test 1 caller.set_x(1); assert(caller.get_x() == 1); @@ -142,6 +186,7 @@ fn main() -> bool { let e = caller.get_e(); match e { E::A(val) => assert(val == 42), _ => { + revert(0) } } @@ -158,6 +203,7 @@ fn main() -> bool { assert(val.int32 == t.int32); } _ => { + revert(0) } }; diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/storage_access_caller/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/storage_access_caller/test.toml new file mode 100644 index 00000000000..c64e79bfb32 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/storage_access_caller/test.toml @@ -0,0 +1,3 @@ +category = "run_on_node" +expected_result = { action = "result", value = 1 } +contracts = ["should_pass/test_contracts/storage_access_contract"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/token_ops_test/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/token_ops_test/test.toml new file mode 100644 index 00000000000..c5bf35a1c74 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/token_ops_test/test.toml @@ -0,0 +1,3 @@ +category = "run_on_node" +expected_result = { action = "result", value = 1 } +contracts = ["should_pass/test_contracts/balance_test_contract", "should_pass/test_contracts/test_fuel_coin_contract"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/address_test/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/address_test/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/address_test/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/alloc/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/alloc/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/alloc/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/assert_test/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/assert_test/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/assert_test/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/b512_struct_alignment/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/b512_struct_alignment/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/b512_struct_alignment/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/b512_test/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/b512_test/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/b512_test/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/block_height/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/block_height/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/block_height/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/contract_id_test/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/contract_id_test/Forc.lock index 5ffb59921bf..a94f7cffeda 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/contract_id_test/Forc.lock +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/contract_id_test/Forc.lock @@ -1,13 +1,14 @@ [[package]] name = 'contract_id_test' -dependencies = ['std git+https://github.com/FuelLabs/sway-lib-std?reference=master#247270e7d8911e754a989b2fe280c462605ae2f6'] +source = 'root' +dependencies = ['std'] [[package]] name = 'core' -source = 'git+https://github.com/FuelLabs/sway-lib-core?reference=master#c331ed20ebc9d646acec6b8ee8f408627ce3b573' +source = 'path+from-root-16F6D7874F5EC2F3' dependencies = [] [[package]] name = 'std' -source = 'git+https://github.com/FuelLabs/sway-lib-std?reference=master#247270e7d8911e754a989b2fe280c462605ae2f6' -dependencies = ['core git+https://github.com/FuelLabs/sway-lib-core?reference=master#c331ed20ebc9d646acec6b8ee8f408627ce3b573'] +source = 'path+from-root-16F6D7874F5EC2F3' +dependencies = ['core'] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/contract_id_test/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/contract_id_test/test.toml new file mode 100644 index 00000000000..83885a3255b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/contract_id_test/test.toml @@ -0,0 +1 @@ +category = "disabled" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/contract_id_type/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/contract_id_type/src/main.sw index 7fd615e65ee..2d808ac7e5a 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/contract_id_type/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/contract_id_type/src/main.sw @@ -9,8 +9,8 @@ fn main() -> bool { let id_2 = ~ContractId::from(0x0000000000000000000000000000000000000000000000000000000000000000); assert(id_1 == id_2); - assert(id_1 == ~ContractId::from(BASE_ASSET_ID)); - assert(id_1.into() == BASE_ASSET_ID); + assert(id_1 == BASE_ASSET_ID); + assert(0x0000000000000000000000000000000000000000000000000000000000000000 == id_1.into()); true } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/contract_id_type/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/contract_id_type/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/contract_id_type/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/ec_recover_test/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/ec_recover_test/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/ec_recover_test/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/evm_ecr/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/evm_ecr/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/evm_ecr/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/exponentiation_test/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/exponentiation_test/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/exponentiation_test/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/ge_test/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/ge_test/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/ge_test/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/identity_eq/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/identity_eq/src/main.sw index 3fa7807463a..ea583775dc3 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/identity_eq/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/identity_eq/src/main.sw @@ -1,6 +1,6 @@ script; -use std::{address::Address, assert::assert, contract_id::ContractId, identity::*}; +use std::{address::Address, assert::assert, contract_id::ContractId, identity::Identity}; fn main() -> bool { let b1 = 0x0000000000000000000000000000000000000000000000000000000000000001; diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/identity_eq/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/identity_eq/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/identity_eq/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/intrinsics/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/intrinsics/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/intrinsics/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/mem/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/mem/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/mem/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/option/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/require/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/require/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/require/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/result/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/result/src/main.sw index 334ff86ed3b..e21600d3b12 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/result/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/result/src/main.sw @@ -1,6 +1,6 @@ script; -use std::result::*; +use std::result::Result; use std::revert::revert; fn main() -> bool { diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/result/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/result/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/result/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u128_div_test/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u128_div_test/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u128_div_test/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u128_mul_test/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u128_mul_test/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u128_mul_test/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u128_test/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u128_test/src/main.sw index 23df01a0746..f6db6ec6351 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u128_test/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u128_test/src/main.sw @@ -78,5 +78,30 @@ fn main() -> bool { assert(three_left_shift_one.upper == 0); assert(three_left_shift_one.lower == 6); + // test as_u64() + let eleven = ~U128::from(0, 11); + let unwrapped = eleven.as_u64().unwrap(); + assert(unwrapped == 11); + + let err_1 = ~U128::from(42, 11).as_u64(); + assert(match err_1 { + Result::Err(U128Error::LossOfPrecision) => { + true + }, + _ => { + false + }, + }); + + let err_1 = ~U128::from(42, 0).as_u64(); + assert(match err_1 { + Result::Err(U128Error::LossOfPrecision) => { + true + }, + _ => { + false + }, + }); + true } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u128_test/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u128_test/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u128_test/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/.gitignore b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/.gitignore new file mode 100644 index 00000000000..77d3844f58c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/.gitignore @@ -0,0 +1,2 @@ +out +target diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/Forc.lock new file mode 100644 index 00000000000..26ae4e45e26 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/Forc.lock @@ -0,0 +1,14 @@ +[[package]] +name = 'core' +source = 'path+from-root-31E9CFD36AA5AC17' +dependencies = [] + +[[package]] +name = 'std' +source = 'path+from-root-31E9CFD36AA5AC17' +dependencies = ['core'] + +[[package]] +name = 'u256_test' +source = 'root' +dependencies = ['std'] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/Forc.toml new file mode 100644 index 00000000000..b1faffa56f5 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "u256_test" + +[dependencies] +std = { path = "../../../../../../../sway-lib-std" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/json_abi_oracle.json new file mode 100644 index 00000000000..f0586b752c7 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/json_abi_oracle.json @@ -0,0 +1,14 @@ +[ + { + "inputs": [], + "name": "main", + "outputs": [ + { + "components": null, + "name": "", + "type": "bool" + } + ], + "type": "function" + } +] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/src/main.sw new file mode 100644 index 00000000000..4cfe0acfb08 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/src/main.sw @@ -0,0 +1,103 @@ +script; + +use core::num::*; +use std::{assert::assert, result::Result, u256::{U256, U256Error}}; + +fn main() -> bool { + // test new() + let new = ~U256::new(); + let empty = U256 { + a: 0, + b: 0, + c: 0, + d: 0, + }; + assert(new == empty); + + // test from() & into() + let(l, m, n, o) = new.into(); + assert(l == 0); + assert(m == 0); + assert(n == 0); + assert(o == 0); + + let a = 11; + let b = 42; + let c = 101; + let d = 69; + let x = ~U256::from(a, b, c, d); + let y = ~U256::from(a, b, c, d); + + assert(x.a == a); + assert(x.a != b); + assert(x.b == b); + assert(x.b != c); + assert(x.c == c); + assert(x.c != d); + assert(x.d == d); + assert(x.d != a); + + let(e, f, g, h) = x.into(); + assert(e == a); + assert(f == b); + assert(g == c); + assert(h == d); + + assert(x == y); + + // test min() & max() + let max = ~U256::max(); + let min = ~U256::min(); + let(one, two, three, four) = max.into(); + assert(one == ~u64::max()); + assert(two == ~u64::max()); + assert(three == ~u64::max()); + assert(four == ~u64::max()); + + let(min_1, min_2, min_3, min_4) = min.into(); + assert(min_1 == ~u64::min()); + assert(min_2 == ~u64::min()); + assert(min_3 == ~u64::min()); + assert(min_4 == ~u64::min()); + + // test as_u64() + let err_1 = ~U256::from(42, 0, 0, 11).as_u64(); + assert(match err_1 { + Result::Err(U256Error::LossOfPrecision) => { + true + }, + _ => { + false + }, + }); + + let err_2 = ~U256::from(0, 42, 0, 11).as_u64(); + assert(match err_2 { + Result::Err(U256Error::LossOfPrecision) => { + true + }, + _ => { + false + }, + }); + + let err_3 = ~U256::from(0, 0, 42, 11).as_u64(); + assert(match err_3 { + Result::Err(U256Error::LossOfPrecision) => { + true + }, + _ => { + false + }, + }); + + + let eleven = ~U256::from(0, 0, 0, 11); + let unwrapped = eleven.as_u64().unwrap(); + assert(unwrapped == 11); + + // test bits() + assert(~U256::bits() == 256u32); + + true +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/u256_test/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/vec/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/vec/src/main.sw index 6e580151b97..ea087ca93da 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/vec/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/vec/src/main.sw @@ -27,7 +27,7 @@ fn main() -> bool { } fn test_vector_new_u8() { - let mut vector: Vec = ~Vec::new::(); + let mut vector = ~Vec::new(); let number0 = 0u8; let number1 = 1u8; @@ -117,7 +117,7 @@ fn test_vector_new_u8() { } fn test_vector_new_b256() { - let mut vector: Vec = ~Vec::new::(); + let mut vector = ~Vec::new(); let b0 = 0x0000000000000000000000000000000000000000000000000000000000000000; let b1 = 0x0000000000000000000000000000000000000000000000000000000000000001; @@ -207,7 +207,7 @@ fn test_vector_new_b256() { } fn test_vector_new_struct() { - let mut vector: Vec = ~Vec::new::(); + let mut vector = ~Vec::new(); let number0 = 0u32; let number1 = 1u32; @@ -359,7 +359,7 @@ fn test_vector_new_struct() { } fn test_vector_new_enum() { - let mut vector: Vec = ~Vec::new::(); + let mut vector = ~Vec::new(); let b0 = 0x0000000000000000000000000000000000000000000000000000000000000000; let b1 = 0x0000000000000000000000000000000000000000000000000000000000000001; @@ -412,7 +412,7 @@ fn test_vector_new_enum() { } fn test_vector_new_tuple() { - let mut vector: Vec<(u16, b256)> = ~Vec::new::<(u16, b256)>(); + let mut vector = ~Vec::new(); let number0 = 0u16; let number1 = 1u16; @@ -536,7 +536,7 @@ fn test_vector_new_tuple() { } fn test_vector_new_string() { - let mut vector: Vec = ~Vec::new::(); + let mut vector = ~Vec::new(); let s0 = "fuel"; let s1 = "john"; @@ -578,9 +578,7 @@ fn test_vector_new_string() { } fn test_vector_new_array() { - let mut vector: Vec<[u64; - 3]> = ~Vec::new::<[u64; - 3]>(); + let mut vector = ~Vec::new(); let a0 = [0, 1, 2]; let a1 = [3, 4, 5]; @@ -628,7 +626,7 @@ fn test_vector_new_array() { } fn test_vector_with_capacity_u64() { - let mut vector: Vec = ~Vec::with_capacity::(8); + let mut vector = ~Vec::with_capacity(8); let number0 = 0; let number1 = 1; diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/vec/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/vec/test.toml new file mode 100644 index 00000000000..ace9e6f3186 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/stdlib/vec/test.toml @@ -0,0 +1,3 @@ +category = "run" +expected_result = { action = "return", value = 1 } +validate_abi = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/abi_with_tuples/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/abi_with_tuples/Forc.lock index ccbfdf27499..b94f93e5b57 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/abi_with_tuples/Forc.lock +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/abi_with_tuples/Forc.lock @@ -1,3 +1,9 @@ [[package]] -name = 'auth_testing_abi' +name = 'abi_with_tuples' +source = 'root' +dependencies = ['core'] + +[[package]] +name = 'core' +source = 'path+from-root-D1CDC58B8291BA8B' dependencies = [] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/storage_access_abi/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/storage_access_abi/src/main.sw index c9bb33696ee..5fae960988d 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/storage_access_abi/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_abis/storage_access_abi/src/main.sw @@ -105,6 +105,8 @@ abi StorageAccess { #[storage(read)] fn get_e() -> E; #[storage(read)] + fn get_e2() -> E; + #[storage(read)] fn get_string() -> str[40]; // Operations diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/abi_with_tuples_contract/json_storage_slots_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/abi_with_tuples_contract/json_storage_slots_oracle.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/abi_with_tuples_contract/json_storage_slots_oracle.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/abi_with_tuples_contract/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/abi_with_tuples_contract/test.toml new file mode 100644 index 00000000000..b9d57f4d5f1 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/abi_with_tuples_contract/test.toml @@ -0,0 +1,3 @@ +category = "compile" +validate_abi = true +validate_storage_slots = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/array_of_structs_contract/json_storage_slots_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/array_of_structs_contract/json_storage_slots_oracle.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/array_of_structs_contract/json_storage_slots_oracle.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/array_of_structs_contract/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/array_of_structs_contract/test.toml new file mode 100644 index 00000000000..b9d57f4d5f1 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/array_of_structs_contract/test.toml @@ -0,0 +1,3 @@ +category = "compile" +validate_abi = true +validate_storage_slots = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/auth_testing_contract/json_storage_slots_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/auth_testing_contract/json_storage_slots_oracle.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/auth_testing_contract/json_storage_slots_oracle.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/auth_testing_contract/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/auth_testing_contract/test.toml new file mode 100644 index 00000000000..b9d57f4d5f1 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/auth_testing_contract/test.toml @@ -0,0 +1,3 @@ +category = "compile" +validate_abi = true +validate_storage_slots = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/balance_test_contract/json_storage_slots_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/balance_test_contract/json_storage_slots_oracle.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/balance_test_contract/json_storage_slots_oracle.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/balance_test_contract/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/balance_test_contract/test.toml new file mode 100644 index 00000000000..b9d57f4d5f1 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/balance_test_contract/test.toml @@ -0,0 +1,3 @@ +category = "compile" +validate_abi = true +validate_storage_slots = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/json_storage_slots_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/json_storage_slots_oracle.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/json_storage_slots_oracle.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/test.toml new file mode 100644 index 00000000000..b9d57f4d5f1 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/basic_storage/test.toml @@ -0,0 +1,3 @@ +category = "compile" +validate_abi = true +validate_storage_slots = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/context_testing_contract/json_storage_slots_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/context_testing_contract/json_storage_slots_oracle.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/context_testing_contract/json_storage_slots_oracle.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/context_testing_contract/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/context_testing_contract/test.toml new file mode 100644 index 00000000000..b9d57f4d5f1 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/context_testing_contract/test.toml @@ -0,0 +1,3 @@ +category = "compile" +validate_abi = true +validate_storage_slots = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/get_storage_key_contract/json_storage_slots_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/get_storage_key_contract/json_storage_slots_oracle.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/get_storage_key_contract/json_storage_slots_oracle.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/get_storage_key_contract/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/get_storage_key_contract/test.toml new file mode 100644 index 00000000000..b9d57f4d5f1 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/get_storage_key_contract/test.toml @@ -0,0 +1,3 @@ +category = "compile" +validate_abi = true +validate_storage_slots = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/increment_contract/json_storage_slots_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/increment_contract/json_storage_slots_oracle.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/increment_contract/json_storage_slots_oracle.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/increment_contract/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/increment_contract/test.toml new file mode 100644 index 00000000000..b9d57f4d5f1 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/increment_contract/test.toml @@ -0,0 +1,3 @@ +category = "compile" +validate_abi = true +validate_storage_slots = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/json_storage_slots_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/json_storage_slots_oracle.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/json_storage_slots_oracle.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/test.toml new file mode 100644 index 00000000000..b9d57f4d5f1 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/issue_1512_repro/test.toml @@ -0,0 +1,3 @@ +category = "compile" +validate_abi = true +validate_storage_slots = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/json_storage_slots_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/json_storage_slots_oracle.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/json_storage_slots_oracle.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/test.toml new file mode 100644 index 00000000000..b9d57f4d5f1 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/multiple_impl/test.toml @@ -0,0 +1,3 @@ +category = "compile" +validate_abi = true +validate_storage_slots = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/nested_struct_args_contract/json_storage_slots_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/nested_struct_args_contract/json_storage_slots_oracle.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/nested_struct_args_contract/json_storage_slots_oracle.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/nested_struct_args_contract/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/nested_struct_args_contract/test.toml new file mode 100644 index 00000000000..b9d57f4d5f1 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/nested_struct_args_contract/test.toml @@ -0,0 +1,3 @@ +category = "compile" +validate_abi = true +validate_storage_slots = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_access_contract/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_access_contract/json_abi_oracle.json index 032838bc581..e035b1b6003 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_access_contract/json_abi_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_access_contract/json_abi_oracle.json @@ -867,6 +867,65 @@ ], "type": "function" }, + { + "inputs": [], + "name": "get_e2", + "outputs": [ + { + "components": [ + { + "components": null, + "name": "A", + "type": "u64" + }, + { + "components": [ + { + "components": null, + "name": "x", + "type": "u64" + }, + { + "components": null, + "name": "y", + "type": "u64" + }, + { + "components": null, + "name": "z", + "type": "b256" + }, + { + "components": null, + "name": "boolean", + "type": "bool" + }, + { + "components": null, + "name": "int8", + "type": "u8" + }, + { + "components": null, + "name": "int16", + "type": "u16" + }, + { + "components": null, + "name": "int32", + "type": "u32" + } + ], + "name": "B", + "type": "struct T" + } + ], + "name": "", + "type": "enum E" + } + ], + "type": "function" + }, { "inputs": [], "name": "get_string", diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_access_contract/json_storage_slots_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_access_contract/json_storage_slots_oracle.json new file mode 100644 index 00000000000..08c003c88c8 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_access_contract/json_storage_slots_oracle.json @@ -0,0 +1,106 @@ +[ + { + "key": "02dac99c283f16bc91b74f6942db7f012699a2ad51272b15207b9cc14a70dbae", + "value": "0000000000000001000000000000000000000000000000000000000000000000" + }, + { + "key": "126435532f2d2faed6fdd08cea385e77766b42cebae52c892908ba163ffd9484", + "value": "0000000000000000000000000000000000000000000000000000000000000003" + }, + { + "key": "2e92e2a58ff87833010c4cb205f65aa14fa39f799ffc3809bd4a7014b131bc93", + "value": "0000000000000001000000000000000000000000000000000000000000000000" + }, + { + "key": "57bba8e7ea11ac802968230c7c3c09485b488cc84009e80055f938a2cebeb0e6", + "value": "0000000000000001000000000000000200000000000000000000000000000000" + }, + { + "key": "57bba8e7ea11ac802968230c7c3c09485b488cc84009e80055f938a2cebeb0e7", + "value": "0000000000000000000000000000000300000000000000010000000000000004" + }, + { + "key": "57bba8e7ea11ac802968230c7c3c09485b488cc84009e80055f938a2cebeb0e8", + "value": "0000000000000005000000000000000600000000000000000000000000000000" + }, + { + "key": "6294951dcb0a9111a517be5cf4785670ff4e166fb5ab9c33b17e6881b48e964f", + "value": "0000000000000008000000000000000000000000000000000000000000000000" + }, + { + "key": "678de5d61f6f4690357e7868bca285aa8faf5bc9d2126bd8e5006a3ee54f0003", + "value": "0000000000000005000000000000000000000000000000000000000000000000" + }, + { + "key": "71c50136ce909d575b4bd2b1505b9b166ace9d514e92b0e6f9a04abfea8e649d", + "value": "0000000000000002000000000000000000000000000000000000000000000000" + }, + { + "key": "7f91d1a929dce734e7f930bbb279ccfccdb5474227502ea8845815c74bd930a7", + "value": "0000000000000020000000000000000000000000000000000000000000000000" + }, + { + "key": "94b2b70d20da552763c7614981b2a4d984380d7ed4e54c01b28c914e79e44bd5", + "value": "0000000000000010000000000000000000000000000000000000000000000000" + }, + { + "key": "9d9209c41faa2ab13bef277e30c3d85b2b95a81816d51614e5816c182ac0a66e", + "value": "0000000000000001000000000000000000000000000000000000000000000000" + }, + { + "key": "ad7293bc17e2debf737147d46a68be4c2487150275ed02c8a59c58d1452e9052", + "value": "0000000000000009000000000000000000000000000000000000000000000000" + }, + { + "key": "af75026d46742957dd1a310d00030e0c7099f07760a0f18d84a541ebf8a0c244", + "value": "0000000000000000000000000000000000000000000000000000000000000006" + }, + { + "key": "b0cfa187f470ad0b95c1e20154630783abb9019127755afbe4fb5e2382888ba6", + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "key": "b0cfa187f470ad0b95c1e20154630783abb9019127755afbe4fb5e2382888ba7", + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "key": "b0cfa187f470ad0b95c1e20154630783abb9019127755afbe4fb5e2382888ba8", + "value": "0000000000000000000000000000030900000000000000000000000000000000" + }, + { + "key": "c28432da64bb717a7d85e34191fc1e50f031b9689c808b653ef3a8328a8e1002", + "value": "0000000000000004000000000000000000000000000000000000000000000000" + }, + { + "key": "c497c29a5cb465e7baae51571dfdcd1d47dae5fb4258de06d4f395487d441468", + "value": "0000000000000007000000000000000000000000000000000000000000000000" + }, + { + "key": "c5e69153be998bc6f957aeb6f8fd46a0e9c5bc2d3dff421a73e02f64a3012fbb", + "value": "4141414141414141414141414141414141414141414141414141414141414141" + }, + { + "key": "c5e69153be998bc6f957aeb6f8fd46a0e9c5bc2d3dff421a73e02f64a3012fbc", + "value": "4141414141414141000000000000000000000000000000000000000000000000" + }, + { + "key": "d55bcd857a8d6a72e6ba8a7aacbf56161e266c2418af5c06c9d1907bbca2624b", + "value": "0000000000000001000000000000000000000000000000000000000000000000" + }, + { + "key": "de9090cb50e71c2588c773487d1da7066d0c719849a7e58dc8b6397a25c567c0", + "value": "0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "key": "e945e3646bf99ebf2a70bf0c8151349dab2a54abac6450f0ee9fad896c308871", + "value": "0000000000000008000000000000000000000000000000000000000000000000" + }, + { + "key": "ea9d1ab55216336383fedadabe3c23a4df23267279ea294a547ca1006371746f", + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "key": "f383b0ce51358be57daa3b725fe44acdb2d880604e367199080b4379c41bb6ed", + "value": "0000000000000040000000000000000000000000000000000000000000000000" + } +] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_access_contract/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_access_contract/src/main.sw index b507509cbbd..6b064d88e05 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_access_contract/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_access_contract/src/main.sw @@ -3,205 +3,184 @@ contract; use storage_access_abi::{E, S, StorageAccess, T}; storage { - x: u64, - y: b256, - s: S, - boolean: bool, - int8: u8, - int16: u16, - int32: u32, - e: E, - string: str[40], + x: u64 = 64, + y: b256 = 0x0101010101010101010101010101010101010101010101010101010101010101, + s: S = S { + x: 1, + y: 2, + z: 0x0000000000000000000000000000000000000000000000000000000000000003, + t: T { + x: 4, + y: 5, + z: 0x0000000000000000000000000000000000000000000000000000000000000006, + boolean: true, + int8: 7, + int16: 8, + int32: 9, + }, + }, + boolean: bool = true, + int8: u8 = 8, + int16: u16 = 16, + int32: u32 = 32, + e: E = E::B(T { + x: 1, + y: 2, + z: 0x0000000000000000000000000000000000000000000000000000000000000003, + boolean: true, + int8: 4, + int16: 5, + int32: 6, + }, + ), e2: E = E::A(777), + string: str[40] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", } impl StorageAccess for Contract { // Setters - #[storage(write)] - fn set_x(x: u64) { + #[storage(write)]fn set_x(x: u64) { storage.x = x; } - #[storage(write)] - fn set_y(y: b256) { + #[storage(write)]fn set_y(y: b256) { storage.y = y; } - #[storage(write)] - fn set_s(s: S) { + #[storage(write)]fn set_s(s: S) { storage.s = s; } - #[storage(write)] - fn set_boolean(boolean: bool) { + #[storage(write)]fn set_boolean(boolean: bool) { storage.boolean = boolean; } - #[storage(write)] - fn set_int8(int8: u8) { + #[storage(write)]fn set_int8(int8: u8) { storage.int8 = int8; } - #[storage(write)] - fn set_int16(int16: u16) { + #[storage(write)]fn set_int16(int16: u16) { storage.int16 = int16; } - #[storage(write)] - fn set_int32(int32: u32) { + #[storage(write)]fn set_int32(int32: u32) { storage.int32 = int32; } - #[storage(write)] - fn set_s_dot_x(x: u64) { + #[storage(write)]fn set_s_dot_x(x: u64) { storage.s.x = x; } - #[storage(write)] - fn set_s_dot_y(y: u64) { + #[storage(write)]fn set_s_dot_y(y: u64) { storage.s.y = y; } - #[storage(write)] - fn set_s_dot_z(z: b256) { + #[storage(write)]fn set_s_dot_z(z: b256) { storage.s.z = z; } - #[storage(write)] - fn set_s_dot_t(t: T) { + #[storage(write)]fn set_s_dot_t(t: T) { storage.s.t = t; } - #[storage(write)] - fn set_s_dot_t_dot_x(x: u64) { + #[storage(write)]fn set_s_dot_t_dot_x(x: u64) { storage.s.t.x = x; } - #[storage(write)] - fn set_s_dot_t_dot_y(y: u64) { + #[storage(write)]fn set_s_dot_t_dot_y(y: u64) { storage.s.t.y = y; } - #[storage(write)] - fn set_s_dot_t_dot_z(z: b256) { + #[storage(write)]fn set_s_dot_t_dot_z(z: b256) { storage.s.t.z = z; } - #[storage(write)] - fn set_s_dot_t_dot_boolean(boolean: bool) { + #[storage(write)]fn set_s_dot_t_dot_boolean(boolean: bool) { storage.s.t.boolean = boolean; } - #[storage(write)] - fn set_s_dot_t_dot_int8(int8: u8) { + #[storage(write)]fn set_s_dot_t_dot_int8(int8: u8) { storage.s.t.int8 = int8; } - #[storage(write)] - fn set_s_dot_t_dot_int16(int16: u16) { + #[storage(write)]fn set_s_dot_t_dot_int16(int16: u16) { storage.s.t.int16 = int16; } - #[storage(write)] - fn set_s_dot_t_dot_int32(int32: u32) { + #[storage(write)]fn set_s_dot_t_dot_int32(int32: u32) { storage.s.t.int32 = int32; } - #[storage(write)] - fn set_e(e: E) { + #[storage(write)]fn set_e(e: E) { storage.e = e; } - #[storage(write)] - fn set_string(string: str[40]) { + #[storage(write)]fn set_string(string: str[40]) { storage.string = string; } // Getters - #[storage(read)] - fn get_x() -> u64 { + #[storage(read)]fn get_x() -> u64 { storage.x } - #[storage(read)] - fn get_y() -> b256 { + #[storage(read)]fn get_y() -> b256 { storage.y } - #[storage(read)] - fn get_s() -> S { + #[storage(read)]fn get_s() -> S { storage.s } - #[storage(read)] - fn get_boolean() -> bool { + #[storage(read)]fn get_boolean() -> bool { storage.boolean } - #[storage(read)] - fn get_int8() -> u8 { + #[storage(read)]fn get_int8() -> u8 { storage.int8 } - #[storage(read)] - fn get_int16() -> u16 { + #[storage(read)]fn get_int16() -> u16 { storage.int16 } - #[storage(read)] - fn get_int32() -> u32 { + #[storage(read)]fn get_int32() -> u32 { storage.int32 } - #[storage(read)] - fn get_s_dot_x() -> u64 { + #[storage(read)]fn get_s_dot_x() -> u64 { storage.s.x } - #[storage(read)] - fn get_s_dot_y() -> u64 { + #[storage(read)]fn get_s_dot_y() -> u64 { storage.s.y } - #[storage(read)] - fn get_s_dot_z() -> b256 { + #[storage(read)]fn get_s_dot_z() -> b256 { storage.s.z } - #[storage(read)] - fn get_s_dot_t() -> T { + #[storage(read)]fn get_s_dot_t() -> T { storage.s.t } - #[storage(read)] - fn get_s_dot_t_dot_x() -> u64 { + #[storage(read)]fn get_s_dot_t_dot_x() -> u64 { storage.s.t.x } - #[storage(read)] - fn get_s_dot_t_dot_y() -> u64 { + #[storage(read)]fn get_s_dot_t_dot_y() -> u64 { storage.s.t.y } - #[storage(read)] - fn get_s_dot_t_dot_z() -> b256 { + #[storage(read)]fn get_s_dot_t_dot_z() -> b256 { storage.s.t.z } - #[storage(read)] - fn get_s_dot_t_dot_boolean() -> bool { + #[storage(read)]fn get_s_dot_t_dot_boolean() -> bool { storage.s.t.boolean } - #[storage(read)] - fn get_s_dot_t_dot_int8() -> u8 { + #[storage(read)]fn get_s_dot_t_dot_int8() -> u8 { storage.s.t.int8 } - #[storage(read)] - fn get_s_dot_t_dot_int16() -> u16 { + #[storage(read)]fn get_s_dot_t_dot_int16() -> u16 { storage.s.t.int16 } - #[storage(read)] - fn get_s_dot_t_dot_int32() -> u32 { + #[storage(read)]fn get_s_dot_t_dot_int32() -> u32 { storage.s.t.int32 } - #[storage(read)] - fn get_e() -> E { + #[storage(read)]fn get_e() -> E { storage.e } - #[storage(read)] - fn get_string() -> str[40] { + #[storage(read)]fn get_e2() -> E { + storage.e2 + } + #[storage(read)]fn get_string() -> str[40] { storage.string } // Operations - #[storage(read, write)] - fn add_to_s_dot_t_dot_x(k: u64) { + #[storage(read, write)]fn add_to_s_dot_t_dot_x(k: u64) { storage.s.t.x += k; } - #[storage(read, write)] - fn subtract_from_s_dot_t_dot_x(k: u64) { + #[storage(read, write)]fn subtract_from_s_dot_t_dot_x(k: u64) { storage.s.t.x -= k; } - #[storage(read, write)] - fn multiply_by_s_dot_t_dot_x(k: u64) { + #[storage(read, write)]fn multiply_by_s_dot_t_dot_x(k: u64) { storage.s.t.x *= k; } - #[storage(read, write)] - fn divide_s_dot_t_dot_x(k: u64) { + #[storage(read, write)]fn divide_s_dot_t_dot_x(k: u64) { storage.s.t.x /= k; } - #[storage(read, write)] - fn shift_left_s_dot_t_dot_x(k: u64) { + #[storage(read, write)]fn shift_left_s_dot_t_dot_x(k: u64) { storage.s.t.x <<= k; } - #[storage(read, write)] - fn shift_right_s_dot_t_dot_x(k: u64) { + #[storage(read, write)]fn shift_right_s_dot_t_dot_x(k: u64) { storage.s.t.x >>= k; } } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_access_contract/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_access_contract/test.toml new file mode 100644 index 00000000000..b9d57f4d5f1 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/storage_access_contract/test.toml @@ -0,0 +1,3 @@ +category = "compile" +validate_abi = true +validate_storage_slots = true diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/test_fuel_coin_contract/json_storage_slots_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/test_fuel_coin_contract/json_storage_slots_oracle.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/test_fuel_coin_contract/json_storage_slots_oracle.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/test_fuel_coin_contract/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/test_fuel_coin_contract/test.toml new file mode 100644 index 00000000000..b9d57f4d5f1 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/test_contracts/test_fuel_coin_contract/test.toml @@ -0,0 +1,3 @@ +category = "compile" +validate_abi = true +validate_storage_slots = true diff --git a/test/src/sdk-harness/Cargo.toml b/test/src/sdk-harness/Cargo.toml index 2d95faae92b..809b0d68a85 100644 --- a/test/src/sdk-harness/Cargo.toml +++ b/test/src/sdk-harness/Cargo.toml @@ -7,12 +7,11 @@ version = "0.0.0" [dependencies] assert_matches = "1.5.0" -fuel-core = { version = "0.8", default-features = false } -fuel-gql-client = { version = "0.8", default-features = false } +fuel-core = { version = "0.9", default-features = false } +fuel-gql-client = { version = "0.9", default-features = false } fuel-types = "0.5" -fuel-vm = "0.11" -fuels = "0.15" -fuels-abigen-macro = "0.15" +fuel-vm = "0.12" +fuels = { version = "0.16", features = ["fuel-core-lib"] } hex = "0.4.3" sha2 = "0.10" sha3 = "0.10.1" diff --git a/test/src/sdk-harness/test_artifacts/context_testing_abi/src/main.sw b/test/src/sdk-harness/test_artifacts/context_testing_abi/src/main.sw index 1b413717df9..09c82551cf8 100644 --- a/test/src/sdk-harness/test_artifacts/context_testing_abi/src/main.sw +++ b/test/src/sdk-harness/test_artifacts/context_testing_abi/src/main.sw @@ -3,7 +3,7 @@ use std::contract_id::ContractId; abi ContextTesting { fn get_this_balance(asset: ContractId) -> u64; - fn get_balance_of_contract(asset: ContractId, contract: ContractId) -> u64; + fn get_balance_of_contract(asset: ContractId, r#contract: ContractId) -> u64; fn get_amount() -> u64; fn get_asset_id() -> ContractId; fn get_gas() -> u64; diff --git a/test/src/sdk-harness/test_artifacts/methods_abi/Forc.lock b/test/src/sdk-harness/test_artifacts/methods_abi/Forc.lock new file mode 100644 index 00000000000..1fb6b0979c0 --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/methods_abi/Forc.lock @@ -0,0 +1,4 @@ +[[package]] +name = 'methods_abi' +source = 'root' +dependencies = [] diff --git a/test/src/sdk-harness/test_artifacts/methods_abi/Forc.toml b/test/src/sdk-harness/test_artifacts/methods_abi/Forc.toml new file mode 100644 index 00000000000..d4c28b1b93a --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/methods_abi/Forc.toml @@ -0,0 +1,6 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "methods_abi" +implicit-std = false diff --git a/test/src/sdk-harness/test_artifacts/methods_abi/src/main.sw b/test/src/sdk-harness/test_artifacts/methods_abi/src/main.sw new file mode 100644 index 00000000000..288efc7153f --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/methods_abi/src/main.sw @@ -0,0 +1,6 @@ +library methods_abi; + +abi MethodsContract { + #[storage(read, write)] + fn test_function() -> bool; +} diff --git a/test/src/sdk-harness/test_artifacts/methods_contract/Forc.lock b/test/src/sdk-harness/test_artifacts/methods_contract/Forc.lock new file mode 100644 index 00000000000..4faa448e08d --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/methods_contract/Forc.lock @@ -0,0 +1,22 @@ +[[package]] +name = 'core' +source = 'path+from-root-051EC93A63B1E039' +dependencies = [] + +[[package]] +name = 'methods_abi' +source = 'path+from-root-051EC93A63B1E039' +dependencies = [] + +[[package]] +name = 'methods_contract' +source = 'root' +dependencies = [ + 'methods_abi', + 'std', +] + +[[package]] +name = 'std' +source = 'path+from-root-051EC93A63B1E039' +dependencies = ['core'] diff --git a/test/src/sdk-harness/test_artifacts/methods_contract/Forc.toml b/test/src/sdk-harness/test_artifacts/methods_contract/Forc.toml new file mode 100644 index 00000000000..19f2d059625 --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/methods_contract/Forc.toml @@ -0,0 +1,9 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "methods_contract" + +[dependencies] +std = { path = "../../../../../sway-lib-std" } +methods_abi = { path = "../methods_abi" } diff --git a/test/src/sdk-harness/test_artifacts/methods_contract/src/main.sw b/test/src/sdk-harness/test_artifacts/methods_contract/src/main.sw new file mode 100644 index 00000000000..0ba065a12c1 --- /dev/null +++ b/test/src/sdk-harness/test_artifacts/methods_contract/src/main.sw @@ -0,0 +1,41 @@ +contract; + +use methods_abi::MethodsContract; + +use std::result::*; +use std::identity::*; +use std::chain::auth::*; +use std::option::*; +use std::assert::*; + +fn bogus() -> Identity { + let sender = msg_sender(); + sender.unwrap() +} + +fn bogus2() -> Identity { + msg_sender().unwrap() +} + +struct MyStruct { + int_option: Option +} + +storage { + stored_struct: MyStruct, +} + +impl MethodsContract for Contract { + #[storage(read, write)] + fn test_function() -> bool { + let identity = bogus(); + let identity2 = bogus2(); + storage.stored_struct = MyStruct { + int_option: Option::Some(99u64) + }; + let stored_struct = storage.stored_struct; + let stored_option_in_struct = stored_struct.int_option; + require(stored_option_in_struct.is_some(), "Error"); + true + } +} \ No newline at end of file diff --git a/test/src/sdk-harness/test_projects/auth/mod.rs b/test/src/sdk-harness/test_projects/auth/mod.rs index 0500b5c92b7..7e19f74650f 100644 --- a/test/src/sdk-harness/test_projects/auth/mod.rs +++ b/test/src/sdk-harness/test_projects/auth/mod.rs @@ -1,5 +1,4 @@ use fuels::{prelude::*, tx::ContractId}; -use fuels_abigen_macro::abigen; abigen!( AuthContract, diff --git a/test/src/sdk-harness/test_projects/call_frames/mod.rs b/test/src/sdk-harness/test_projects/call_frames/mod.rs index 4170b015a12..7a31354a131 100644 --- a/test/src/sdk-harness/test_projects/call_frames/mod.rs +++ b/test/src/sdk-harness/test_projects/call_frames/mod.rs @@ -1,6 +1,5 @@ use fuel_vm::consts::VM_MAX_RAM; use fuels::{prelude::*, tx::ContractId}; -use fuels_abigen_macro::abigen; abigen!( CallFramesTestContract, diff --git a/test/src/sdk-harness/test_projects/context/mod.rs b/test/src/sdk-harness/test_projects/context/mod.rs index 8d17c62c10a..c80298f5384 100644 --- a/test/src/sdk-harness/test_projects/context/mod.rs +++ b/test/src/sdk-harness/test_projects/context/mod.rs @@ -1,6 +1,5 @@ use fuel_vm::consts::VM_MAX_RAM; use fuels::{prelude::*, tx::ContractId}; -use fuels_abigen_macro::abigen; abigen!( TestContextContract, diff --git a/test/src/sdk-harness/test_projects/context/src/main.sw b/test/src/sdk-harness/test_projects/context/src/main.sw index 02595a02795..46e6e7c362c 100644 --- a/test/src/sdk-harness/test_projects/context/src/main.sw +++ b/test/src/sdk-harness/test_projects/context/src/main.sw @@ -9,8 +9,8 @@ impl ContextTesting for Contract { this_balance(asset) } - fn get_balance_of_contract(asset: ContractId, contract: ContractId) -> u64 { - balance_of(asset, contract) + fn get_balance_of_contract(asset: ContractId, r#contract: ContractId) -> u64 { + balance_of(asset, r#contract) } fn get_amount() -> u64 { diff --git a/test/src/sdk-harness/test_projects/exponentiation/mod.rs b/test/src/sdk-harness/test_projects/exponentiation/mod.rs index 1a711b7fad8..bd81097b056 100644 --- a/test/src/sdk-harness/test_projects/exponentiation/mod.rs +++ b/test/src/sdk-harness/test_projects/exponentiation/mod.rs @@ -1,7 +1,6 @@ use fuels::prelude::*; use fuels::signers::LocalWallet; use fuels::tx::ContractId; -use fuels_abigen_macro::abigen; abigen!(TestPowContract, "test_artifacts/pow/out/debug/pow-abi.json"); diff --git a/test/src/sdk-harness/test_projects/harness.rs b/test/src/sdk-harness/test_projects/harness.rs index 4469718f340..8ed5800d05c 100644 --- a/test/src/sdk-harness/test_projects/harness.rs +++ b/test/src/sdk-harness/test_projects/harness.rs @@ -6,6 +6,7 @@ mod context; mod exponentiation; mod hashing; mod logging; +mod methods; mod reentrancy; mod registers; mod script_data; diff --git a/test/src/sdk-harness/test_projects/hashing/mod.rs b/test/src/sdk-harness/test_projects/hashing/mod.rs index 3133b5e4361..3182d784827 100644 --- a/test/src/sdk-harness/test_projects/hashing/mod.rs +++ b/test/src/sdk-harness/test_projects/hashing/mod.rs @@ -1,5 +1,4 @@ use fuels::{prelude::*, tx::ContractId}; -use fuels_abigen_macro::abigen; use sha2::{Digest, Sha256}; use sha3::Keccak256; diff --git a/test/src/sdk-harness/test_projects/logging/mod.rs b/test/src/sdk-harness/test_projects/logging/mod.rs index 341435748b8..11be47c43fc 100644 --- a/test/src/sdk-harness/test_projects/logging/mod.rs +++ b/test/src/sdk-harness/test_projects/logging/mod.rs @@ -1,7 +1,7 @@ use fuel_core::service::{Config, FuelService}; use fuel_gql_client::client::FuelClient; use fuels::contract::script::Script; -use fuels::tx::{default_parameters::MAX_GAS_PER_TX, Transaction}; +use fuels::tx::{ConsensusParameters, Transaction}; use hex; #[tokio::test] @@ -12,7 +12,7 @@ async fn run_valid() { let tx = Transaction::Script { gas_price: 0, - gas_limit: MAX_GAS_PER_TX, + gas_limit: ConsensusParameters::DEFAULT.max_gas_per_tx, maturity: 0, byte_price: 0, receipts_root: Default::default(), diff --git a/test/src/sdk-harness/test_projects/methods/mod.rs b/test/src/sdk-harness/test_projects/methods/mod.rs new file mode 100644 index 00000000000..24789d6ba23 --- /dev/null +++ b/test/src/sdk-harness/test_projects/methods/mod.rs @@ -0,0 +1,27 @@ +use fuels::prelude::*; +use fuels::signers::wallet::Wallet; + +abigen!( + MethodsContract, + "test_artifacts/methods_contract/out/debug/methods_contract-abi.json", +); + +#[tokio::test] +async fn run_methods_test() { + let wallet = launch_provider_and_get_single_wallet().await; + let instance = get_methods_instance(wallet).await; + + let result = instance.test_function().call().await.unwrap(); + assert_eq!(result.value, true); +} + +async fn get_methods_instance(wallet: Wallet) -> MethodsContract { + let id = Contract::deploy( + "test_artifacts/methods_contract/out/debug/methods_contract.bin", + &wallet, + TxParameters::default(), + ) + .await + .unwrap(); + MethodsContract::new(id.to_string(), wallet) +} diff --git a/test/src/sdk-harness/test_projects/reentrancy/mod.rs b/test/src/sdk-harness/test_projects/reentrancy/mod.rs index b100f414536..4b0a14d3915 100644 --- a/test/src/sdk-harness/test_projects/reentrancy/mod.rs +++ b/test/src/sdk-harness/test_projects/reentrancy/mod.rs @@ -1,7 +1,6 @@ use fuels::prelude::*; use fuels::signers::wallet::Wallet; -use fuels::tx::{default_parameters::MAX_GAS_PER_TX, ContractId}; -use fuels_abigen_macro::abigen; +use fuels::tx::{ConsensusParameters, ContractId}; abigen!( AttackerContract, @@ -22,7 +21,12 @@ async fn can_detect_reentrancy() { let result = attacker_instance .launch_attack(target_id) .set_contracts(&[target_id]) - .tx_params(TxParameters::new(Some(0), Some(MAX_GAS_PER_TX), None, None)) + .tx_params(TxParameters::new( + Some(0), + Some(ConsensusParameters::DEFAULT.max_gas_per_tx), + None, + None, + )) .call() .await .unwrap(); diff --git a/test/src/sdk-harness/test_projects/registers/mod.rs b/test/src/sdk-harness/test_projects/registers/mod.rs index b5797a9fb6c..26500ff29a8 100644 --- a/test/src/sdk-harness/test_projects/registers/mod.rs +++ b/test/src/sdk-harness/test_projects/registers/mod.rs @@ -1,6 +1,5 @@ use fuel_vm::consts::VM_MAX_RAM; use fuels::prelude::*; -use fuels_abigen_macro::abigen; abigen!( TestRegistersContract, diff --git a/test/src/sdk-harness/test_projects/script_data/mod.rs b/test/src/sdk-harness/test_projects/script_data/mod.rs index 9377f91ead7..84df5a132dd 100644 --- a/test/src/sdk-harness/test_projects/script_data/mod.rs +++ b/test/src/sdk-harness/test_projects/script_data/mod.rs @@ -2,7 +2,7 @@ use assert_matches::assert_matches; use fuel_core::service::{Config, FuelService}; use fuel_gql_client::client::FuelClient; use fuels::contract::script::Script; -use fuels::tx::{default_parameters::MAX_GAS_PER_TX, Receipt, Transaction}; +use fuels::tx::{ConsensusParameters, Receipt, Transaction}; async fn call_script(script_data: Vec) -> Result, fuels::prelude::Error> { let bin = std::fs::read("test_projects/script_data/out/debug/script_data.bin"); @@ -11,7 +11,7 @@ async fn call_script(script_data: Vec) -> Result, fuels::prelud let tx = Transaction::Script { gas_price: 0, - gas_limit: MAX_GAS_PER_TX, + gas_limit: ConsensusParameters::DEFAULT.max_gas_per_tx, maturity: 0, byte_price: 0, receipts_root: Default::default(), diff --git a/test/src/sdk-harness/test_projects/storage/mod.rs b/test/src/sdk-harness/test_projects/storage/mod.rs index 544079f650e..97bf157bd26 100644 --- a/test/src/sdk-harness/test_projects/storage/mod.rs +++ b/test/src/sdk-harness/test_projects/storage/mod.rs @@ -1,5 +1,4 @@ use fuels::prelude::*; -use fuels_abigen_macro::abigen; abigen!( TestStorageContract, diff --git a/test/src/sdk-harness/test_projects/storage_map/mod.rs b/test/src/sdk-harness/test_projects/storage_map/mod.rs index fe72514eda2..a1dc1b1a736 100644 --- a/test/src/sdk-harness/test_projects/storage_map/mod.rs +++ b/test/src/sdk-harness/test_projects/storage_map/mod.rs @@ -1,5 +1,4 @@ use fuels::prelude::*; -use fuels_abigen_macro::abigen; abigen!( TestStorageMapContract, diff --git a/test/src/sdk-harness/test_projects/token_ops/mod.rs b/test/src/sdk-harness/test_projects/token_ops/mod.rs index e4f89152f6c..0127a116517 100644 --- a/test/src/sdk-harness/test_projects/token_ops/mod.rs +++ b/test/src/sdk-harness/test_projects/token_ops/mod.rs @@ -1,7 +1,6 @@ use fuels::prelude::*; use fuels::signers::wallet::Wallet; use fuels::tx::{AssetId, ContractId}; -use fuels_abigen_macro::abigen; abigen!( TestFuelCoinContract, diff --git a/test/src/sdk-harness/test_projects/tx_fields/mod.rs b/test/src/sdk-harness/test_projects/tx_fields/mod.rs index 23bd01aee8a..9c3b6cad763 100644 --- a/test/src/sdk-harness/test_projects/tx_fields/mod.rs +++ b/test/src/sdk-harness/test_projects/tx_fields/mod.rs @@ -1,9 +1,9 @@ use fuel_types::bytes::WORD_SIZE; -use fuel_vm::consts::VM_TX_MEMORY; +use fuel_vm::fuel_tx::ConsensusParameters; use fuels::prelude::*; use fuels::signers::wallet::Wallet; use fuels::tx::{Bytes32, ContractId}; -use fuels_abigen_macro::abigen; +use std::str::FromStr; abigen!( TxContractTest, @@ -92,7 +92,7 @@ async fn can_get_maturity() { async fn can_get_script_length() { let (contract_instance, _, _) = get_contracts().await; // TODO use programmatic script length https://github.com/FuelLabs/fuels-rs/issues/181 - let script_length = 24; + let script_length = 32; let result = contract_instance .get_tx_script_length() @@ -106,7 +106,7 @@ async fn can_get_script_length() { async fn can_get_script_data_length() { let (contract_instance, _, _) = get_contracts().await; // TODO make this programmatic. - let script_data_length = 80; + let script_data_length = 88; let result = contract_instance .get_tx_script_data_length() @@ -158,7 +158,9 @@ async fn can_get_witnesses_count() { #[tokio::test] async fn can_get_receipts_root() { let (contract_instance, _, _) = get_contracts().await; - let zero_receipts_root = Bytes32::default(); + let zero_receipts_root = + Bytes32::from_str("4be973feb50f1dabb9b2e451229135add52f9c0973c11e556fe5bce4a19df470") + .unwrap(); let result = contract_instance .get_tx_receipts_root() @@ -187,7 +189,8 @@ async fn can_get_script_start_offset() { + WORD_SIZE // Outputs size + WORD_SIZE // Witnesses size + Bytes32::LEN; // Receipts root - let script_start_offset = VM_TX_MEMORY + TRANSACTION_SCRIPT_FIXED_SIZE; + let script_start_offset = + ConsensusParameters::DEFAULT.tx_offset() + TRANSACTION_SCRIPT_FIXED_SIZE; let result = contract_instance .get_tx_script_start_offset()