From 01a821f8b902c3c57cea34ae80420c734bc210bf Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Wed, 27 Aug 2025 11:32:51 +0200 Subject: [PATCH 001/243] ci: filter for python files when verifying optimization rule formal proofs --- scripts/run_proofs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run_proofs.sh b/scripts/run_proofs.sh index db6b0e02b2..e23603e642 100755 --- a/scripts/run_proofs.sh +++ b/scripts/run_proofs.sh @@ -10,7 +10,7 @@ git fetch origin error=0 for new_proof in $(git diff origin/develop --name-only test/formal/) do - if [ -e "$new_proof" ]; then + if [[ -e "$new_proof" ]] && [[ "$new_proof" == *.py ]]; then set +e echo "Proving $new_proof..." output=$(python3 "$new_proof") From 4dbfe8ea65efcfc64c7a261a530dbd279f769878 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Wed, 27 Aug 2025 11:21:44 +0200 Subject: [PATCH 002/243] docs: update links to argotorg and ipsilon where appropriate --- .circleci/config.yml | 10 +++++----- .circleci/osx_install_dependencies.sh | 2 +- .circleci/parallel_bytecode_report.sh | 2 +- .github/workflows/welcome-external-pr.yml | 2 +- CODING_STYLE.md | 2 +- README.md | 2 +- ReleaseChecklist.md | 16 +++++++-------- SECURITY.md | 2 +- docs/050-breaking-changes.rst | 2 +- docs/060-breaking-changes.rst | 2 +- docs/070-breaking-changes.rst | 2 +- docs/080-breaking-changes.rst | 2 +- docs/bugs.rst | 4 ++-- docs/contributing.rst | 20 +++++++++---------- docs/index.rst | 2 +- docs/installing-solidity.rst | 18 ++++++++--------- docs/internals/optimizer.rst | 2 +- docs/layout-of-source-files.rst | 2 +- docs/path-resolution.rst | 4 ++-- docs/resources.rst | 4 ++-- docs/smtchecker.rst | 2 +- .../experimental/analysis/Analysis.cpp | 2 +- libsolidity/interface/ImportRemapper.h | 2 +- libsolidity/interface/Natspec.h | 2 +- .../Dockerfile.ubuntu.clang.ossfuzz | 2 +- .../buildpack-deps/Dockerfile.ubuntu2204 | 2 +- .../buildpack-deps/Dockerfile.ubuntu2404 | 2 +- .../Dockerfile.ubuntu2404.clang | 2 +- scripts/docker_deploy_manual.sh | 2 +- scripts/docs_version_pragma_check.sh | 2 +- scripts/download_ossfuzz_corpus.sh | 2 +- scripts/externalTests/common.sh | 2 +- scripts/install_evmone.ps1 | 2 +- scripts/release_ppa.sh | 8 ++++---- .../bytecode_reports_for_modified_binaries.sh | 2 +- .../docker-scripts/rebuild_tags.sh | 4 ++-- test/Common.h | 6 +++--- test/buglistTests.js | 2 +- .../input.sol | 2 +- .../test.py | 2 +- .../~deduplicator-verbatim-bug/test.sh | 2 +- .../~name_dependent_cse_bug/test.sh | 2 +- test/evmc/README.md | 8 ++++---- test/evmc/evmc.h | 2 +- test/externalTests/euler.sh | 2 +- test/externalTests/gp2.sh | 2 +- test/formal/README.md | 2 +- test/libsolidity/SolidityEndToEndTest.cpp | 2 +- test/libsolidity/SolidityOptimizer.cpp | 2 +- test/libsolidity/StandardCompiler.cpp | 6 +++--- ..._1d_array_into_2d_memory_array_element.sol | 2 +- .../asm_address_constant_regression.sol | 2 +- .../structs/copy_from_storage.sol | 2 +- .../array/copy_from_function_type.sol | 2 +- ...recursive_struct_nested_mapping_memory.sol | 2 +- .../expressionSimplifier/invalid_match.yul | 2 +- .../yulOptimizerTests/fullSuite/aztec.yul | 2 +- .../loadResolver/keccak_crash.yul | 2 +- .../tools/ossfuzz/StackReuseCodegenFuzzer.cpp | 2 +- test/tools/ossfuzz/abiV2Proto.proto | 2 +- tools/yulPhaser/README.md | 2 +- 61 files changed, 103 insertions(+), 103 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 544b6161cb..d206beb693 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -745,7 +745,7 @@ defaults: # Parameterized Job Templates # Separate compile-only runs of those external tests where a full run takes much longer. - # Also see https://github.com/ethereum/solidity/pull/14234 for why we excluded those + # Also see https://github.com/argotorg/solidity/pull/14234 for why we excluded those # external tests from the nightly jobs. - job_ems_compile_ext_colony: &job_ems_compile_ext_colony <<: *requires_b_ems @@ -1083,7 +1083,7 @@ jobs: b_ubu_static: # We temporarily keep building static release binaries on ubuntu 20.04 # to avoid glibc incompatibilities. - # See: https://github.com/ethereum/solidity/issues/13954 + # See: https://github.com/argotorg/solidity/issues/13954 # On large runs 2x faster than on medium. 3x on xlarge. <<: *base_ubuntu2404_xlarge environment: @@ -1140,7 +1140,7 @@ jobs: - run: name: Regression tests command: | - git clone https://github.com/ethereum/solidity-fuzzing-corpus /tmp/solidity-fuzzing-corpus + git clone https://github.com/argotorg/solidity-fuzzing-corpus /tmp/solidity-fuzzing-corpus mkdir -p test_results scripts/regressions.py -o test_results - store_test_results: @@ -1333,7 +1333,7 @@ jobs: <<: *base_ubuntu2404_env ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2 # Suppress CLN memory leak. - # See: https://github.com/ethereum/solidity/issues/13891 for details. + # See: https://github.com/argotorg/solidity/issues/13891 for details. LSAN_OPTIONS: suppressions=/root/project/.circleci/cln-asan.supp:print_suppressions=0 steps: - cmdline_tests @@ -1349,7 +1349,7 @@ jobs: SOLTEST_FLAGS: --no-smt ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2 # Suppress CLN memory leak. - # See: https://github.com/ethereum/solidity/issues/13891 for details. + # See: https://github.com/argotorg/solidity/issues/13891 for details. LSAN_OPTIONS: suppressions=/root/project/.circleci/cln-asan.supp steps: - soltest diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh index 3ac380e367..f47e08210c 100755 --- a/.circleci/osx_install_dependencies.sh +++ b/.circleci/osx_install_dependencies.sh @@ -112,7 +112,7 @@ then # evmone evmone_version="0.13.0" evmone_package="evmone-${evmone_version}-darwin-arm64.tar.gz" - wget "https://github.com/ethereum/evmone/releases/download/v${evmone_version}/${evmone_package}" + wget "https://github.com/ipsilon/evmone/releases/download/v${evmone_version}/${evmone_package}" validate_checksum "$evmone_package" 49fe6cc35e0e13c48ca2f29a6b85a47f7b25dcd427e14254000d3bc29cddf2a6 sudo tar xzpf "$evmone_package" -C /usr/local rm "$evmone_package" diff --git a/.circleci/parallel_bytecode_report.sh b/.circleci/parallel_bytecode_report.sh index ee9dfde9f3..0cb5b7f9be 100755 --- a/.circleci/parallel_bytecode_report.sh +++ b/.circleci/parallel_bytecode_report.sh @@ -57,7 +57,7 @@ if [[ $binary_type == native || $binary_type == "osx_intel" ]]; then --report-file "../bytecode-report-${label}-${interface}-${preset}.txt" else echo "Installing solc-js" - git clone --depth 1 https://github.com/ethereum/solc-js.git solc-js + git clone --depth 1 https://github.com/argotorg/solc-js.git solc-js cp "$binary_path" solc-js/soljson.js cd solc-js/ diff --git a/.github/workflows/welcome-external-pr.yml b/.github/workflows/welcome-external-pr.yml index ac8d1362ea..b795a36b0f 100644 --- a/.github/workflows/welcome-external-pr.yml +++ b/.github/workflows/welcome-external-pr.yml @@ -27,7 +27,7 @@ jobs: "Thank you for your contribution to the Solidity compiler! A team member will follow up shortly." "\n\n" "If you haven't read our [contributing guidelines](https://docs.soliditylang.org/en/latest/contributing.html) and our " - "[review checklist](https://github.com/ethereum/solidity/blob/develop/ReviewChecklist.md) before, " + "[review checklist](https://github.com/argotorg/solidity/blob/develop/ReviewChecklist.md) before, " "please do it now, this makes the reviewing process and accepting your contribution smoother." "\n\n" "If you have any questions or need our help, feel free to post them in the PR or talk to us directly on the " diff --git a/CODING_STYLE.md b/CODING_STYLE.md index b70126d916..0ff6e1b35e 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -46,7 +46,7 @@ foo->bar(someLongVariableName, cout << "some very long string that contains completely irrelevant text that talks about this and that and contains the words \"lorem\" and \"ipsum\"" << endl; ``` -To set indentation and tab width settings uniformly, the repository contains an [EditorConfig](https://editorconfig.org/) [`.editorconfig`](https://github.com/ethereum/solidity/blob/develop/.editorconfig) file, which describes some of the styles used and which is recognized by many IDE's and editors. +To set indentation and tab width settings uniformly, the repository contains an [EditorConfig](https://editorconfig.org/) [`.editorconfig`](https://github.com/argotorg/solidity/blob/develop/.editorconfig) file, which describes some of the styles used and which is recognized by many IDE's and editors. ## 1. Namespaces diff --git a/README.md b/README.md index 2d8e023220..884aca4846 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ Please follow the if you want to help. You can find our current feature and bug priorities for forthcoming -releases in the [projects section](https://github.com/ethereum/solidity/projects). +releases in the [projects section](https://github.com/argotorg/solidity/projects). ## Maintainers The Solidity programming language and compiler are open-source community projects governed by a core team. diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index acaabca63c..f0148cd0e6 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -1,8 +1,8 @@ ## Checklist for making a release: ### Requirements -- [ ] GitHub account with access to [solidity](https://github.com/ethereum/solidity), [solc-js](https://github.com/ethereum/solc-js), - [solc-bin](https://github.com/ethereum/solc-bin), [solidity-website](https://github.com/ethereum/solidity-website). +- [ ] GitHub account with access to [solidity](https://github.com/argotorg/solidity), [solc-js](https://github.com/argotorg/solc-js), + [solc-bin](https://github.com/argotorg/solc-bin), [solidity-website](https://github.com/argotorg/solidity-website). - [ ] DockerHub account with push rights to the [`solc` image](https://hub.docker.com/r/ethereum/solc). - [ ] Launchpad (Ubuntu One) account with a membership in the ["Ethereum" team](https://launchpad.net/~ethereum) and a gnupg key for your email in the `ethereum.org` domain (has to be version 1, gpg2 won't work). @@ -39,8 +39,8 @@ At least a day before the release: - [ ] Prepare drafts of Twitter, Reddit and Solidity Forum announcements. ### Blog Post -- [ ] Create a post on [solidity-website](https://github.com/ethereum/solidity-website/tree/main/src/posts) in the `Releases` category and explain some of the new features or concepts. -- [ ] Create a post on [solidity-website](https://github.com/ethereum/solidity-website/tree/main/src/posts) in the `Security Alerts` category in case of important bug(s). +- [ ] Create a post on [solidity-website](https://github.com/argotorg/solidity-website/tree/main/src/posts) in the `Releases` category and explain some of the new features or concepts. +- [ ] Create a post on [solidity-website](https://github.com/argotorg/solidity-website/tree/main/src/posts) in the `Security Alerts` category in case of important bug(s). - [ ] Get the posts reviewed and approved **before the release starts**. ### Changelog @@ -52,7 +52,7 @@ At least a day before the release: - [ ] Copy the changelog into the release blog post. ### Create the Release -- [ ] Create a [release on GitHub](https://github.com/ethereum/solidity/releases/new). +- [ ] Create a [release on GitHub](https://github.com/argotorg/solidity/releases/new). Set the target to the `develop` branch and the tag to the new version, e.g. `v0.8.5`. Include the following warning: `**The release is still in progress. You may see broken links and binaries may not yet be available from all sources.**`. Do not publish it yet - click the `Save draft` button instead. @@ -108,10 +108,10 @@ At least a day before the release: ### Post-release - [ ] Make sure the documentation for the new release has been published successfully. Go to the [documentation status page at ReadTheDocs](https://readthedocs.org/projects/solidity/) and verify that the new version is listed, works and is marked as default. -- [ ] Remove "still in progress" warning from the [release notes](https://github.com/ethereum/solidity/releases). -- [ ] Merge the [blog posts](https://github.com/ethereum/solidity-website/pulls) related to the release. +- [ ] Remove "still in progress" warning from the [release notes](https://github.com/argotorg/solidity/releases). +- [ ] Merge the [blog posts](https://github.com/argotorg/solidity-website/pulls) related to the release. - [ ] Create a commit to increase the version number on `develop` in `CMakeLists.txt` and add a new skeleton changelog entry. -- [ ] Update the release information section [in the source of soliditylang.org](https://github.com/ethereum/solidity-website/blob/main/src/pages/index.tsx). +- [ ] Update the release information section [in the source of soliditylang.org](https://github.com/argotorg/solidity-website/blob/main/src/pages/index.tsx). - [ ] Announce on [Twitter](https://twitter.com/solidity_lang), including links to the release and the blog post. - [ ] Announce on [Fosstodon](https://fosstodon.org/@solidity/), including links to the release and the blog post. - [ ] Share the announcement on Reddit in [`/r/ethdev`](https://reddit.com/r/ethdev/), cross-posted to [`/r/ethereum`](https://reddit.com/r/ethereum/). diff --git a/SECURITY.md b/SECURITY.md index d319b332f2..8cb2b0eb8a 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -49,4 +49,4 @@ The Solidity team maintains the following JSON-formatted lists of patched securi [1]: https://bounty.ethereum.org/ [2]: https://bounty.ethereum.org/#rules [3]: https://docs.soliditylang.org/en/develop/bugs.html -[4]: https://github.com/ethereum/solidity/blob/develop/docs/bugs_by_version.json +[4]: https://github.com/argotorg/solidity/blob/develop/docs/bugs_by_version.json diff --git a/docs/050-breaking-changes.rst b/docs/050-breaking-changes.rst index a40bbcf6e9..19f05adff2 100644 --- a/docs/050-breaking-changes.rst +++ b/docs/050-breaking-changes.rst @@ -6,7 +6,7 @@ This section highlights the main breaking changes introduced in Solidity version 0.5.0, along with the reasoning behind the changes and how to update affected code. For the full list check -`the release changelog `_. +`the release changelog `_. .. note:: Contracts compiled with Solidity v0.5.0 can still interface with contracts diff --git a/docs/060-breaking-changes.rst b/docs/060-breaking-changes.rst index e7c93057f6..c2a6ceb1ed 100644 --- a/docs/060-breaking-changes.rst +++ b/docs/060-breaking-changes.rst @@ -6,7 +6,7 @@ This section highlights the main breaking changes introduced in Solidity version 0.6.0, along with the reasoning behind the changes and how to update affected code. For the full list check -`the release changelog `_. +`the release changelog `_. Changes the Compiler Might not Warn About diff --git a/docs/070-breaking-changes.rst b/docs/070-breaking-changes.rst index 05e98a4dcc..a966b547a4 100644 --- a/docs/070-breaking-changes.rst +++ b/docs/070-breaking-changes.rst @@ -6,7 +6,7 @@ This section highlights the main breaking changes introduced in Solidity version 0.7.0, along with the reasoning behind the changes and how to update affected code. For the full list check -`the release changelog `_. +`the release changelog `_. Silent Changes of the Semantics diff --git a/docs/080-breaking-changes.rst b/docs/080-breaking-changes.rst index 9d1f3347b6..91712b33f2 100644 --- a/docs/080-breaking-changes.rst +++ b/docs/080-breaking-changes.rst @@ -5,7 +5,7 @@ Solidity v0.8.0 Breaking Changes This section highlights the main breaking changes introduced in Solidity version 0.8.0. For the full list check -`the release changelog `_. +`the release changelog `_. Silent Changes of the Semantics =============================== diff --git a/docs/bugs.rst b/docs/bugs.rst index f7f18e9a7f..0cb3cdb54e 100644 --- a/docs/bugs.rst +++ b/docs/bugs.rst @@ -8,12 +8,12 @@ List of Known Bugs Below, you can find a JSON-formatted list of some of the known security-relevant bugs in the Solidity compiler. The file itself is hosted in the `GitHub repository -`_. +`_. The list stretches back as far as version 0.3.0, bugs known to be present only in versions preceding that are not listed. There is another file called `bugs_by_version.json -`_, +`_, which can be used to check which bugs affect a specific version of the compiler. Contract source verification tools and also other tools interacting with diff --git a/docs/contributing.rst b/docs/contributing.rst index 2d5faa550b..6bf89cc920 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -8,8 +8,8 @@ In particular, we appreciate support in the following areas: * Reporting issues. * Fixing and responding to `Solidity's GitHub issues - `_, especially those tagged as - `"good first issue" `_ which are + `_, especially those tagged as + `"good first issue" `_ which are meant as introductory issues for external contributors. * Improving the documentation. * `Translating `_ the documentation into more languages. @@ -38,7 +38,7 @@ How to Report Issues ==================== To report an issue, please use the -`GitHub issues tracker `_. When +`GitHub issues tracker `_. When reporting issues, please mention the following details: * Solidity version. @@ -76,11 +76,11 @@ New features and bugfixes should be added to the ``Changelog.md`` file: please follow the style of previous entries, when applicable. Finally, please make sure you respect the `coding style -`_ +`_ for this project. Also, even though we do CI testing, please test your code and ensure that it builds locally before submitting a pull request. -We highly recommend going through our `review checklist `_ before submitting the pull request. +We highly recommend going through our `review checklist `_ before submitting the pull request. We thoroughly review every PR and will help you get it right, but there are many common problems that can be easily avoided, making the review much smoother. Thank you for your help! @@ -92,7 +92,7 @@ Prerequisites ------------- For running all compiler tests you may want to optionally install a few -dependencies (`evmone `_, +dependencies (`evmone `_, `z3 `_, `Eldarica `_, `cvc5 `). @@ -119,7 +119,7 @@ application ``soltest`` (or its wrapper ``scripts/soltest.sh``), as well as comm compilation tests. The test system automatically tries to discover the location of -the `evmone `_ for running the semantic tests. +the `evmone `_ for running the semantic tests. The ``evmone`` library must be located in the ``deps`` or ``deps/lib`` directory relative to the current working directory, to its parent or its parent's parent. Alternatively, an explicit location @@ -277,7 +277,7 @@ Command-line Tests Our suite of end-to-end command-line tests checks the behaviour of the compiler binary as a whole in various scenarios. -These tests are located in `test/cmdlineTests/ `_, +These tests are located in `test/cmdlineTests/ `_, one per subdirectory, and can be executed using the ``cmdlineTests.sh`` script. By default the script runs all available tests. @@ -547,7 +547,7 @@ The Solidity forum serves as the place to propose and discuss new language featu the early stages of ideation or modifications of existing features. As soon as proposals get more tangible, their -implementation will also be discussed in the `Solidity GitHub repository `_ +implementation will also be discussed in the `Solidity GitHub repository `_ in the form of issues. In addition to the forum and issue discussions, we regularly host language design discussion calls in which selected @@ -557,7 +557,7 @@ We are also sharing feedback surveys and other content that is relevant to langu If you want to know where the team is standing in terms of implementing new features, you can follow the implementation status in the `Solidity GitHub project `_. Issues in the design backlog need further specification and will either be discussed in a language design call or in a regular team call. You can -see the upcoming changes for the next breaking release by changing from the default branch (`develop`) to the `breaking branch `_. +see the upcoming changes for the next breaking release by changing from the default branch (`develop`) to the `breaking branch `_. For ad-hoc cases and questions, you can reach out to us via the `Solidity-dev Gitter channel `_ — a dedicated chatroom for conversations around the Solidity compiler and language development. diff --git a/docs/index.rst b/docs/index.rst index 73295404d4..77d06cd6b3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -14,7 +14,7 @@ With Solidity, you can create contracts for uses such as voting, crowdfunding, b When deploying contracts, you should use the latest released version of Solidity. Apart from exceptional cases, only the latest version receives -`security fixes `_. +`security fixes `_. Furthermore, breaking changes, as well as new features, are introduced regularly. We currently use a 0.y.z version number `to indicate this fast pace of change `_. diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 14c0be34a7..6f085539bc 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -33,7 +33,7 @@ Remix `Access Remix online `_, you do not need to install anything. If you want to use it without connection to the Internet, go to -https://github.com/ethereum/remix-live/tree/gh-pages#readme and follow the instructions on that page. +https://github.com/argotorg/remix-live/tree/gh-pages#readme and follow the instructions on that page. Remix is also a convenient option for testing nightly builds without installing multiple Solidity versions. @@ -51,7 +51,7 @@ Use ``npm`` for a convenient and portable way to install ``solcjs``, a Solidity further down this page. The :ref:`commandline-compiler` documentation assumes you are using the full-featured compiler, ``solc``. The usage of ``solcjs`` is documented inside its own -`repository `_. +`repository `_. Note: The solc-js project is derived from the C++ ``solc`` by using Emscripten, which means that both use the same compiler source code. @@ -115,7 +115,7 @@ Linux Packages ============== Binary packages of Solidity are available at -`solidity/releases `_. +`solidity/releases `_. We also have PPAs for Ubuntu, you can get the latest stable version using the following commands: @@ -256,7 +256,7 @@ Each one includes a ``list.json`` file listing the available binaries. For examp This means that: - You can find the binary in the same directory under the name - `solc-emscripten-wasm32-v0.7.4+commit.3f05b770.js `_. + `solc-emscripten-wasm32-v0.7.4+commit.3f05b770.js `_. Note that the file might be a symlink, and you will need to resolve it yourself if you are not using git to download it or your file system does not support symlinks. - The binary is also mirrored at https://binaries.soliditylang.org/emscripten-wasm32/solc-emscripten-wasm32-v0.7.4+commit.3f05b770.js. @@ -304,8 +304,8 @@ This means that: in the long-term. .. _IPFS: https://ipfs.io -.. _solc-bin: https://github.com/ethereum/solc-bin/ -.. _Solidity release page on GitHub: https://github.com/ethereum/solidity/releases +.. _solc-bin: https://github.com/argotorg/solc-bin/ +.. _Solidity release page on GitHub: https://github.com/argotorg/solidity/releases .. _sha3sum: https://github.com/maandree/sha3sum .. _keccak256() function from ethereumjs-util: https://github.com/ethereumjs/ethereumjs-util/blob/master/docs/modules/_hash_.md#const-keccak256 .. _WebAssembly builds: https://emscripten.org/docs/compiling/WebAssembly.html @@ -365,7 +365,7 @@ The following are dependencies for all builds of Solidity: Doing this is not recommended for general use but may be necessary when using a toolchain we are not testing with or trying to build an older version with newer tools. If you encounter such warnings, please consider - `reporting them `_. + `reporting them `_. Minimum Compiler Versions ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -447,7 +447,7 @@ To clone the source code, execute the following command: .. code-block:: bash - git clone --recursive https://github.com/ethereum/solidity.git + git clone --recursive https://github.com/argotorg/solidity.git cd solidity If you want to help develop Solidity, @@ -463,7 +463,7 @@ you should fork Solidity and add your personal fork as a second remote: If you want to re-build a released Solidity compiler, then please use the source tarball on the GitHub release page: - https://github.com/ethereum/solidity/releases/download/v0.X.Y/solidity_0.X.Y.tar.gz + https://github.com/argotorg/solidity/releases/download/v0.X.Y/solidity_0.X.Y.tar.gz (not the "Source code" provided by GitHub). diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index 4d02d3e181..8c737371c0 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -11,7 +11,7 @@ The Solidity compiler involves optimizations at three different levels (in order - Optimizing transformations on the Yul IR code. - Optimizations at the opcode level. -The opcode-based optimizer applies a set of `simplification rules `_ +The opcode-based optimizer applies a set of `simplification rules `_ to opcodes. It also combines equal code sets and removes unused code. The Yul-based optimizer is much more powerful, because it can work across function diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index 600f2ed0f4..e51b3ad98e 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -159,7 +159,7 @@ The :ref:`build instructions` explain how to activate this op It is activated for the Ubuntu PPA releases in most versions, but not for the Docker images, Windows binaries or the statically-built Linux binaries. It can be activated for solc-js via the -`smtCallback `_ if you have an SMT solver +`smtCallback `_ if you have an SMT solver installed locally and run solc-js via node (not via the browser). If you use ``pragma experimental SMTChecker;``, then you get additional diff --git a/docs/path-resolution.rst b/docs/path-resolution.rst index ff4930bc9d..492959bc1e 100644 --- a/docs/path-resolution.rst +++ b/docs/path-resolution.rst @@ -39,7 +39,7 @@ compilation fails. By default, the command-line compiler provides the *Host Filesystem Loader* - a rudimentary callback that interprets a source unit name as a path in the local filesystem. This callback can be disabled using the ``--no-import-callback`` command-line option. -The `JavaScript interface `_ does not provide any by default, +The `JavaScript interface `_ does not provide any by default, but one can be provided by the user. This mechanism can be used to obtain source code from locations other than the local filesystem (which may not even be accessible, e.g. when the compiler is running in a browser). @@ -83,7 +83,7 @@ The initial content of the VFS depends on how you invoke the compiler: #. **Standard JSON** When using the :ref:`Standard JSON ` API (via either the `JavaScript interface - `_ or the ``--standard-json`` command-line option) + `_ or the ``--standard-json`` command-line option) you provide input in JSON format, containing, among other things, the content of all your source files: diff --git a/docs/resources.rst b/docs/resources.rst index 43161f7d06..3ba0620f95 100644 --- a/docs/resources.rst +++ b/docs/resources.rst @@ -8,8 +8,8 @@ General Resources * `Ethereum.org Developers page `_ * `Ethereum StackExchange `_ * `Solidity website `_ -* `Solidity changelog `_ -* `Solidity codebase on GitHub `_ +* `Solidity changelog `_ +* `Solidity codebase on GitHub `_ * `Solidity language users chat `_ * `Solidity compiler developers chat `_ * `awesome-solidity `_ diff --git a/docs/smtchecker.rst b/docs/smtchecker.rst index 5de941bf27..69b620dbc3 100644 --- a/docs/smtchecker.rst +++ b/docs/smtchecker.rst @@ -828,7 +828,7 @@ option ``--model-checker-solvers {all,cvc5,eld,smtlib2,z3}`` or the JSON option - ``cvc5`` is used via its binary which must be installed in the system. Only BMC uses ``cvc5``. - ``eld`` is used via its binary which must be installed in the system. Only CHC uses ``eld``, and only if ``z3`` is not enabled. - ``smtlib2`` outputs SMT/Horn queries in the `smtlib2 `_ format. - These can be used together with the compiler's `callback mechanism `_ so that + These can be used together with the compiler's `callback mechanism `_ so that any solver binary from the system can be employed to synchronously return the results of the queries to the compiler. This can be used by both BMC and CHC depending on which solvers are called. - ``z3`` is available statically in ``soljson.js`` (from Solidity 0.6.9), that is, the JavaScript binary of the compiler. Otherwise it is used via its binary which must be installed in the system. diff --git a/libsolidity/experimental/analysis/Analysis.cpp b/libsolidity/experimental/analysis/Analysis.cpp index 441700675f..fa14d37ab6 100644 --- a/libsolidity/experimental/analysis/Analysis.cpp +++ b/libsolidity/experimental/analysis/Analysis.cpp @@ -164,7 +164,7 @@ bool Analysis::check(std::vector> const& _sour SyntaxRestrictor, TypeClassRegistration, TypeRegistration, - // TODO move after step introduced in https://github.com/ethereum/solidity/pull/14578, but before TypeInference + // TODO move after step introduced in https://github.com/argotorg/solidity/pull/14578, but before TypeInference FunctionDependencyAnalysis, TypeInference, DebugWarner diff --git a/libsolidity/interface/ImportRemapper.h b/libsolidity/interface/ImportRemapper.h index 66946d0917..45166a1ec7 100644 --- a/libsolidity/interface/ImportRemapper.h +++ b/libsolidity/interface/ImportRemapper.h @@ -63,7 +63,7 @@ class ImportRemapper static std::optional parseRemapping(std::string_view _input); private: - /// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum + /// list of path prefix remappings, e.g. mylibrary: github.com/argotorg = /usr/local/argotorg /// "context:prefix=target" std::vector m_remappings = {}; }; diff --git a/libsolidity/interface/Natspec.h b/libsolidity/interface/Natspec.h index 994f1350cb..11cc7b6222 100644 --- a/libsolidity/interface/Natspec.h +++ b/libsolidity/interface/Natspec.h @@ -73,7 +73,7 @@ class Natspec /// of a method's return notice documentation static Json extractReturnParameterDocs(std::multimap const& _tags, std::vector const& _returnParameterNames); - /// Temporary function until https://github.com/ethereum/solidity/issues/11114 is implemented. + /// Temporary function until https://github.com/argotorg/solidity/issues/11114 is implemented. /// @return all events defined in the contract and its base contracts and all events /// that are emitted during the execution of the contract, but allowing only unique signatures. /// In case of conflict between a library event and a contract one, selects the latter diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz b/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz index bf228d6176..465f2de0a1 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz @@ -143,7 +143,7 @@ RUN set -ex; \ # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.13.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.13.0" --recurse-submodules https://github.com/ipsilon/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2204 b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2204 index f66c1d8fd0..48272ed955 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2204 +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2204 @@ -94,7 +94,7 @@ FROM base AS libraries # EVMONE RUN set -ex; \ - wget -O /usr/src/evmone.tar.gz https://github.com/ethereum/evmone/releases/download/v0.13.0/evmone-0.13.0-linux-x86_64.tar.gz; \ + wget -O /usr/src/evmone.tar.gz https://github.com/ipsilon/evmone/releases/download/v0.13.0/evmone-0.13.0-linux-x86_64.tar.gz; \ test "$(sha256sum /usr/src/evmone.tar.gz)" = "94efc7fa27ff94018003ad95a62c20b17a45f027d434eb642c8e345e8d4d3cad /usr/src/evmone.tar.gz"; \ cd /usr; \ tar -xf /usr/src/evmone.tar.gz; \ diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 index fc372c709a..475d4e83cb 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 @@ -97,7 +97,7 @@ FROM base AS libraries # EVMONE RUN set -ex; \ - wget -O /usr/src/evmone.tar.gz https://github.com/ethereum/evmone/releases/download/v0.13.0/evmone-0.13.0-linux-x86_64.tar.gz; \ + wget -O /usr/src/evmone.tar.gz https://github.com/ipsilon/evmone/releases/download/v0.13.0/evmone-0.13.0-linux-x86_64.tar.gz; \ test "$(sha256sum /usr/src/evmone.tar.gz)" = "94efc7fa27ff94018003ad95a62c20b17a45f027d434eb642c8e345e8d4d3cad /usr/src/evmone.tar.gz"; \ cd /usr; \ tar -xf /usr/src/evmone.tar.gz; \ diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang index 91a31c2630..18cf813fe7 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang @@ -99,7 +99,7 @@ ENV CXX clang++ # EVMONE RUN set -ex; \ - wget -O /usr/src/evmone.tar.gz https://github.com/ethereum/evmone/releases/download/v0.13.0/evmone-0.13.0-linux-x86_64.tar.gz; \ + wget -O /usr/src/evmone.tar.gz https://github.com/ipsilon/evmone/releases/download/v0.13.0/evmone-0.13.0-linux-x86_64.tar.gz; \ test "$(sha256sum /usr/src/evmone.tar.gz)" = "94efc7fa27ff94018003ad95a62c20b17a45f027d434eb642c8e345e8d4d3cad /usr/src/evmone.tar.gz"; \ cd /usr; \ tar -xf /usr/src/evmone.tar.gz; \ diff --git a/scripts/docker_deploy_manual.sh b/scripts/docker_deploy_manual.sh index 1386bcfa17..196a3b36c3 100755 --- a/scripts/docker_deploy_manual.sh +++ b/scripts/docker_deploy_manual.sh @@ -13,7 +13,7 @@ if (( $# < 1 || $# > 3 )); then fi branch="$1" -repo_url="${2:-https://github.com/ethereum/solidity.git}" +repo_url="${2:-https://github.com/argotorg/solidity.git}" if (( $# >= 3 )); then [[ $3 == --no-push ]] || fail "Invalid flag: $3. Expected --no-push." diff --git a/scripts/docs_version_pragma_check.sh b/scripts/docs_version_pragma_check.sh index c927cfb1e8..46605539f9 100755 --- a/scripts/docs_version_pragma_check.sh +++ b/scripts/docs_version_pragma_check.sh @@ -181,7 +181,7 @@ SOLTMPDIR=$(mktemp -d) if [[ ! -f "$solc_bin" ]] then echo "Downloading release from github..." - if wget -q "https://github.com/ethereum/solidity/releases/download/v$version/solc-static-linux" >/dev/null + if wget -q "https://github.com/argotorg/solidity/releases/download/v$version/solc-static-linux" >/dev/null then mv solc-static-linux "$solc_bin" else diff --git a/scripts/download_ossfuzz_corpus.sh b/scripts/download_ossfuzz_corpus.sh index 92478caf6f..a741934a3b 100755 --- a/scripts/download_ossfuzz_corpus.sh +++ b/scripts/download_ossfuzz_corpus.sh @@ -1,4 +1,4 @@ #!/usr/bin/env sh set -e cd /tmp -git clone --depth 1 https://github.com/ethereum/solidity-fuzzing-corpus.git +git clone --depth 1 https://github.com/argotorg/solidity-fuzzing-corpus.git diff --git a/scripts/externalTests/common.sh b/scripts/externalTests/common.sh index 26f27b07ba..6e435da956 100644 --- a/scripts/externalTests/common.sh +++ b/scripts/externalTests/common.sh @@ -83,7 +83,7 @@ function setup_solc printLog "Setting up solc-js..." if [[ $solcjs_dir == "" ]]; then printLog "Cloning branch ${solcjs_branch}..." - git clone --depth 1 -b "$solcjs_branch" https://github.com/ethereum/solc-js.git "$install_dir" + git clone --depth 1 -b "$solcjs_branch" https://github.com/argotorg/solc-js.git "$install_dir" else printLog "Using local solc-js from ${solcjs_dir}..." cp -ra "$solcjs_dir" solc diff --git a/scripts/install_evmone.ps1 b/scripts/install_evmone.ps1 index 53fc7b6eee..3e2a1f819f 100644 --- a/scripts/install_evmone.ps1 +++ b/scripts/install_evmone.ps1 @@ -3,6 +3,6 @@ $ErrorActionPreference = "Stop" # Needed for Invoke-WebRequest to work via CI. $progressPreference = "silentlyContinue" -Invoke-WebRequest -URI "https://github.com/ethereum/evmone/releases/download/v0.13.0/evmone-0.13.0-windows-amd64.zip" -OutFile "evmone.zip" +Invoke-WebRequest -URI "https://github.com/ipsilon/evmone/releases/download/v0.13.0/evmone-0.13.0-windows-amd64.zip" -OutFile "evmone.zip" tar -xf evmone.zip "bin/evmone.dll" mv bin/evmone.dll deps/ diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh index a8d89040f2..6c1fd51dbf 100755 --- a/scripts/release_ppa.sh +++ b/scripts/release_ppa.sh @@ -105,7 +105,7 @@ fi ppafilesurl=https://launchpad.net/~ethereum/+archive/ubuntu/${pparepo}/+files # Fetch source -git clone --depth 2 --recursive https://github.com/ethereum/solidity.git -b "$branch" +git clone --depth 2 --recursive https://github.com/argotorg/solidity.git -b "$branch" mv solidity solc # Determine version @@ -152,8 +152,8 @@ Build-Depends: debhelper (>= 9.0.0), scons Standards-Version: 3.9.5 Homepage: https://ethereum.org -Vcs-Git: https://github.com/ethereum/solidity.git -Vcs-Browser: https://github.com/ethereum/solidity +Vcs-Git: https://github.com/argotorg/solidity.git +Vcs-Browser: https://github.com/argotorg/solidity Package: solc Architecture: any-amd64 @@ -201,7 +201,7 @@ EOF cat < debian/copyright Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: solc -Source: https://github.com/ethereum/solidity +Source: https://github.com/argotorg/solidity Files: * Copyright: 2014-2016 Ethereum diff --git a/scripts/solc-bin/bytecode_reports_for_modified_binaries.sh b/scripts/solc-bin/bytecode_reports_for_modified_binaries.sh index bd62519132..50a7f53dd8 100755 --- a/scripts/solc-bin/bytecode_reports_for_modified_binaries.sh +++ b/scripts/solc-bin/bytecode_reports_for_modified_binaries.sh @@ -109,7 +109,7 @@ export LC_ALL=C cd "$tmp_dir" -git clone https://github.com/ethereum/solc-js.git "$solcjs_dir" +git clone https://github.com/argotorg/solc-js.git "$solcjs_dir" cd "$solcjs_dir" npm install npm run build diff --git a/scripts/wasm-rebuild/docker-scripts/rebuild_tags.sh b/scripts/wasm-rebuild/docker-scripts/rebuild_tags.sh index 9ef9caaa8c..b1d816ae33 100755 --- a/scripts/wasm-rebuild/docker-scripts/rebuild_tags.sh +++ b/scripts/wasm-rebuild/docker-scripts/rebuild_tags.sh @@ -24,8 +24,8 @@ while (( "$#" )); do shift done -SOLIDITY_REPO_URL="https://github.com/ethereum/solidity" -SOLC_JS_REPO_URL="https://github.com/ethereum/solc-js" +SOLIDITY_REPO_URL="https://github.com/argotorg/solidity" +SOLC_JS_REPO_URL="https://github.com/argotorg/solc-js" SOLC_JS_BRANCH=wasmRebuildTests RELEASE_URL="https://binaries.soliditylang.org/bin" RELEASE_COMMIT_LIST_URL="$RELEASE_URL/list.txt" diff --git a/test/Common.h b/test/Common.h index e331c497a4..a8a0a4e839 100644 --- a/test/Common.h +++ b/test/Common.h @@ -39,13 +39,13 @@ namespace solidity::test #ifdef _WIN32 static constexpr auto evmoneFilename = "evmone.dll"; -static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.13.0/evmone-0.13.0-windows-amd64.zip"; +static constexpr auto evmoneDownloadLink = "https://github.com/ipsilon/evmone/releases/download/v0.13.0/evmone-0.13.0-windows-amd64.zip"; #elif defined(__APPLE__) static constexpr auto evmoneFilename = "libevmone.dylib"; -static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.13.0/evmone-0.13.0-darwin-arm64.tar.gz"; +static constexpr auto evmoneDownloadLink = "https://github.com/ipsilon/evmone/releases/download/v0.13.0/evmone-0.13.0-darwin-arm64.tar.gz"; #else static constexpr auto evmoneFilename = "libevmone.so"; -static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.13.0/evmone-0.13.0-linux-x86_64.tar.gz"; +static constexpr auto evmoneDownloadLink = "https://github.com/ipsilon/evmone/releases/download/v0.13.0/evmone-0.13.0-linux-x86_64.tar.gz"; #endif struct ConfigException: public util::Exception {}; diff --git a/test/buglistTests.js b/test/buglistTests.js index f24f0cb689..2d141776c8 100755 --- a/test/buglistTests.js +++ b/test/buglistTests.js @@ -76,7 +76,7 @@ async function checkJSONPath(name, buggy, fine) var jsonPath = bugsByName[name].check['ast-compact-json-path'] if (jsonPath !== undefined) { - var url = "http://github.com/ethereum/solidity/releases/download/v" + bugsByName[name].introduced + "/solc-static-linux" + var url = "https://github.com/argotorg/solidity/releases/download/v" + bugsByName[name].introduced + "/solc-static-linux" try { var tmpdir = await mktemp.createDir('XXXXX') var binary = tmpdir + "/solc-static-linux" diff --git a/test/cmdlineTests/yul_optimizer_steps_invalid_substitution_in_expression_simplifier/input.sol b/test/cmdlineTests/yul_optimizer_steps_invalid_substitution_in_expression_simplifier/input.sol index 72b2c88582..3a4dcef6b7 100644 --- a/test/cmdlineTests/yul_optimizer_steps_invalid_substitution_in_expression_simplifier/input.sol +++ b/test/cmdlineTests/yul_optimizer_steps_invalid_substitution_in_expression_simplifier/input.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0 -// reproducing https://github.com/ethereum/solidity/issues/16155 +// reproducing https://github.com/argotorg/solidity/issues/16155 pragma solidity ^0.8.19; diff --git a/test/cmdlineTests/~bytecode_equivalence_with_unused_contracts/test.py b/test/cmdlineTests/~bytecode_equivalence_with_unused_contracts/test.py index e15293b8ea..692fa10c0a 100755 --- a/test/cmdlineTests/~bytecode_equivalence_with_unused_contracts/test.py +++ b/test/cmdlineTests/~bytecode_equivalence_with_unused_contracts/test.py @@ -35,7 +35,7 @@ def test_bytecode_equivalence(): ) ) - # Repro for https://github.com/ethereum/solidity/issues/14829 + # Repro for https://github.com/argotorg/solidity/issues/14829 save_bytecode( Path("B.bin"), solc_bin_report( diff --git a/test/cmdlineTests/~deduplicator-verbatim-bug/test.sh b/test/cmdlineTests/~deduplicator-verbatim-bug/test.sh index 19b57e8caa..98eb041e81 100755 --- a/test/cmdlineTests/~deduplicator-verbatim-bug/test.sh +++ b/test/cmdlineTests/~deduplicator-verbatim-bug/test.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -eo pipefail -# This is a regression test against https://github.com/ethereum/solidity/issues/14640 +# This is a regression test against https://github.com/argotorg/solidity/issues/14640 # The bug caused the block deduplicator to incorrectly merge two blocks which have # verbatim items surrounded by identical opcodes. Due to the bug, the contents of # the verbatim were ignored and the blocks merged into a single one. diff --git a/test/cmdlineTests/~name_dependent_cse_bug/test.sh b/test/cmdlineTests/~name_dependent_cse_bug/test.sh index 710e82b1e7..898b36596c 100755 --- a/test/cmdlineTests/~name_dependent_cse_bug/test.sh +++ b/test/cmdlineTests/~name_dependent_cse_bug/test.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -eo pipefail -# This is a regression test against https://github.com/ethereum/solidity/issues/14494 +# This is a regression test against https://github.com/argotorg/solidity/issues/14494 # Due to the bug, a decision about which variable to use to replace a subexpression in CSE would # depend on sorting order of variable names. A variable not being used as a replacement could lead # to it being unused in general and removed by Unused Pruner. This would show up as a difference diff --git a/test/evmc/README.md b/test/evmc/README.md index 35fb11901c..c1f2f53d16 100644 --- a/test/evmc/README.md +++ b/test/evmc/README.md @@ -1,10 +1,10 @@ # EVMC -This is an import of [EVMC](https://github.com/ethereum/evmc) version [12.0.0](https://github.com/ethereum/evmc/releases/tag/v12.0.0). +This is an import of [EVMC](https://github.com/ipsilon/evmc) version [12.0.0](https://github.com/ipsilon/evmc/releases/tag/v12.0.0). Steps when upgrading: -- Copy all from [include/evmc](https://github.com/ethereum/evmc/tree/master/include/evmc) to [test/evmc](https://github.com/ethereum/solidity/tree/develop/test/evmc) +- Copy all from [include/evmc](https://github.com/ipsilon/evmc/tree/master/include/evmc) to [test/evmc](https://github.com/argotorg/solidity/tree/develop/test/evmc) - Note that you should delete (or not copy in the first place) `tooling.hpp` and `instructions.h`. -- Copy [`loader.c`](https://github.com/ethereum/evmc/blob/master/lib/loader/loader.c) to [test/evmc](https://github.com/ethereum/solidity/tree/develop/test/evmc) +- Copy [`loader.c`](https://github.com/ipsilon/evmc/blob/master/lib/loader/loader.c) to [test/evmc](https://github.com/argotorg/solidity/tree/develop/test/evmc) - `MockedAccount.storage` in `mocked_host.hpp` should be changed to a `map` from `unordered_map` as ordering is important for fuzzing. You'll also need to include ``. - See [PR #11094](https://github.com/ethereum/solidity/pull/11094) for more details. + See [PR #11094](https://github.com/argotorg/solidity/pull/11094) for more details. diff --git a/test/evmc/evmc.h b/test/evmc/evmc.h index bec847597c..3bc3996c34 100644 --- a/test/evmc/evmc.h +++ b/test/evmc/evmc.h @@ -278,7 +278,7 @@ typedef evmc_bytes32 (*evmc_get_block_hash_fn)(struct evmc_host_context* context * * @note * In case new status codes are needed, please create an issue or pull request - * in the EVMC repository (https://github.com/ethereum/evmc). + * in the EVMC repository (https://github.com/ipsilon/evmc). */ enum evmc_status_code { diff --git a/test/externalTests/euler.sh b/test/externalTests/euler.sh index 9ede1a3406..9ccfcd781c 100755 --- a/test/externalTests/euler.sh +++ b/test/externalTests/euler.sh @@ -33,7 +33,7 @@ SELECTED_PRESETS="$3" function compile_fn { npm run compile; } function test_fn { - # The default timeout of 20000 ms is too short for unoptimized code (https://github.com/ethereum/solidity/pull/12765). + # The default timeout of 20000 ms is too short for unoptimized code (https://github.com/argotorg/solidity/pull/12765). TEST_TIMEOUT=100000 npx --no hardhat --no-compile test } diff --git a/test/externalTests/gp2.sh b/test/externalTests/gp2.sh index e0f6943659..fb33482375 100755 --- a/test/externalTests/gp2.sh +++ b/test/externalTests/gp2.sh @@ -94,7 +94,7 @@ function gp2_test src/contracts/GPv2Settlement.sol # This test is not supposed to work. The compiler is supposed to enforce zero padding since - # at least 0.5.8 (see https://github.com/ethereum/solidity/pull/5815). For some reason the + # at least 0.5.8 (see https://github.com/argotorg/solidity/pull/5815). For some reason the # test worked on 0.7.6 but no longer works on 0.8.x. sed -i 's|it\(("invalid EVM transaction encoding does not change order hash"\)|it.skip\1|g' test/GPv2Signing.test.ts diff --git a/test/formal/README.md b/test/formal/README.md index 83bd79e76d..21176ee4a0 100644 --- a/test/formal/README.md +++ b/test/formal/README.md @@ -1,4 +1,4 @@ -The Solidity compiler implements several [optimization rules](https://github.com/ethereum/solidity/blob/develop/libevmasm/RuleList.h). +The Solidity compiler implements several [optimization rules](https://github.com/argotorg/solidity/blob/develop/libevmasm/RuleList.h). This directory contains an effort to formally prove the correctness of those rules in: diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 76699c2e3b..57422dbb84 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1691,7 +1691,7 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_abi) // ABI_CHECK(callContractFunction("f()"), encodeArgs(1 + 2 + 3)); //} -// Disabled until https://github.com/ethereum/solidity/issues/715 is implemented +// Disabled until https://github.com/argotorg/solidity/issues/715 is implemented //BOOST_AUTO_TEST_CASE(constant_struct) //{ // char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index d52ad6b9c4..832299baff 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -256,7 +256,7 @@ BOOST_AUTO_TEST_CASE(storage_write_in_loops) compareVersions("f(uint256)", 36); } -// Test disabled with https://github.com/ethereum/solidity/pull/762 +// Test disabled with https://github.com/argotorg/solidity/pull/762 // Information in joining branches is not retained anymore. BOOST_AUTO_TEST_CASE(retain_information_in_branches) { diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index b9e31c5e38..93a6320329 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -822,7 +822,7 @@ BOOST_AUTO_TEST_CASE(filename_with_colon) "language": "Solidity", "settings": { "outputSelection": { - "https://github.com/ethereum/solidity/blob/develop/test/compilationTests/gnosis/Tokens/StandardToken.sol": { + "https://github.com/argotorg/solidity/blob/develop/test/compilationTests/gnosis/Tokens/StandardToken.sol": { "A": [ "abi" ] @@ -830,7 +830,7 @@ BOOST_AUTO_TEST_CASE(filename_with_colon) } }, "sources": { - "https://github.com/ethereum/solidity/blob/develop/test/compilationTests/gnosis/Tokens/StandardToken.sol": { + "https://github.com/argotorg/solidity/blob/develop/test/compilationTests/gnosis/Tokens/StandardToken.sol": { "content": "contract A { }" } } @@ -838,7 +838,7 @@ BOOST_AUTO_TEST_CASE(filename_with_colon) )"; Json result = compile(input); BOOST_CHECK(containsAtMostWarnings(result)); - Json contract = getContractResult(result, "https://github.com/ethereum/solidity/blob/develop/test/compilationTests/gnosis/Tokens/StandardToken.sol", "A"); + Json contract = getContractResult(result, "https://github.com/argotorg/solidity/blob/develop/test/compilationTests/gnosis/Tokens/StandardToken.sol", "A"); BOOST_CHECK(contract.is_object()); BOOST_CHECK(contract["abi"].is_array()); BOOST_CHECK_EQUAL(util::jsonCompactPrint(contract["abi"]), "[]"); diff --git a/test/libsolidity/semanticTests/array/copying/calldata_1d_array_into_2d_memory_array_element.sol b/test/libsolidity/semanticTests/array/copying/calldata_1d_array_into_2d_memory_array_element.sol index e78d5bdd8a..babc3c7396 100644 --- a/test/libsolidity/semanticTests/array/copying/calldata_1d_array_into_2d_memory_array_element.sol +++ b/test/libsolidity/semanticTests/array/copying/calldata_1d_array_into_2d_memory_array_element.sol @@ -1,4 +1,4 @@ -// Example from https://github.com/ethereum/solidity/issues/12558 +// Example from https://github.com/argotorg/solidity/issues/12558 pragma abicoder v2; contract C { function f(uint[] calldata a) external returns (uint[][] memory) { diff --git a/test/libsolidity/semanticTests/constants/asm_address_constant_regression.sol b/test/libsolidity/semanticTests/constants/asm_address_constant_regression.sol index ae405d8f36..9e256ad6ba 100644 --- a/test/libsolidity/semanticTests/constants/asm_address_constant_regression.sol +++ b/test/libsolidity/semanticTests/constants/asm_address_constant_regression.sol @@ -1,4 +1,4 @@ -// Test for regression of https://github.com/ethereum/solidity/issues/8406 +// Test for regression of https://github.com/argotorg/solidity/issues/8406 contract C { address constant e = 0x1212121212121212121212121000002134593163; diff --git a/test/libsolidity/semanticTests/structs/copy_from_storage.sol b/test/libsolidity/semanticTests/structs/copy_from_storage.sol index b3d7304534..ab66e91df2 100644 --- a/test/libsolidity/semanticTests/structs/copy_from_storage.sol +++ b/test/libsolidity/semanticTests/structs/copy_from_storage.sol @@ -1,5 +1,5 @@ pragma abicoder v2; -// Example from https://github.com/ethereum/solidity/issues/12558 +// Example from https://github.com/argotorg/solidity/issues/12558 struct S { uint x; } diff --git a/test/libsolidity/syntaxTests/array/copy_from_function_type.sol b/test/libsolidity/syntaxTests/array/copy_from_function_type.sol index b795094683..2ac295d35d 100644 --- a/test/libsolidity/syntaxTests/array/copy_from_function_type.sol +++ b/test/libsolidity/syntaxTests/array/copy_from_function_type.sol @@ -1,4 +1,4 @@ -// Example from https://github.com/ethereum/solidity/issues/12558 +// Example from https://github.com/argotorg/solidity/issues/12558 pragma abicoder v2; contract C { function() external[1][] s0; diff --git a/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_memory.sol b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_memory.sol index c8708e3e0d..8351a8e00c 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_memory.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_nested_mapping_memory.sol @@ -2,7 +2,7 @@ library a { struct b { mapping (uint => b) c ; } - // Segfaults in https://github.com/ethereum/solidity/issues/9443 + // Segfaults in https://github.com/argotorg/solidity/issues/9443 function d(b memory) public {} } // ---- diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/invalid_match.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/invalid_match.yul index 28beb0383a..36c08228f6 100644 --- a/test/libyul/yulOptimizerTests/expressionSimplifier/invalid_match.yul +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/invalid_match.yul @@ -1,4 +1,4 @@ -// regression test for https://github.com/ethereum/solidity/issues/16155 +// regression test for https://github.com/argotorg/solidity/issues/16155 { function identity(value) -> ret { ret := value } diff --git a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul index 94d4b14fff..e3ac520ccd 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul @@ -12,7 +12,7 @@ * Stay tuned for updates! * * Permission to use as test case in the Solidity compiler granted by the author: - * https://github.com/ethereum/solidity/pull/5713#issuecomment-449042830 + * https://github.com/argotorg/solidity/pull/5713#issuecomment-449042830 **/ { validateJoinSplit() diff --git a/test/libyul/yulOptimizerTests/loadResolver/keccak_crash.yul b/test/libyul/yulOptimizerTests/loadResolver/keccak_crash.yul index c67a0f8755..cc00b1eaba 100644 --- a/test/libyul/yulOptimizerTests/loadResolver/keccak_crash.yul +++ b/test/libyul/yulOptimizerTests/loadResolver/keccak_crash.yul @@ -1,4 +1,4 @@ -// This test used to crash: https://github.com/ethereum/solidity/issues/11801 +// This test used to crash: https://github.com/argotorg/solidity/issues/11801 { for {} addmod(keccak256(0x0,create(0x0, 0x0, 0x0)), 0x0, 0x0) {} {} } diff --git a/test/tools/ossfuzz/StackReuseCodegenFuzzer.cpp b/test/tools/ossfuzz/StackReuseCodegenFuzzer.cpp index 71a4ff54a9..5bd5dd3590 100644 --- a/test/tools/ossfuzz/StackReuseCodegenFuzzer.cpp +++ b/test/tools/ossfuzz/StackReuseCodegenFuzzer.cpp @@ -80,7 +80,7 @@ DEFINE_PROTO_FUZZER(Program const& _input) ); std::string yul_source = converter.programToString(_input); // Do not fuzz the EVM Version field. - // See https://github.com/ethereum/solidity/issues/12590 + // See https://github.com/argotorg/solidity/issues/12590 langutil::EVMVersion version; EVMHost hostContext(version, evmone); hostContext.reset(); diff --git a/test/tools/ossfuzz/abiV2Proto.proto b/test/tools/ossfuzz/abiV2Proto.proto index ec6fe1d55e..be4cdb99c0 100644 --- a/test/tools/ossfuzz/abiV2Proto.proto +++ b/test/tools/ossfuzz/abiV2Proto.proto @@ -66,7 +66,7 @@ message NonValueType { } // TODO: Add more types -// See https://github.com/ethereum/solidity/issues/6749 +// See https://github.com/argotorg/solidity/issues/6749 message Type { oneof type_oneof { ValueType vtype = 1; diff --git a/tools/yulPhaser/README.md b/tools/yulPhaser/README.md index 99004d4c93..e7be239948 100644 --- a/tools/yulPhaser/README.md +++ b/tools/yulPhaser/README.md @@ -85,7 +85,7 @@ The difficult part is providing a fairly representative set of input files. If the files you give do not need certain optimisations the tool will find sequences that do not use these optimisations and perform badly for programs that could benefit from them. If all the provided files greatly benefit from a specific optimisation, the sequence may not work well for programs that do not. -We have conducted [a set of rough experiments](https://github.com/ethereum/solidity/issues/7806#issuecomment-598644491) to evaluate some combinations of parameter values. +We have conducted [a set of rough experiments](https://github.com/argotorg/solidity/issues/7806#issuecomment-598644491) to evaluate some combinations of parameter values. The conclusions were used to adjust the defaults but you might still benefit from some general observations: 1. The algorithm that performed the best was `GEWEP`. From c5fa337aefc183694d0e14823e896ae4cda0fbd4 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Wed, 27 Aug 2025 12:13:38 +0200 Subject: [PATCH 003/243] Bump dockerfile label version --- scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz | 2 +- scripts/docker/buildpack-deps/Dockerfile.ubuntu2204 | 2 +- scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 | 2 +- scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz b/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz index 465f2de0a1..c14039d90d 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz @@ -22,7 +22,7 @@ # (c) 2016-2021 solidity contributors. #------------------------------------------------------------------------------ FROM gcr.io/oss-fuzz-base/base-builder AS base -LABEL version="10" +LABEL version="11" ARG DEBIAN_FRONTEND=noninteractive diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2204 b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2204 index 48272ed955..be3daee909 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2204 +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2204 @@ -22,7 +22,7 @@ # (c) 2016-2025 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:jammy AS base -LABEL version="2" +LABEL version="3" ARG DEBIAN_FRONTEND=noninteractive diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 index 475d4e83cb..1edf6f37e4 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 @@ -22,7 +22,7 @@ # (c) 2016-2024 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:noble AS base -LABEL version="3" +LABEL version="4" ARG DEBIAN_FRONTEND=noninteractive diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang index 18cf813fe7..3622cc7eba 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang @@ -22,7 +22,7 @@ # (c) 2016-2024 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:noble AS base -LABEL version="4" +LABEL version="5" ARG DEBIAN_FRONTEND=noninteractive From 0232abc6db66e396856b0fa2ae71c2fb36dcc5b2 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Wed, 27 Aug 2025 14:23:04 +0200 Subject: [PATCH 004/243] ci: rename osx-dependencies -> macos-dependencies --- .circleci/config.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d206beb693..219321ab6e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -380,11 +380,11 @@ commands: steps: - restore_cache: keys: - - osx-dependencies-cached-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} + - macos-dependencies-cached-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} - run: name: Installing dependencies / Restoring dependency cache command: | - if [[ -f ~/osx-dependencies-cached ]]; then + if [[ -f ~/macos-dependencies-cached ]]; then echo "Dependency flag exists. Removing /usr/local/, /opt/homebrew/, and /opt/boost. These directories will be restored from cache." # CircleCI is providing the circleci cli tools via some kind of symlink magic. @@ -414,7 +414,7 @@ commands: fi - restore_cache: keys: - - osx-dependencies-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} + - macos-dependencies-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} # DO NOT EDIT between here and save_cache, but rather edit ./circleci/osx_install_dependencies.sh # WARNING! If you do edit anything here instead, remember to invalidate the cache manually. - run: @@ -422,18 +422,18 @@ commands: command: .circleci/osx_install_dependencies.sh - run: name: Mark dependencies as cached - command: touch ~/osx-dependencies-cached + command: touch ~/macos-dependencies-cached - save_cache: - key: osx-dependencies-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} + key: macos-dependencies-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} paths: # Homebrew is installed in /usr/local on intel macs, but in /opt/homebrew on apple silicon. - /usr/local - /opt/homebrew - /opt/boost - save_cache: - key: osx-dependencies-cached-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} + key: macos-dependencies-cached-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} paths: - - ~/osx-dependencies-cached + - ~/macos-dependencies-cached defaults: From 029a9806417f7af4c83e070a3444d63857f57450 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Wed, 27 Aug 2025 18:43:59 +0200 Subject: [PATCH 005/243] Update project_slug in download_benchmarks.py --- scripts/externalTests/download_benchmarks.py | 4 ++-- test/scripts/test_externalTests_benchmark_downloader.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/externalTests/download_benchmarks.py b/scripts/externalTests/download_benchmarks.py index 0f8a650360..386367798f 100755 --- a/scripts/externalTests/download_benchmarks.py +++ b/scripts/externalTests/download_benchmarks.py @@ -123,8 +123,8 @@ def download_benchmarks( debug_requests: bool = False, silent: bool = False, ) -> Status: - github = Github('ethereum/solidity', debug_requests) - circleci = CircleCI('ethereum/solidity', debug_requests) + github = Github('argotorg/solidity', debug_requests) + circleci = CircleCI('argotorg/solidity', debug_requests) expected_commit_hash = None if branch is None and pull_request_id is None and base_of_pr is None: diff --git a/test/scripts/test_externalTests_benchmark_downloader.py b/test/scripts/test_externalTests_benchmark_downloader.py index f9ca9ae721..2df72d90b9 100644 --- a/test/scripts/test_externalTests_benchmark_downloader.py +++ b/test/scripts/test_externalTests_benchmark_downloader.py @@ -36,7 +36,7 @@ def _git_run_command_mock(command): def _requests_get_mock(url, params, headers, timeout): response_mock = Mock() - if url == 'https://api.github.com/repos/ethereum/solidity/pulls/12818': + if url == 'https://api.github.com/repos/argotorg/solidity/pulls/12818': response_mock.json.return_value = { "head": { "ref": "benchmark-downloader", @@ -50,7 +50,7 @@ def _requests_get_mock(url, params, headers, timeout): return response_mock if ( - url == 'https://circleci.com/api/v2/project/gh/ethereum/solidity/pipeline' and + url == 'https://circleci.com/api/v2/project/gh/argotorg/solidity/pipeline' and params.get('branch') == 'develop' ): response_mock.json.return_value = { @@ -97,7 +97,7 @@ def _requests_get_mock(url, params, headers, timeout): return response_mock if ( - url == 'https://circleci.com/api/v2/project/gh/ethereum/solidity/pipeline' and + url == 'https://circleci.com/api/v2/project/gh/argotorg/solidity/pipeline' and params.get('branch') == 'benchmark-downloader' ): response_mock.json.return_value = { @@ -156,7 +156,7 @@ def _requests_get_mock(url, params, headers, timeout): } return response_mock - if url == 'https://circleci.com/api/v2/project/gh/ethereum/solidity/1018023/artifacts': + if url == 'https://circleci.com/api/v2/project/gh/argotorg/solidity/1018023/artifacts': if ( os.environ.get('CIRCLECI_TOKEN') == 'valid_token' and headers.get('Circle-Token') == os.environ.get('CIRCLECI_TOKEN') From 69af5ee0be572f06d199a1484fb3861f29af0156 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Thu, 28 Aug 2025 15:07:29 +0200 Subject: [PATCH 006/243] Replace unmaintained GitHub Action for PR comments --- .github/workflows/buildpack-deps.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/buildpack-deps.yml b/.github/workflows/buildpack-deps.yml index 00d08372d6..b277565a85 100644 --- a/.github/workflows/buildpack-deps.yml +++ b/.github/workflows/buildpack-deps.yml @@ -1,5 +1,8 @@ name: buildpack-deps +permissions: + pull-requests: write + on: pull_request: branches: [ develop ] @@ -38,9 +41,6 @@ jobs: - name: comment PR if: "env.DOCKER_IMAGE" - # NOTE: Can't update to v1.3.1 due to an error: `/entrypoint.sh:5:in 'require_relative': cannot load such file -- /lib/github (LoadError)` - uses: unsplash/comment-on-pr@ffe8f97ccc63ce12c3c23c6885b169db67958d3b #v1.3.0 + uses: thollander/actions-comment-pull-request@v3 with: - msg: "`${{ env.DOCKER_IMAGE }} ${{ env.DOCKER_REPO_DIGEST }}`." - check_for_duplicate_msg: false - + message: "`${{ env.DOCKER_IMAGE }} ${{ env.DOCKER_REPO_DIGEST }}`." From 50598a32e2c1a47f5d2166bbcb91684853c681c7 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Fri, 29 Aug 2025 13:24:13 +0200 Subject: [PATCH 007/243] Update Solidity project board link --- docs/contributing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 6bf89cc920..45b15e579d 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -555,7 +555,7 @@ topics, issues or feature implementations are debated in detail. The invitation We are also sharing feedback surveys and other content that is relevant to language design in the forum. -If you want to know where the team is standing in terms of implementing new features, you can follow the implementation status in the `Solidity GitHub project `_. +If you want to know where the team is standing in terms of implementing new features, you can follow the implementation status in the `Solidity GitHub project `_. Issues in the design backlog need further specification and will either be discussed in a language design call or in a regular team call. You can see the upcoming changes for the next breaking release by changing from the default branch (`develop`) to the `breaking branch `_. From 5991ef62bc5f20798bd5d84e7bb371dcc2053f24 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Tue, 2 Sep 2025 19:40:51 +0200 Subject: [PATCH 008/243] Change bot account --- scripts/ci/post_style_errors_on_github.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ci/post_style_errors_on_github.sh b/scripts/ci/post_style_errors_on_github.sh index b0bf1bda71..db7614179d 100755 --- a/scripts/ci/post_style_errors_on_github.sh +++ b/scripts/ci/post_style_errors_on_github.sh @@ -26,7 +26,7 @@ function post_error_to_github --url "$GITHUB_API_URL" \ --header 'accept: application/vnd.github.v3+json' \ --header 'content-type: application/json' \ - -u "stackenbotten:$GITHUB_ACCESS_TOKEN" \ + -u "stackenbotten3000:$GITHUB_ACCESS_TOKEN" \ --data "{\"body\": \"There was an error when running \`$CIRCLE_JOB\` for commit \`$CIRCLE_SHA1\`:\n\`\`\`\n$FORMATTED_ERROR_MSG\n\`\`\`\nPlease check that your changes are working as intended.\"}" } @@ -47,7 +47,7 @@ function post_review_comment_to_github --url "$GITHUB_API_URL" \ --header 'accept: application/vnd.github.v3+json, application/vnd.github.comfort-fade-preview+json' \ --header 'content-type: application/json' \ - -u "stackenbotten:$GITHUB_ACCESS_TOKEN" \ + -u "stackenbotten3000:$GITHUB_ACCESS_TOKEN" \ --data "{\"commit_id\": \"$CIRCLE_SHA1\", \"path\": \"$ERROR_PATH\", \"line\": $ERROR_LINE, \"side\": \"RIGHT\", \"body\": \"Coding style error\"}" done < "$ERROR_LOG" } From 5b4389af996ee56b60df2b12477bdca99a70d5b0 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Mon, 8 Sep 2025 18:44:26 +0200 Subject: [PATCH 009/243] Grant pull-requests write permission to enable welcome-external-pr bot commenting --- .github/workflows/welcome-external-pr.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/welcome-external-pr.yml b/.github/workflows/welcome-external-pr.yml index b795a36b0f..035e2bf5be 100644 --- a/.github/workflows/welcome-external-pr.yml +++ b/.github/workflows/welcome-external-pr.yml @@ -5,6 +5,10 @@ on: types: - opened +permissions: + pull-requests: write + contents: read + env: DRY_RUN: false From a3480a3b8aa34824958b20b380c45e3233ad6f6a Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Wed, 10 Sep 2025 11:56:16 +0200 Subject: [PATCH 010/243] Pin GitHub Actions workflows to specific release commits --- .github/workflows/buildpack-deps.yml | 4 ++-- .github/workflows/stale.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/buildpack-deps.yml b/.github/workflows/buildpack-deps.yml index b277565a85..5e49f2f326 100644 --- a/.github/workflows/buildpack-deps.yml +++ b/.github/workflows/buildpack-deps.yml @@ -29,7 +29,7 @@ jobs: image_variant: [emscripten, ubuntu.clang.ossfuzz, ubuntu2004, ubuntu2204, ubuntu2404, ubuntu2404.clang] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 @@ -41,6 +41,6 @@ jobs: - name: comment PR if: "env.DOCKER_IMAGE" - uses: thollander/actions-comment-pull-request@v3 + uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1 with: message: "`${{ env.DOCKER_IMAGE }} ${{ env.DOCKER_REPO_DIGEST }}`." diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 0b34472822..aaf8002172 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -19,7 +19,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v6 + - uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0 with: debug-only: false days-before-issue-stale: ${{ env.BEFORE_ISSUE_STALE }} From f71b385216997d20c1d3d92354a68b95cb925fb7 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Wed, 24 Sep 2025 16:45:23 +0200 Subject: [PATCH 011/243] CI re-enable full prb-math preset matrix --- test/externalTests/prb-math.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/externalTests/prb-math.py b/test/externalTests/prb-math.py index 0cfefb843f..029311d0a5 100755 --- a/test/externalTests/prb-math.py +++ b/test/externalTests/prb-math.py @@ -52,16 +52,13 @@ def configure(self): name="PRBMath", repo_url="https://github.com/PaulRBerg/prb-math.git", ref="", - compile_only_presets=[ - # pylint: disable=line-too-long - # SettingsPreset.IR_NO_OPTIMIZE, # Error: Yul exception:Variable expr_15699_address is 2 slot(s) too deep inside the stack. Stack too deep. - # SettingsPreset.IR_OPTIMIZE_EVM_ONLY, # Error: Yul exception:Variable expr_15699_address is 2 slot(s) too deep inside the stack. Stack too deep. - ], settings_presets=[ SettingsPreset.LEGACY_NO_OPTIMIZE, SettingsPreset.LEGACY_OPTIMIZE_EVM_ONLY, SettingsPreset.LEGACY_OPTIMIZE_EVM_YUL, + SettingsPreset.IR_NO_OPTIMIZE, SettingsPreset.IR_OPTIMIZE_EVM_YUL, + SettingsPreset.IR_OPTIMIZE_EVM_ONLY, ], ) From 88cbcfb8c6c924e65c756a93af3e3950b7069df0 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Thu, 25 Sep 2025 11:40:58 +0200 Subject: [PATCH 012/243] CI: Use nightly foundry version for prb-math and switch to prb-math main ref --- .circleci/config.yml | 4 +--- test/externalTests/prb-math.py | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 219321ab6e..4c79434a8d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -839,9 +839,7 @@ defaults: project: prb-math binary_type: native image: cimg/rust:1.74.0-node - resource_class: medium - # TODO: Use nightly Foundry builds after https://github.com/PaulRBerg/prb-math/issues/248 is fixed - foundry_version: "v0.3.0" + resource_class: large # Tests run out of memory on a smaller machine - job_native_test_ext_elementfi: &job_native_test_ext_elementfi <<: *requires_b_ubu_static diff --git a/test/externalTests/prb-math.py b/test/externalTests/prb-math.py index 029311d0a5..afdca1313e 100755 --- a/test/externalTests/prb-math.py +++ b/test/externalTests/prb-math.py @@ -51,7 +51,7 @@ def configure(self): test_config = TestConfig( name="PRBMath", repo_url="https://github.com/PaulRBerg/prb-math.git", - ref="", + ref="main", settings_presets=[ SettingsPreset.LEGACY_NO_OPTIMIZE, SettingsPreset.LEGACY_OPTIMIZE_EVM_ONLY, From 6795a558da76e88b004dfd0fdf1b2773a3494e01 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Thu, 25 Sep 2025 16:54:32 +0200 Subject: [PATCH 013/243] CI: Install python3-dev, librairo2-dev, and pkg-config before building docs --- .circleci/config.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4c79434a8d..f198be8922 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1235,6 +1235,11 @@ jobs: steps: - checkout - setup_prerelease_commit_hash + - run: + name: Install build system dependencies + command: | + apt-get update + apt-get install --quiet=2 --no-install-recommends python3-dev libcairo2-dev pkg-config - run: name: Build documentation command: docs/docs.sh From 5997617131253f6457e7b9363ddea694231f647c Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 29 Sep 2025 22:15:11 +0200 Subject: [PATCH 014/243] docs: use sphinx-syntax over sphinx-a4doc --- docs/_static/css/custom-dark.css | 2 +- docs/conf.py | 9 ++++++--- docs/grammar.rst | 8 ++++---- docs/requirements.txt | 11 +++++------ 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/_static/css/custom-dark.css b/docs/_static/css/custom-dark.css index 044a8f800d..1294a6f648 100644 --- a/docs/_static/css/custom-dark.css +++ b/docs/_static/css/custom-dark.css @@ -590,6 +590,6 @@ stroke: white; } -:root[style*=dark] .a4 .sig-name { +:root[style*=dark] .sig-name { background-color: transparent !important; } diff --git a/docs/conf.py b/docs/conf.py index dc52c96bdf..44da93dba5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -92,13 +92,15 @@ def get_github_username_repo(url): # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx_a4doc', + 'sphinx_syntax', 'html_extra_template_renderer', 'remix_code_links', 'sphinx.ext.imgconverter', ] -a4_base_path = os.path.dirname(__file__) + '/grammar' +syntax_base_path = 'grammar' +# generate link anchors compatible with Sphinx-A4Doc’s naming +syntax_a4doc_compat_links = True # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -182,7 +184,8 @@ def get_github_username_repo(url): # documentation. html_theme_options = { 'logo_only': True, - 'display_version': True, + 'version_selector': True, + 'language_selector': True, } # Add any paths that contain custom themes here, relative to this directory. diff --git a/docs/grammar.rst b/docs/grammar.rst index 788b6d6950..d58cbb41a6 100644 --- a/docs/grammar.rst +++ b/docs/grammar.rst @@ -2,12 +2,12 @@ Language Grammar **************** -.. a4:autogrammar:: SolidityParser - :only-reachable-from: SolidityParser.sourceUnit +.. syntax:autogrammar:: SolidityParser.g4 + :root-rule: SolidityParser.sourceUnit :undocumented: :cc-to-dash: -.. a4:autogrammar:: SolidityLexer - :only-reachable-from: SolidityParser.sourceUnit +.. syntax:autogrammar:: SolidityLexer.g4 + :root-rule: SolidityParser.sourceUnit :fragments: :cc-to-dash: \ No newline at end of file diff --git a/docs/requirements.txt b/docs/requirements.txt index f4c1aae617..8f5f27ffff 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,12 +2,11 @@ # which could result in it being installed anyway and the style (especially bullet points) being broken. # See https://github.com/readthedocs/sphinx_rtd_theme/issues/1115 # theme >=3.0.0 removes the display_version option in favor of version_selector and language_selector -sphinx_rtd_theme>=0.5.2, <3.0.0 +# Use rtd theme version that supports sphinx>=8.0 +sphinx_rtd_theme>=3.0.0 pygments-lexer-solidity>=0.7.0 -sphinx-a4doc>=1.6.0; python_version < '3.13' -# todo remove this once there is a version > 1.6.0 -sphinx-a4doc @ git+https://github.com/taminomara/sphinx-a4doc@f63d3b2; python_version >= '3.13' +sphinx-syntax>=1.0.1 -# Sphinx 2.1.0 is the oldest version that accepts a lexer class in add_lexer() -sphinx>=2.1.0, <9.0 +# Sphinx version constraints for sphinx-syntax compatibility +sphinx>=8.0.0, <9.0.0 From 89aafb968beb2601cca4aa0097b4172da1db8195 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Tue, 30 Sep 2025 11:48:18 +0200 Subject: [PATCH 015/243] bump rtd os to ubuntu 24.04 and python 3.13 --- .readthedocs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index c0c139b7e8..bb81012240 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,9 +1,9 @@ version: 2 build: - os: ubuntu-22.04 + os: ubuntu-24.04 tools: - python: "3.11" + python: "3.13" sphinx: builder: html From 292bfd43432757a701ee0025f689a39ec68e4daf Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Tue, 30 Sep 2025 10:51:11 +0200 Subject: [PATCH 016/243] dockerfiles: bump emscripten to v21 containing boost 1.83.0 --- .../buildpack-deps/Dockerfile.emscripten | 72 ++++++++++--------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/scripts/docker/buildpack-deps/Dockerfile.emscripten b/scripts/docker/buildpack-deps/Dockerfile.emscripten index d8eb9a903f..007729d411 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.emscripten +++ b/scripts/docker/buildpack-deps/Dockerfile.emscripten @@ -33,26 +33,29 @@ # Using $(em-config CACHE)/sysroot/usr seems to work, though, and still has cmake find the # dependencies automatically. FROM emscripten/emsdk:3.1.19 AS base -LABEL version="20" +LABEL version="21" ADD emscripten.jam /usr/src -RUN set -ex && \ - apt-get update && \ +RUN <<-EOF + set -ex + apt-get update apt-get install -qqy --no-install-recommends \ lsof \ lz4 \ python3 \ python3-pip \ sudo && \ - pip3 install requests; + pip3 install requests +EOF # Install Z3 -RUN set -ex && \ - cd /usr/src && \ - git clone https://github.com/Z3Prover/z3.git -b z3-4.13.3 --depth 1 && \ - cd z3 && \ - mkdir build && \ - cd build && \ +RUN <<-EOF + set -ex + cd /usr/src + git clone https://github.com/Z3Prover/z3.git -b z3-4.13.3 --depth 1 + cd z3 + mkdir build + cd build emcmake cmake \ -DCMAKE_INSTALL_PREFIX=$(em-config CACHE)/sysroot/usr \ -DCMAKE_BUILD_TYPE=MinSizeRel \ @@ -62,33 +65,38 @@ RUN set -ex && \ -DZ3_BUILD_EXECUTABLE=OFF \ -DZ3_SINGLE_THREADED=ON \ -DCMAKE_CXX_FLAGS="-s DISABLE_EXCEPTION_CATCHING=0" \ - .. && \ - make && \ - make install && \ + .. + make + make install rm -r /usr/src/z3 +EOF # Install Boost -RUN set -ex && \ - cd /usr/src && \ - wget -q 'https://archives.boost.io/release/1.75.0/source/boost_1_75_0.tar.bz2' -O boost.tar.bz2 && \ - test "$(sha256sum boost.tar.bz2)" = "953db31e016db7bb207f11432bef7df100516eeb746843fa0486a222e3fd49cb boost.tar.bz2" && \ - tar -xf boost.tar.bz2 && \ - rm boost.tar.bz2 && \ - cd boost_1_75_0 && \ - mv ../emscripten.jam . && \ - ./bootstrap.sh && \ - echo "using emscripten : : em++ ;" >> project-config.jam && \ +RUN <<-EOF + set -ex + cd /usr/src + wget -q 'https://archives.boost.io/release/1.83.0/source/boost_1_83_0.tar.bz2' -O boost.tar.bz2 + test "$(sha256sum boost.tar.bz2)" = "6478edfe2f3305127cffe8caf73ea0176c53769f4bf1585be237eb30798c3b8e boost.tar.bz2" + tar -xf boost.tar.bz2 + rm boost.tar.bz2 + cd boost_1_83_0 + mv ../emscripten.jam . + ./bootstrap.sh + echo "using emscripten : : em++ ;" >> project-config.jam ./b2 toolset=emscripten link=static variant=release threading=single runtime-link=static \ --with-system --with-filesystem --with-test --with-program_options \ cxxflags="-s DISABLE_EXCEPTION_CATCHING=0 -Wno-unused-local-typedef -Wno-variadic-macros -Wno-c99-extensions -Wno-all" \ - --prefix=$(em-config CACHE)/sysroot/usr install && \ - rm -r /usr/src/boost_1_75_0 + --prefix=$(em-config CACHE)/sysroot/usr install + rm -r /usr/src/boost_1_83_0 +EOF # CVC5 -RUN set -ex; \ - cvc5_version="1.2.0"; \ - cvc5_archive_name="cvc5-Linux-x86_64-static"; \ - wget "https://github.com/cvc5/cvc5/releases/download/cvc5-${cvc5_version}/${cvc5_archive_name}.zip" -O /opt/cvc5.zip; \ - test "$(sha256sum /opt/cvc5.zip)" = "d18f174ff9a11923c32c3f871f844ed16bd77a28f51050b8e7c8d821c98a1c2e /opt/cvc5.zip"; \ - unzip -j /opt/cvc5.zip "${cvc5_archive_name}/bin/cvc5" -d /usr/bin; \ - rm -f /opt/cvc5.zip; +RUN <<-EOF + set -ex + cvc5_version="1.2.0" + cvc5_archive_name="cvc5-Linux-x86_64-static" + wget "https://github.com/cvc5/cvc5/releases/download/cvc5-${cvc5_version}/${cvc5_archive_name}.zip" -O /opt/cvc5.zip + test "$(sha256sum /opt/cvc5.zip)" = "d18f174ff9a11923c32c3f871f844ed16bd77a28f51050b8e7c8d821c98a1c2e /opt/cvc5.zip" + unzip -j /opt/cvc5.zip "${cvc5_archive_name}/bin/cvc5" -d /usr/bin + rm -f /opt/cvc5.zip +EOF From fff81f3cf3e4587b54ad915ac982823a92d96d77 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Wed, 20 Aug 2025 15:52:20 +0200 Subject: [PATCH 017/243] CI: Add job that tests minimum requirement versions for linux --- .circleci/config.yml | 53 +++++++++++-- .../install_and_check_minimum_requirements.sh | 75 +++++++++++++++++++ 2 files changed, 121 insertions(+), 7 deletions(-) create mode 100755 scripts/ci/install_and_check_minimum_requirements.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index f198be8922..ac8d48d6d6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -198,6 +198,19 @@ commands: fi echo -n "$CIRCLE_SHA1" > commit_hash.txt + install_and_check_minimum_requirements: + parameters: + compiler: + description: "Compiler to check: gcc or clang" + type: enum + enum: + - gcc + - clang + steps: + - run: + name: Install and check minimum requirements + command: scripts/ci/install_and_check_minimum_requirements.sh "--<< parameters.compiler >>" + run_build: steps: - run: @@ -1103,23 +1116,43 @@ jobs: - solc/solc-static-linux - matrix_notify_failure_unless_pr - b_ubu_2204: - <<: *base_ubuntu2204_large + b_ubu_min_req: + <<: *base_ubuntu2404_large steps: - checkout + - install_and_check_minimum_requirements: + compiler: gcc - run_build + - store_artifacts_solc + - persist_executables_to_workspace - matrix_notify_failure_unless_pr + - build - b_ubu_2204_clang: - <<: *base_ubuntu2204_clang_large + b_ubu_min_req_clang: + <<: *base_ubuntu2404_clang_large environment: - <<: *base_ubuntu2204_clang_large_env + <<: *base_ubuntu2404_clang_large_env MAKEFLAGS: -j 10 steps: - checkout + - install_and_check_minimum_requirements: + compiler: clang - run_build - matrix_notify_failure_unless_pr + t_ubu_min_req_soltest: + <<: *base_ubuntu2404_large + parallelism: 20 + environment: + <<: *base_ubuntu2404_large_env + EVM: << pipeline.parameters.evm-version >> + EOF_VERSION: 0 + OPTIMIZE: 0 + # Skip semantic tests due to evmone compatibility issues with Ubuntu 22.04 + SOLTEST_FLAGS: --no-semantic-tests --no-smt + steps: + - soltest + b_ubu_ossfuzz: &b_ubu_ossfuzz <<: *base_ubuntu_clang_large steps: @@ -1893,8 +1926,14 @@ workflows: # build-only - b_docs: *requires_nothing - b_ubu_ossfuzz: *requires_nothing - - b_ubu_2204: *requires_nothing - - b_ubu_2204_clang: *requires_nothing + + # build and test with minimum supported versions of dependencies + - b_ubu_min_req_clang: *requires_nothing + - b_ubu_min_req: *requires_nothing + - t_ubu_min_req_soltest: + <<: *on_all_tags_and_branches + requires: + - b_ubu_min_req # OS/X build and tests - b_osx: *requires_nothing diff --git a/scripts/ci/install_and_check_minimum_requirements.sh b/scripts/ci/install_and_check_minimum_requirements.sh new file mode 100755 index 0000000000..db3ff0fec2 --- /dev/null +++ b/scripts/ci/install_and_check_minimum_requirements.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +set -euo pipefail + +# minimum boost version +BOOST_VERSION=1.83 + +# minimum cmake version +CMAKE_MAJOR=3 +CMAKE_MINOR=13 +CMAKE_PATCH=0 +CMAKE_FULL_VERSION="${CMAKE_MAJOR}.${CMAKE_MINOR}.${CMAKE_PATCH}" + +# minimum gcc/clang versions +GCC_VERSION=13.3.0 +CLANG_VERSION=18.1.3 + +# which compiler version to check in this script +check_gcc=false +check_clang=false + +while (( $# > 0 )); do + case "$1" in + --gcc) + check_gcc=true + shift + ;; + --clang) + check_clang=true + shift + ;; + *) + echo "Unknown option: $1" + echo "Usage: $0 [--gcc] [--clang]" + exit 1 + ;; + esac +done + +echo "-- Removing boost and CMake from the system" +sudo apt-get --quiet=2 remove --purge 'libboost*' +sudo apt-get --quiet=2 remove --purge cmake + +echo "-- Installing Boost ${BOOST_VERSION}" +sudo apt-get --quiet=2 update +sudo apt-get --quiet=2 install libboost${BOOST_VERSION}-all-dev +installed_boost_version=$(dpkg-query --showformat='${Version}' --show libboost${BOOST_VERSION}-all-dev 2>/dev/null || echo "none") +if [[ $installed_boost_version != ${BOOST_VERSION}* ]]; then + echo "Error: installed version of boost is $installed_boost_version, expected $BOOST_VERSION" + exit 1 +fi + +echo "-- Installing CMake ${CMAKE_FULL_VERSION}" +wget "https://cmake.org/files/v${CMAKE_MAJOR}.${CMAKE_MINOR}/cmake-${CMAKE_FULL_VERSION}-Linux-x86_64.tar.gz" +tar --extract --gzip --file "cmake-${CMAKE_FULL_VERSION}-Linux-x86_64.tar.gz" +sudo mv "cmake-${CMAKE_FULL_VERSION}-Linux-x86_64" "/opt/cmake-${CMAKE_FULL_VERSION}" +sudo ln --symbolic "/opt/cmake-${CMAKE_FULL_VERSION}/bin/"* /usr/local/bin/ +echo "-- Installed $(cmake --version)" + +if [[ "$check_gcc" == true ]]; then + installed_gcc_version=$(gcc -dumpfullversion -dumpversion || echo "none") + if [[ "$installed_gcc_version" != "$GCC_VERSION" ]]; then + echo "Error: installed version of gcc is $installed_gcc_version, expected $GCC_VERSION" + exit 1 + fi + echo "-- gcc version check passed: $installed_gcc_version" +fi + +if [[ "$check_clang" == true ]]; then + installed_clang_version=$(clang -dumpfullversion -dumpversion || echo "none") + if [[ "$installed_clang_version" != "$CLANG_VERSION" ]]; then + echo "Error: installed version of clang is $installed_clang_version, expected $CLANG_VERSION" + exit 1 + fi + echo "-- clang version check passed: $installed_clang_version" +fi From cbaa5ab9926ad132fe3845cf907f6758703ba29e Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Tue, 9 Sep 2025 10:09:41 +0200 Subject: [PATCH 018/243] Deps: Update minimum version requirements of Boost to 1.83.0 for non-windows builds and of GCC and Clang to 13.3 and 18.1.3, respectively. Fixes infinite recursion on `boost::rational` comparison affecting compiler binaries built with GCC<14.0 and Boost<1.75. --- .circleci/config.yml | 4 ++-- Changelog.md | 2 +- cmake/EthDependencies.cmake | 4 +++- docs/installing-solidity.rst | 10 +++++++--- scripts/ci/install_and_check_minimum_requirements.sh | 2 ++ 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ac8d48d6d6..20c6bc2a86 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -30,8 +30,8 @@ parameters: emscripten-docker-image: type: string # NOTE: Please remember to update the `scripts/build_emscripten.sh` whenever the hash of this image changes. - # solbuildpackpusher/solidity-buildpack-deps:emscripten-20 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:98f963ed799a0d206ef8e7b5475f847e0dea53b7fdea9618bbc6106a62730bd2" + # solbuildpackpusher/solidity-buildpack-deps:emscripten-21 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:fc53d68a4680ffa7d5f70164e13a903478964f15bcc07434d74833a05f4fbc19" evm-version: type: string default: prague diff --git a/Changelog.md b/Changelog.md index df45a558f4..53039947bc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,7 +10,7 @@ Bugfixes: * Yul Optimizer: Fix edge case in which invalid Yul code is produced by ExpressionSimplifier due to expressions being substituted that contain out-of-scope variables. Build System: -* Update to boost 1.70.0 for non-windows builds. +* Update minimum version requirements of Boost to 1.83.0 for non-windows builds and of GCC and Clang to 13.3 and 18.1.3, respectively. Fixes infinite recursion on `boost::rational` comparison affecting compiler binaries built with GCC<14.0 and Boost<1.75. ### 0.8.30 (2025-05-07) diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index 453c6d14ce..ac810c50df 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -44,7 +44,9 @@ else() # Boost 1.67 moved container_hash into is own module. # Boost 1.69 boost::system is header-only and no longer needs to be fetched as component # Boost 1.70 comes with its own BoostConfig.cmake and is the new (non-deprecated) behavior - find_package(Boost 1.70.0 QUIET REQUIRED COMPONENTS ${BOOST_COMPONENTS}) + # Boost 1.75 fixes infinite recursion on `boost::rational` comparison with GCC<14.0 under C++20 + # Boost 1.83 is the version that comes with Ubuntu 24.04. + find_package(Boost 1.83.0 QUIET REQUIRED COMPONENTS ${BOOST_COMPONENTS}) endif() # If cmake is older than boost and boost is older than 1.70, diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 6f085539bc..6c6d6029a1 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -320,6 +320,8 @@ Prerequisites - All Operating Systems The following are dependencies for all builds of Solidity: +.. Note: This has to be kept in sync with `scripts/ci/install_and_check_minimum_requirements.sh`. + +-----------------------------------+-------------------------------------------------------+ | Software | Notes | +===================================+=======================================================+ @@ -327,7 +329,7 @@ The following are dependencies for all builds of Solidity: | Windows, 3.13+ otherwise) | | +-----------------------------------+-------------------------------------------------------+ | `Boost`_ (version 1.77+ on | C++ libraries. | -| Windows, 1.70+ otherwise) | | +| Windows, 1.83+ otherwise) | | +-----------------------------------+-------------------------------------------------------+ | `Git`_ | Command-line tool for retrieving source code. | +-----------------------------------+-------------------------------------------------------+ @@ -372,8 +374,10 @@ Minimum Compiler Versions The following C++ compilers and their minimum versions can build the Solidity codebase: -- `GCC `_, version 11+ -- `Clang `_, version 14+ +.. Note: Minimum versions for GCC and Clang are based on availability in Ubuntu 24.04. + +- `GCC `_, version 13.3+ +- `Clang `_, version 18.1.3+ - `MSVC `_, version 2019+ Prerequisites - macOS diff --git a/scripts/ci/install_and_check_minimum_requirements.sh b/scripts/ci/install_and_check_minimum_requirements.sh index db3ff0fec2..bc167b6735 100755 --- a/scripts/ci/install_and_check_minimum_requirements.sh +++ b/scripts/ci/install_and_check_minimum_requirements.sh @@ -1,6 +1,8 @@ #!/usr/bin/env bash set -euo pipefail +# These versions must be kept in sync with the docs in `docs/installing-solidity.rst#building-from-source`. + # minimum boost version BOOST_VERSION=1.83 From 8c1e48917228bda980a3b1d307e874ce80c8371e Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 22 Sep 2025 12:46:39 +0200 Subject: [PATCH 019/243] CI: Prune 2204 jobs from the config --- .circleci/config.yml | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 20c6bc2a86..076c7b8d19 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -11,10 +11,6 @@ parameters: type: string # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-26 default: "solbuildpackpusher/solidity-buildpack-deps@sha256:1f387a77be889f65a2a25986a5c5eccc88cec23fabe6aeaf351790751145c81e" - ubuntu-2204-docker-image: - type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu2204-2 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:877fcc2589779f8245770711d10db92eda97d338dae76b6a9f27dde1a41b3aa0" ubuntu-2404-docker-image: type: string # solbuildpackpusher/solidity-buildpack-deps:ubuntu2404-3 @@ -573,42 +569,6 @@ defaults: MAKEFLAGS: -j 10 CPUs: 10 - - base_ubuntu2204: &base_ubuntu2204 - docker: - - image: << pipeline.parameters.ubuntu-2204-docker-image >> - environment: &base_ubuntu2204_env - TERM: xterm - CC: gcc - CXX: g++ - MAKEFLAGS: -j 3 - CPUs: 3 - - - base_ubuntu2204_large: &base_ubuntu2204_large - <<: *base_ubuntu2204 - resource_class: large - environment: &base_ubuntu2204_large_env - <<: *base_ubuntu2204_env - MAKEFLAGS: -j 5 - CPUs: 5 - - - base_ubuntu2204_clang: &base_ubuntu2204_clang - docker: - - image: << pipeline.parameters.ubuntu-2204-docker-image >> - environment: &base_ubuntu2204_clang_env - TERM: xterm - CC: clang - CXX: clang++ - MAKEFLAGS: -j 3 - CPUs: 3 - - - base_ubuntu2204_clang_large: &base_ubuntu2204_clang_large - <<: *base_ubuntu2204_clang - resource_class: large - environment: &base_ubuntu2204_clang_large_env - <<: *base_ubuntu2204_clang_env - MAKEFLAGS: -j 5 - CPUs: 5 - - base_ubuntu2404: &base_ubuntu2404 docker: - image: << pipeline.parameters.ubuntu-2404-docker-image >> @@ -1148,8 +1108,6 @@ jobs: EVM: << pipeline.parameters.evm-version >> EOF_VERSION: 0 OPTIMIZE: 0 - # Skip semantic tests due to evmone compatibility issues with Ubuntu 22.04 - SOLTEST_FLAGS: --no-semantic-tests --no-smt steps: - soltest From 7e994ce17c83b111e77b0ff9f97fc91435a8a896 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Tue, 30 Sep 2025 09:00:39 +0200 Subject: [PATCH 020/243] Move ssa pipeline things into own subdirectory --- libyul/CMakeLists.txt | 28 +++---- libyul/YulStack.cpp | 10 +-- libyul/backends/evm/{ => ssa}/ControlFlow.cpp | 9 +- libyul/backends/evm/{ => ssa}/ControlFlow.h | 11 +-- .../LivenessAnalysis.cpp} | 14 ++-- .../LivenessAnalysis.h} | 12 +-- .../SSACFG.cpp} | 13 +-- .../{SSAControlFlowGraph.h => ssa/SSACFG.h} | 6 +- .../SSACFGBuilder.cpp} | 83 +++++++++---------- .../SSACFGBuilder.h} | 14 ++-- .../evm/ssa/SSACFGJsonExporter.cpp} | 24 +++--- .../evm/ssa/SSACFGJsonExporter.h} | 18 ++-- .../evm/{ => ssa}/SSACFGLoopNestingForest.cpp | 4 +- .../evm/{ => ssa}/SSACFGLoopNestingForest.h | 6 +- .../evm/{ => ssa}/SSACFGTopologicalSort.cpp | 4 +- .../evm/{ => ssa}/SSACFGTopologicalSort.h | 4 +- test/libyul/SSAControlFlowGraphTest.cpp | 6 +- 17 files changed, 133 insertions(+), 133 deletions(-) rename libyul/backends/evm/{ => ssa}/ControlFlow.cpp (78%) rename libyul/backends/evm/{ => ssa}/ControlFlow.h (89%) rename libyul/backends/evm/{SSACFGLiveness.cpp => ssa/LivenessAnalysis.cpp} (93%) rename libyul/backends/evm/{SSACFGLiveness.h => ssa/LivenessAnalysis.h} (87%) rename libyul/backends/evm/{SSAControlFlowGraph.cpp => ssa/SSACFG.cpp} (96%) rename libyul/backends/evm/{SSAControlFlowGraph.h => ssa/SSACFG.h} (98%) rename libyul/backends/evm/{SSAControlFlowGraphBuilder.cpp => ssa/SSACFGBuilder.cpp} (88%) rename libyul/backends/evm/{SSAControlFlowGraphBuilder.h => ssa/SSACFGBuilder.h} (93%) rename libyul/{YulControlFlowGraphExporter.cpp => backends/evm/ssa/SSACFGJsonExporter.cpp} (86%) rename libyul/{YulControlFlowGraphExporter.h => backends/evm/ssa/SSACFGJsonExporter.h} (72%) rename libyul/backends/evm/{ => ssa}/SSACFGLoopNestingForest.cpp (96%) rename libyul/backends/evm/{ => ssa}/SSACFGLoopNestingForest.h (93%) rename libyul/backends/evm/{ => ssa}/SSACFGTopologicalSort.cpp (96%) rename libyul/backends/evm/{ => ssa}/SSACFGTopologicalSort.h (96%) diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 58390d9565..e70a6e7b67 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -42,8 +42,6 @@ add_library(yul ScopeFiller.h Utilities.cpp Utilities.h - YulControlFlowGraphExporter.h - YulControlFlowGraphExporter.cpp YulName.h YulString.h backends/evm/AbstractAssembly.h @@ -51,8 +49,6 @@ add_library(yul backends/evm/AsmCodeGen.h backends/evm/ConstantOptimiser.cpp backends/evm/ConstantOptimiser.h - backends/evm/ControlFlow.cpp - backends/evm/ControlFlow.h backends/evm/ControlFlowGraph.h backends/evm/ControlFlowGraphBuilder.cpp backends/evm/ControlFlowGraphBuilder.h @@ -72,21 +68,25 @@ add_library(yul backends/evm/NoOutputAssembly.cpp backends/evm/OptimizedEVMCodeTransform.cpp backends/evm/OptimizedEVMCodeTransform.h - backends/evm/SSACFGLiveness.cpp - backends/evm/SSACFGLiveness.h - backends/evm/SSACFGLoopNestingForest.cpp - backends/evm/SSACFGLoopNestingForest.h - backends/evm/SSACFGTopologicalSort.cpp - backends/evm/SSACFGTopologicalSort.h - backends/evm/SSAControlFlowGraph.cpp - backends/evm/SSAControlFlowGraph.h - backends/evm/SSAControlFlowGraphBuilder.cpp - backends/evm/SSAControlFlowGraphBuilder.h backends/evm/StackHelpers.h backends/evm/StackLayoutGenerator.cpp backends/evm/StackLayoutGenerator.h backends/evm/VariableReferenceCounter.h backends/evm/VariableReferenceCounter.cpp + backends/evm/ssa/ControlFlow.cpp + backends/evm/ssa/ControlFlow.h + backends/evm/ssa/LivenessAnalysis.cpp + backends/evm/ssa/LivenessAnalysis.h + backends/evm/ssa/SSACFGLoopNestingForest.cpp + backends/evm/ssa/SSACFGLoopNestingForest.h + backends/evm/ssa/SSACFGTopologicalSort.cpp + backends/evm/ssa/SSACFGTopologicalSort.h + backends/evm/ssa/SSACFG.cpp + backends/evm/ssa/SSACFG.h + backends/evm/ssa/SSACFGBuilder.cpp + backends/evm/ssa/SSACFGBuilder.h + backends/evm/ssa/SSACFGJsonExporter.h + backends/evm/ssa/SSACFGJsonExporter.cpp optimiser/ASTCopier.cpp optimiser/ASTCopier.h optimiser/ASTWalker.cpp diff --git a/libyul/YulStack.cpp b/libyul/YulStack.cpp index 52c6241c6a..75b39134f7 100644 --- a/libyul/YulStack.cpp +++ b/libyul/YulStack.cpp @@ -20,7 +20,8 @@ #include #include -#include +#include +#include #include #include #include @@ -28,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -398,14 +398,14 @@ Json YulStack::cfgJson() const // operations to the control flow graphs bool constexpr keepLiteralAssignments = true; // NOTE: The block Ids are reset for each object - std::unique_ptr controlFlow = SSAControlFlowGraphBuilder::build( + std::unique_ptr controlFlow = ssa::SSACFGBuilder::build( *_object.analysisInfo, languageToDialect(m_language, m_evmVersion, m_eofVersion), _object.code()->root(), keepLiteralAssignments ); - std::unique_ptr liveness = std::make_unique(*controlFlow); - YulControlFlowGraphExporter exporter(*controlFlow, liveness.get()); + std::unique_ptr liveness = std::make_unique(*controlFlow); + ssa::SSACFGJsonExporter exporter(*controlFlow, liveness.get()); return exporter.run(); }; diff --git a/libyul/backends/evm/ControlFlow.cpp b/libyul/backends/evm/ssa/ControlFlow.cpp similarity index 78% rename from libyul/backends/evm/ControlFlow.cpp rename to libyul/backends/evm/ssa/ControlFlow.cpp index 863fa30f6c..fb560d6dd6 100644 --- a/libyul/backends/evm/ControlFlow.cpp +++ b/libyul/backends/evm/ssa/ControlFlow.cpp @@ -16,15 +16,16 @@ */ // SPDX-License-Identifier: GPL-3.0 -#include +#include + #include -using namespace solidity::yul; +using namespace solidity::yul::ssa; ControlFlowLiveness::ControlFlowLiveness(ControlFlow const& _controlFlow): controlFlow(_controlFlow), - mainLiveness(std::make_unique(*_controlFlow.mainGraph)), - functionLiveness(_controlFlow.functionGraphs | ranges::views::transform([](auto const& _cfg) { return std::make_unique(*_cfg); }) | ranges::to) + mainLiveness(std::make_unique(*_controlFlow.mainGraph)), + functionLiveness(_controlFlow.functionGraphs | ranges::views::transform([](auto const& _cfg) { return std::make_unique(*_cfg); }) | ranges::to) { } std::string ControlFlowLiveness::toDot() const diff --git a/libyul/backends/evm/ControlFlow.h b/libyul/backends/evm/ssa/ControlFlow.h similarity index 89% rename from libyul/backends/evm/ControlFlow.h rename to libyul/backends/evm/ssa/ControlFlow.h index 394ce00c58..314545643e 100644 --- a/libyul/backends/evm/ControlFlow.h +++ b/libyul/backends/evm/ssa/ControlFlow.h @@ -18,12 +18,13 @@ #pragma once +#include +#include + #include #include -#include -#include -namespace solidity::yul +namespace solidity::yul::ssa { struct ControlFlow; @@ -32,8 +33,8 @@ struct ControlFlowLiveness{ explicit ControlFlowLiveness(ControlFlow const& _controlFlow); std::reference_wrapper controlFlow; - std::unique_ptr mainLiveness; - std::vector> functionLiveness; + std::unique_ptr mainLiveness; + std::vector> functionLiveness; std::string toDot() const; }; diff --git a/libyul/backends/evm/SSACFGLiveness.cpp b/libyul/backends/evm/ssa/LivenessAnalysis.cpp similarity index 93% rename from libyul/backends/evm/SSACFGLiveness.cpp rename to libyul/backends/evm/ssa/LivenessAnalysis.cpp index a0bfeb5bb1..49f077feb3 100644 --- a/libyul/backends/evm/SSACFGLiveness.cpp +++ b/libyul/backends/evm/ssa/LivenessAnalysis.cpp @@ -16,7 +16,7 @@ */ // SPDX-License-Identifier: GPL-3.0 -#include +#include #include @@ -24,7 +24,7 @@ #include #include -using namespace solidity::yul; +using namespace solidity::yul::ssa; namespace { @@ -37,7 +37,7 @@ constexpr auto literalsFilter(SSACFG const& _cfg) } } -std::set SSACFGLiveness::blockExitValues(SSACFG::BlockId const& _blockId) const +std::set LivenessAnalysis::blockExitValues(SSACFG::BlockId const& _blockId) const { std::set result; util::GenericVisitor exitVisitor { @@ -60,7 +60,7 @@ std::set SSACFGLiveness::blockExitValues(SSACFG::BlockId const& return result; } -SSACFGLiveness::SSACFGLiveness(SSACFG const& _cfg): +LivenessAnalysis::LivenessAnalysis(SSACFG const& _cfg): m_cfg(_cfg), m_topologicalSort(_cfg), m_loopNestingForest(m_topologicalSort), @@ -75,7 +75,7 @@ SSACFGLiveness::SSACFGLiveness(SSACFG const& _cfg): fillOperationsLiveOut(); } -void SSACFGLiveness::runDagDfs() +void LivenessAnalysis::runDagDfs() { // SSA Book, Algorithm 9.2 for (auto const blockIdValue: m_topologicalSort.postOrder()) @@ -137,7 +137,7 @@ void SSACFGLiveness::runDagDfs() } } -void SSACFGLiveness::runLoopTreeDfs(size_t const _loopHeader) +void LivenessAnalysis::runLoopTreeDfs(size_t const _loopHeader) { // SSA Book, Algorithm 9.3 if (m_loopNestingForest.loopNodes().count(_loopHeader) > 0) @@ -161,7 +161,7 @@ void SSACFGLiveness::runLoopTreeDfs(size_t const _loopHeader) } } -void SSACFGLiveness::fillOperationsLiveOut() +void LivenessAnalysis::fillOperationsLiveOut() { for (size_t blockIdValue = 0; blockIdValue < m_cfg.numBlocks(); ++blockIdValue) { diff --git a/libyul/backends/evm/SSACFGLiveness.h b/libyul/backends/evm/ssa/LivenessAnalysis.h similarity index 87% rename from libyul/backends/evm/SSACFGLiveness.h rename to libyul/backends/evm/ssa/LivenessAnalysis.h index 6ec0d16884..74b7f20fa1 100644 --- a/libyul/backends/evm/SSACFGLiveness.h +++ b/libyul/backends/evm/ssa/LivenessAnalysis.h @@ -18,25 +18,25 @@ #pragma once -#include -#include -#include +#include +#include +#include #include #include #include -namespace solidity::yul +namespace solidity::yul::ssa { /// Performs liveness analysis on a reducible SSA CFG following Algorithm 9.1 in [1]. /// /// [1] Rastello, Fabrice, and Florent Bouchez Tichadou, eds. SSA-based Compiler Design. Springer, 2022. -class SSACFGLiveness +class LivenessAnalysis { public: using LivenessData = std::set; - explicit SSACFGLiveness(SSACFG const& _cfg); + explicit LivenessAnalysis(SSACFG const& _cfg); LivenessData const& liveIn(SSACFG::BlockId _blockId) const { return m_liveIns[_blockId.value]; } LivenessData const& liveOut(SSACFG::BlockId _blockId) const { return m_liveOuts[_blockId.value]; } diff --git a/libyul/backends/evm/SSAControlFlowGraph.cpp b/libyul/backends/evm/ssa/SSACFG.cpp similarity index 96% rename from libyul/backends/evm/SSAControlFlowGraph.cpp rename to libyul/backends/evm/ssa/SSACFG.cpp index 215fe7358b..1807534248 100644 --- a/libyul/backends/evm/SSAControlFlowGraph.cpp +++ b/libyul/backends/evm/ssa/SSACFG.cpp @@ -16,9 +16,9 @@ */ // SPDX-License-Identifier: GPL-3.0 -#include +#include -#include +#include #include #include @@ -33,18 +33,19 @@ using namespace solidity; using namespace solidity::util; using namespace solidity::yul; +using namespace solidity::yul::ssa; namespace { class SSACFGPrinter { public: - SSACFGPrinter(SSACFG const& _cfg, SSACFG::BlockId _blockId, SSACFGLiveness const* _liveness): + SSACFGPrinter(SSACFG const& _cfg, SSACFG::BlockId _blockId, LivenessAnalysis const* _liveness): m_cfg(_cfg), m_functionIndex(0), m_liveness(_liveness) { printBlock(_blockId); } - SSACFGPrinter(SSACFG const& _cfg, size_t _functionIndex, Scope::Function const& _function, SSACFGLiveness const* _liveness): + SSACFGPrinter(SSACFG const& _cfg, size_t _functionIndex, Scope::Function const& _function, LivenessAnalysis const* _liveness): m_cfg(_cfg), m_functionIndex(_functionIndex), m_liveness(_liveness) { printFunction(_function); @@ -291,7 +292,7 @@ class SSACFGPrinter SSACFG const& m_cfg; size_t m_functionIndex; - SSACFGLiveness const* m_liveness; + LivenessAnalysis const* m_liveness; std::stringstream m_result{}; }; } @@ -299,7 +300,7 @@ class SSACFGPrinter std::string SSACFG::toDot( bool _includeDiGraphDefinition, std::optional _functionIndex, - SSACFGLiveness const* _liveness + LivenessAnalysis const* _liveness ) const { std::ostringstream output; diff --git a/libyul/backends/evm/SSAControlFlowGraph.h b/libyul/backends/evm/ssa/SSACFG.h similarity index 98% rename from libyul/backends/evm/SSAControlFlowGraph.h rename to libyul/backends/evm/ssa/SSACFG.h index ee1bdf3b4e..32768ca6bd 100644 --- a/libyul/backends/evm/SSAControlFlowGraph.h +++ b/libyul/backends/evm/ssa/SSACFG.h @@ -35,9 +35,9 @@ #include #include -namespace solidity::yul +namespace solidity::yul::ssa { -class SSACFGLiveness; +class LivenessAnalysis; class SSACFG { @@ -227,7 +227,7 @@ class SSACFG std::string toDot( bool _includeDiGraphDefinition=true, std::optional _functionIndex=std::nullopt, - SSACFGLiveness const* _liveness=nullptr + LivenessAnalysis const* _liveness=nullptr ) const; private: std::deque m_valueInfos; diff --git a/libyul/backends/evm/SSAControlFlowGraphBuilder.cpp b/libyul/backends/evm/ssa/SSACFGBuilder.cpp similarity index 88% rename from libyul/backends/evm/SSAControlFlowGraphBuilder.cpp rename to libyul/backends/evm/ssa/SSACFGBuilder.cpp index 0af08e39dd..92b49e7fc1 100644 --- a/libyul/backends/evm/SSAControlFlowGraphBuilder.cpp +++ b/libyul/backends/evm/ssa/SSACFGBuilder.cpp @@ -19,9 +19,10 @@ * Transformation of a Yul AST into a control flow graph. */ -#include +#include + +#include -#include #include #include #include @@ -42,11 +43,9 @@ using namespace solidity; using namespace solidity::yul; +using namespace solidity::yul::ssa; -namespace solidity::yul -{ - -SSAControlFlowGraphBuilder::SSAControlFlowGraphBuilder( +SSACFGBuilder::SSACFGBuilder( ControlFlow& _controlFlow, SSACFG& _graph, AsmAnalysisInfo const& _analysisInfo, @@ -63,7 +62,7 @@ SSAControlFlowGraphBuilder::SSAControlFlowGraphBuilder( { } -std::unique_ptr SSAControlFlowGraphBuilder::build( +std::unique_ptr SSACFGBuilder::build( AsmAnalysisInfo const& _analysisInfo, Dialect const& _dialect, Block const& _block, @@ -73,7 +72,7 @@ std::unique_ptr SSAControlFlowGraphBuilder::build( ControlFlowSideEffectsCollector sideEffects(_dialect, _block); auto controlFlow = std::make_unique(); - SSAControlFlowGraphBuilder builder(*controlFlow, *controlFlow->mainGraph, _analysisInfo, sideEffects, _dialect, _keepLiteralAssignments); + SSACFGBuilder builder(*controlFlow, *controlFlow->mainGraph, _analysisInfo, sideEffects, _dialect, _keepLiteralAssignments); builder.m_currentBlock = controlFlow->mainGraph->makeBlock(debugDataOf(_block)); builder.sealBlock(builder.m_currentBlock); builder(_block); @@ -84,7 +83,7 @@ std::unique_ptr SSAControlFlowGraphBuilder::build( return controlFlow; } -SSACFG::ValueId SSAControlFlowGraphBuilder::tryRemoveTrivialPhi(SSACFG::ValueId _phi) +SSACFG::ValueId SSACFGBuilder::tryRemoveTrivialPhi(SSACFG::ValueId _phi) { // TODO: double-check if this is sane auto const* phiInfo = std::get_if(&m_graph.valueInfo(_phi)); @@ -157,7 +156,7 @@ SSACFG::ValueId SSAControlFlowGraphBuilder::tryRemoveTrivialPhi(SSACFG::ValueId } /// Removes edges to blocks that are not reachable. -void SSAControlFlowGraphBuilder::cleanUnreachable() +void SSACFGBuilder::cleanUnreachable() { // Determine which blocks are reachable from the entry. util::BreadthFirstSearch reachabilityCheck{{m_graph.entry}}; @@ -201,7 +200,7 @@ void SSAControlFlowGraphBuilder::cleanUnreachable() } } -void SSAControlFlowGraphBuilder::buildFunctionGraph( +void SSACFGBuilder::buildFunctionGraph( Scope::Function const* _function, FunctionDefinition const* _functionDefinition ) @@ -230,7 +229,7 @@ void SSAControlFlowGraphBuilder::buildFunctionGraph( cfg.arguments = arguments; cfg.returns = returns; - SSAControlFlowGraphBuilder builder(m_controlFlow, cfg, m_info, m_sideEffects, m_dialect, m_keepLiteralAssignments); + SSACFGBuilder builder(m_controlFlow, cfg, m_info, m_sideEffects, m_dialect, m_keepLiteralAssignments); builder.m_currentBlock = cfg.entry; builder.m_functionDefinitions = m_functionDefinitions; for (auto&& [var, varId]: cfg.arguments) @@ -245,7 +244,7 @@ void SSAControlFlowGraphBuilder::buildFunctionGraph( builder.cleanUnreachable(); } -void SSAControlFlowGraphBuilder::operator()(ExpressionStatement const& _expressionStatement) +void SSACFGBuilder::operator()(ExpressionStatement const& _expressionStatement) { auto const* functionCall = std::get_if(&_expressionStatement.expression); yulAssert(functionCall); @@ -253,7 +252,7 @@ void SSAControlFlowGraphBuilder::operator()(ExpressionStatement const& _expressi yulAssert(results.empty()); } -void SSAControlFlowGraphBuilder::operator()(Assignment const& _assignment) +void SSACFGBuilder::operator()(Assignment const& _assignment) { assign( _assignment.variableNames | ranges::views::transform([&](auto& _var) { return std::ref(lookupVariable(_var.name)); }) | ranges::to, @@ -261,7 +260,7 @@ void SSAControlFlowGraphBuilder::operator()(Assignment const& _assignment) ); } -void SSAControlFlowGraphBuilder::operator()(VariableDeclaration const& _variableDeclaration) +void SSACFGBuilder::operator()(VariableDeclaration const& _variableDeclaration) { assign( _variableDeclaration.variables | ranges::views::transform([&](auto& _var) { return std::ref(lookupVariable(_var.name)); }) | ranges::to, @@ -269,13 +268,13 @@ void SSAControlFlowGraphBuilder::operator()(VariableDeclaration const& _variable ); } -void SSAControlFlowGraphBuilder::operator()(FunctionDefinition const& _functionDefinition) +void SSACFGBuilder::operator()(FunctionDefinition const& _functionDefinition) { Scope::Function const& function = lookupFunction(_functionDefinition.name); buildFunctionGraph(&function, &_functionDefinition); } -void SSAControlFlowGraphBuilder::operator()(If const& _if) +void SSACFGBuilder::operator()(If const& _if) { std::optional constantCondition; if (auto const* literalCondition = std::get_if(_if.condition.get())) @@ -306,7 +305,7 @@ void SSAControlFlowGraphBuilder::operator()(If const& _if) } } -void SSAControlFlowGraphBuilder::operator()(Switch const& _switch) +void SSACFGBuilder::operator()(Switch const& _switch) { auto expression = std::visit(*this, *_switch.expression); @@ -418,7 +417,7 @@ void SSAControlFlowGraphBuilder::operator()(Switch const& _switch) sealBlock(afterSwitch); } } -void SSAControlFlowGraphBuilder::operator()(ForLoop const& _loop) +void SSACFGBuilder::operator()(ForLoop const& _loop) { ScopedSaveAndRestore scopeRestore(m_scope, m_info.scopes.at(&_loop.pre).get()); (*this)(_loop.pre); @@ -481,7 +480,7 @@ void SSAControlFlowGraphBuilder::operator()(ForLoop const& _loop) m_currentBlock = afterLoop; } -void SSAControlFlowGraphBuilder::operator()(Break const& _break) +void SSACFGBuilder::operator()(Break const& _break) { yulAssert(!m_forLoopInfo.empty()); auto currentBlockDebugData = debugDataOf(currentBlock()); @@ -490,7 +489,7 @@ void SSAControlFlowGraphBuilder::operator()(Break const& _break) sealBlock(m_currentBlock); } -void SSAControlFlowGraphBuilder::operator()(Continue const& _continue) +void SSACFGBuilder::operator()(Continue const& _continue) { yulAssert(!m_forLoopInfo.empty()); auto currentBlockDebugData = debugDataOf(currentBlock()); @@ -499,7 +498,7 @@ void SSAControlFlowGraphBuilder::operator()(Continue const& _continue) sealBlock(m_currentBlock); } -void SSAControlFlowGraphBuilder::operator()(Leave const& _leaveStatement) +void SSACFGBuilder::operator()(Leave const& _leaveStatement) { auto currentBlockDebugData = debugDataOf(currentBlock()); currentBlock().exit = SSACFG::BasicBlock::FunctionReturn{ @@ -512,7 +511,7 @@ void SSAControlFlowGraphBuilder::operator()(Leave const& _leaveStatement) sealBlock(m_currentBlock); } -void SSAControlFlowGraphBuilder::registerFunctionDefinition(FunctionDefinition const& _functionDefinition) +void SSACFGBuilder::registerFunctionDefinition(FunctionDefinition const& _functionDefinition) { yulAssert(m_scope, ""); yulAssert(m_scope->identifiers.count(_functionDefinition.name), ""); @@ -521,7 +520,7 @@ void SSAControlFlowGraphBuilder::registerFunctionDefinition(FunctionDefinition c m_functionDefinitions.emplace_back(&function, &_functionDefinition); } -void SSAControlFlowGraphBuilder::operator()(Block const& _block) +void SSACFGBuilder::operator()(Block const& _block) { ScopedSaveAndRestore saveScope(m_scope, m_info.scopes.at(&_block).get()); // gather all function definitions so that they are visible to each other's subgraphs @@ -535,25 +534,25 @@ void SSAControlFlowGraphBuilder::operator()(Block const& _block) std::visit(*this, statement); } -SSACFG::ValueId SSAControlFlowGraphBuilder::operator()(FunctionCall const& _call) +SSACFG::ValueId SSACFGBuilder::operator()(FunctionCall const& _call) { auto results = visitFunctionCall(_call); yulAssert(results.size() == 1); return results.front(); } -SSACFG::ValueId SSAControlFlowGraphBuilder::operator()(Identifier const& _identifier) +SSACFG::ValueId SSACFGBuilder::operator()(Identifier const& _identifier) { auto const& var = lookupVariable(_identifier.name); return readVariable(var, m_currentBlock); } -SSACFG::ValueId SSAControlFlowGraphBuilder::operator()(Literal const& _literal) +SSACFG::ValueId SSACFGBuilder::operator()(Literal const& _literal) { return m_graph.newLiteral(debugDataOf(currentBlock()), _literal.value.value()); } -void SSAControlFlowGraphBuilder::assign(std::vector> _variables, Expression const* _expression) +void SSACFGBuilder::assign(std::vector> _variables, Expression const* _expression) { auto rhs = [&]() -> std::vector { if (auto const* functionCall = std::get_if(_expression)) @@ -582,7 +581,7 @@ void SSAControlFlowGraphBuilder::assign(std::vector SSAControlFlowGraphBuilder::visitFunctionCall(FunctionCall const& _call) +std::vector SSACFGBuilder::visitFunctionCall(FunctionCall const& _call) { bool canContinue = true; SSACFG::Operation operation = std::visit(util::GenericVisitor{ @@ -624,19 +623,19 @@ std::vector SSAControlFlowGraphBuilder::visitFunctionCall(Funct return results; } -SSACFG::ValueId SSAControlFlowGraphBuilder::zero() +SSACFG::ValueId SSACFGBuilder::zero() { return m_graph.newLiteral(debugDataOf(currentBlock()), 0u); } -SSACFG::ValueId SSAControlFlowGraphBuilder::readVariable(Scope::Variable const& _variable, SSACFG::BlockId _block) +SSACFG::ValueId SSACFGBuilder::readVariable(Scope::Variable const& _variable, SSACFG::BlockId _block) { if (auto const& def = currentDef(_variable, _block)) return *def; return readVariableRecursive(_variable, _block); } -SSACFG::ValueId SSAControlFlowGraphBuilder::readVariableRecursive(Scope::Variable const& _variable, SSACFG::BlockId _block) +SSACFG::ValueId SSACFGBuilder::readVariableRecursive(Scope::Variable const& _variable, SSACFG::BlockId _block) { auto& block = m_graph.block(_block); auto& info = blockInfo(_block); @@ -666,7 +665,7 @@ SSACFG::ValueId SSAControlFlowGraphBuilder::readVariableRecursive(Scope::Variabl return val; } -SSACFG::ValueId SSAControlFlowGraphBuilder::addPhiOperands(Scope::Variable const& _variable, SSACFG::ValueId _phi) +SSACFG::ValueId SSACFGBuilder::addPhiOperands(Scope::Variable const& _variable, SSACFG::ValueId _phi) { yulAssert(std::holds_alternative(m_graph.valueInfo(_phi))); auto& phi = std::get(m_graph.valueInfo(_phi)); @@ -676,12 +675,12 @@ SSACFG::ValueId SSAControlFlowGraphBuilder::addPhiOperands(Scope::Variable const return _phi; } -void SSAControlFlowGraphBuilder::writeVariable(Scope::Variable const& _variable, SSACFG::BlockId _block, SSACFG::ValueId _value) +void SSACFGBuilder::writeVariable(Scope::Variable const& _variable, SSACFG::BlockId _block, SSACFG::ValueId _value) { currentDef(_variable, _block) = _value; } -Scope::Function const& SSAControlFlowGraphBuilder::lookupFunction(YulName _name) const +Scope::Function const& SSACFGBuilder::lookupFunction(YulName _name) const { Scope::Function const* function = nullptr; yulAssert(m_scope->lookup(_name, util::GenericVisitor{ @@ -692,7 +691,7 @@ Scope::Function const& SSAControlFlowGraphBuilder::lookupFunction(YulName _name) return *function; } -Scope::Variable const& SSAControlFlowGraphBuilder::lookupVariable(YulName _name) const +Scope::Variable const& SSACFGBuilder::lookupVariable(YulName _name) const { yulAssert(m_scope, ""); Scope::Variable const* var = nullptr; @@ -710,7 +709,7 @@ Scope::Variable const& SSAControlFlowGraphBuilder::lookupVariable(YulName _name) yulAssert(false, "External identifier access unimplemented."); } -void SSAControlFlowGraphBuilder::sealBlock(SSACFG::BlockId _block) +void SSACFGBuilder::sealBlock(SSACFG::BlockId _block) { // this method deviates from Algorithm 4 in the reference paper, // as it would lead to tryRemoveTrivialPhi being called on unsealed blocks @@ -724,7 +723,7 @@ void SSAControlFlowGraphBuilder::sealBlock(SSACFG::BlockId _block) } -void SSAControlFlowGraphBuilder::conditionalJump( +void SSACFGBuilder::conditionalJump( langutil::DebugData::ConstPtr _debugData, SSACFG::ValueId _condition, SSACFG::BlockId _nonZero, @@ -742,7 +741,7 @@ void SSAControlFlowGraphBuilder::conditionalJump( m_currentBlock = {}; } -void SSAControlFlowGraphBuilder::jump( +void SSACFGBuilder::jump( langutil::DebugData::ConstPtr _debugData, SSACFG::BlockId _target ) @@ -753,7 +752,7 @@ void SSAControlFlowGraphBuilder::jump( m_currentBlock = _target; } -void SSAControlFlowGraphBuilder::tableJump( +void SSACFGBuilder::tableJump( langutil::DebugData::ConstPtr _debugData, SSACFG::ValueId _value, std::map _cases, @@ -770,7 +769,7 @@ void SSAControlFlowGraphBuilder::tableJump( m_currentBlock = {}; } -FunctionDefinition const* SSAControlFlowGraphBuilder::findFunctionDefinition(Scope::Function const* _function) const +FunctionDefinition const* SSACFGBuilder::findFunctionDefinition(Scope::Function const* _function) const { auto it = std::find_if( m_functionDefinitions.begin(), @@ -781,5 +780,3 @@ FunctionDefinition const* SSAControlFlowGraphBuilder::findFunctionDefinition(Sco return std::get<1>(*it); return nullptr; } - -} diff --git a/libyul/backends/evm/SSAControlFlowGraphBuilder.h b/libyul/backends/evm/ssa/SSACFGBuilder.h similarity index 93% rename from libyul/backends/evm/SSAControlFlowGraphBuilder.h rename to libyul/backends/evm/ssa/SSACFGBuilder.h index 8cd2141462..7b497db2ac 100644 --- a/libyul/backends/evm/SSAControlFlowGraphBuilder.h +++ b/libyul/backends/evm/ssa/SSACFGBuilder.h @@ -29,17 +29,17 @@ */ #pragma once +#include #include -#include -#include +#include #include -namespace solidity::yul +namespace solidity::yul::ssa { -class SSAControlFlowGraphBuilder +class SSACFGBuilder { - SSAControlFlowGraphBuilder( + SSACFGBuilder( ControlFlow& _controlFlow, SSACFG& _graph, AsmAnalysisInfo const& _analysisInfo, @@ -48,8 +48,8 @@ class SSAControlFlowGraphBuilder bool _keepLiteralAssignments ); public: - SSAControlFlowGraphBuilder(SSAControlFlowGraphBuilder const&) = delete; - SSAControlFlowGraphBuilder& operator=(SSAControlFlowGraphBuilder const&) = delete; + SSACFGBuilder(SSACFGBuilder const&) = delete; + SSACFGBuilder& operator=(SSACFGBuilder const&) = delete; static std::unique_ptr build( AsmAnalysisInfo const& _analysisInfo, Dialect const& _dialect, diff --git a/libyul/YulControlFlowGraphExporter.cpp b/libyul/backends/evm/ssa/SSACFGJsonExporter.cpp similarity index 86% rename from libyul/YulControlFlowGraphExporter.cpp rename to libyul/backends/evm/ssa/SSACFGJsonExporter.cpp index f0ad055f25..58a72d05af 100644 --- a/libyul/YulControlFlowGraphExporter.cpp +++ b/libyul/backends/evm/ssa/SSACFGJsonExporter.cpp @@ -16,8 +16,8 @@ */ // SPDX-License-Identifier: GPL-3.0 +#include #include -#include #include #include @@ -27,15 +27,13 @@ #include using namespace solidity; -using namespace solidity::langutil; -using namespace solidity::util; -using namespace solidity::yul; +using namespace solidity::yul::ssa; -YulControlFlowGraphExporter::YulControlFlowGraphExporter(ControlFlow const& _controlFlow, ControlFlowLiveness const* _liveness): m_controlFlow(_controlFlow), m_liveness(_liveness) +SSACFGJsonExporter::SSACFGJsonExporter(ControlFlow const& _controlFlow, ControlFlowLiveness const* _liveness): m_controlFlow(_controlFlow), m_liveness(_liveness) { } -std::string YulControlFlowGraphExporter::varToString(SSACFG const& _cfg, SSACFG::ValueId _var) +std::string SSACFGJsonExporter::varToString(SSACFG const& _cfg, SSACFG::ValueId _var) { if (_var.value == std::numeric_limits::max()) return std::string("INVALID"); @@ -56,7 +54,7 @@ std::string YulControlFlowGraphExporter::varToString(SSACFG const& _cfg, SSACFG: ); } -Json YulControlFlowGraphExporter::run() +Json SSACFGJsonExporter::run() { if (m_liveness) yulAssert(&m_liveness->controlFlow.get() == &m_controlFlow); @@ -73,7 +71,7 @@ Json YulControlFlowGraphExporter::run() return yulObjectJson; } -Json YulControlFlowGraphExporter::exportFunction(SSACFG const& _cfg, SSACFGLiveness const* _liveness) +Json SSACFGJsonExporter::exportFunction(SSACFG const& _cfg, LivenessAnalysis const* _liveness) { Json functionJson = Json::object(); functionJson["type"] = "Function"; @@ -85,7 +83,7 @@ Json YulControlFlowGraphExporter::exportFunction(SSACFG const& _cfg, SSACFGLiven return functionJson; } -Json YulControlFlowGraphExporter::exportBlock(SSACFG const& _cfg, SSACFG::BlockId _entryId, SSACFGLiveness const* _liveness) +Json SSACFGJsonExporter::exportBlock(SSACFG const& _cfg, SSACFG::BlockId _entryId, LivenessAnalysis const* _liveness) { Json blocksJson = Json::array(); util::BreadthFirstSearch bfs{{{_entryId}}}; @@ -132,7 +130,7 @@ Json YulControlFlowGraphExporter::exportBlock(SSACFG const& _cfg, SSACFG::BlockI return blocksJson; } -Json YulControlFlowGraphExporter::toJson(SSACFG const& _cfg, SSACFG::BlockId _blockId, SSACFGLiveness const* _liveness) +Json SSACFGJsonExporter::toJson(SSACFG const& _cfg, SSACFG::BlockId _blockId, LivenessAnalysis const* _liveness) { auto const valueToString = [&](SSACFG::ValueId const& valueId) { return varToString(_cfg, valueId); }; @@ -174,10 +172,10 @@ Json YulControlFlowGraphExporter::toJson(SSACFG const& _cfg, SSACFG::BlockId _bl return blockJson; } -Json YulControlFlowGraphExporter::toJson(Json& _ret, SSACFG const& _cfg, SSACFG::Operation const& _operation) +Json SSACFGJsonExporter::toJson(Json& _ret, SSACFG const& _cfg, SSACFG::Operation const& _operation) { Json opJson = Json::object(); - std::visit(GenericVisitor{ + std::visit(util::GenericVisitor{ [&](SSACFG::Call const& _call) { _ret["type"] = "FunctionCall"; @@ -222,7 +220,7 @@ Json YulControlFlowGraphExporter::toJson(Json& _ret, SSACFG const& _cfg, SSACFG: return opJson; } -Json YulControlFlowGraphExporter::toJson(SSACFG const& _cfg, std::vector const& _values) +Json SSACFGJsonExporter::toJson(SSACFG const& _cfg, std::vector const& _values) { Json ret = Json::array(); for (auto const& value: _values) diff --git a/libyul/YulControlFlowGraphExporter.h b/libyul/backends/evm/ssa/SSACFGJsonExporter.h similarity index 72% rename from libyul/YulControlFlowGraphExporter.h rename to libyul/backends/evm/ssa/SSACFGJsonExporter.h index 91f9d040e4..7198595812 100644 --- a/libyul/YulControlFlowGraphExporter.h +++ b/libyul/backends/evm/ssa/SSACFGJsonExporter.h @@ -18,26 +18,28 @@ #pragma once -#include +#include #include #include -using namespace solidity; -using namespace yul; +namespace solidity::yul::ssa +{ -class YulControlFlowGraphExporter +class SSACFGJsonExporter { public: - YulControlFlowGraphExporter(ControlFlow const& _controlFlow, ControlFlowLiveness const* _liveness=nullptr); + SSACFGJsonExporter(ControlFlow const& _controlFlow, ControlFlowLiveness const* _liveness=nullptr); Json run(); - Json exportBlock(SSACFG const& _cfg, SSACFG::BlockId _blockId, SSACFGLiveness const* _liveness); - Json exportFunction(SSACFG const& _cfg, SSACFGLiveness const* _liveness); + Json exportBlock(SSACFG const& _cfg, SSACFG::BlockId _blockId, LivenessAnalysis const* _liveness); + Json exportFunction(SSACFG const& _cfg, LivenessAnalysis const* _liveness); std::string varToString(SSACFG const& _cfg, SSACFG::ValueId _var); private: ControlFlow const& m_controlFlow; ControlFlowLiveness const* m_liveness; - Json toJson(SSACFG const& _cfg, SSACFG::BlockId _blockId, SSACFGLiveness const* _liveness); + Json toJson(SSACFG const& _cfg, SSACFG::BlockId _blockId, LivenessAnalysis const* _liveness); Json toJson(Json& _ret, SSACFG const& _cfg, SSACFG::Operation const& _operation); Json toJson(SSACFG const& _cfg, std::vector const& _values); }; + +} diff --git a/libyul/backends/evm/SSACFGLoopNestingForest.cpp b/libyul/backends/evm/ssa/SSACFGLoopNestingForest.cpp similarity index 96% rename from libyul/backends/evm/SSACFGLoopNestingForest.cpp rename to libyul/backends/evm/ssa/SSACFGLoopNestingForest.cpp index b285978134..bbd171a452 100644 --- a/libyul/backends/evm/SSACFGLoopNestingForest.cpp +++ b/libyul/backends/evm/ssa/SSACFGLoopNestingForest.cpp @@ -16,9 +16,9 @@ */ // SPDX-License-Identifier: GPL-3.0 -#include +#include -using namespace solidity::yul; +using namespace solidity::yul::ssa; SSACFGLoopNestingForest::SSACFGLoopNestingForest(ForwardSSACFGTopologicalSort const& _sort): m_sort(_sort), diff --git a/libyul/backends/evm/SSACFGLoopNestingForest.h b/libyul/backends/evm/ssa/SSACFGLoopNestingForest.h similarity index 93% rename from libyul/backends/evm/SSACFGLoopNestingForest.h rename to libyul/backends/evm/ssa/SSACFGLoopNestingForest.h index 0aad6ab396..56cd5ace74 100644 --- a/libyul/backends/evm/SSACFGLoopNestingForest.h +++ b/libyul/backends/evm/ssa/SSACFGLoopNestingForest.h @@ -18,8 +18,8 @@ #pragma once -#include -#include +#include +#include #include @@ -27,7 +27,7 @@ #include #include -namespace solidity::yul +namespace solidity::yul::ssa { /// Constructs a loop nesting forest for an SSACFG using Tarjan's algorithm [1]. diff --git a/libyul/backends/evm/SSACFGTopologicalSort.cpp b/libyul/backends/evm/ssa/SSACFGTopologicalSort.cpp similarity index 96% rename from libyul/backends/evm/SSACFGTopologicalSort.cpp rename to libyul/backends/evm/ssa/SSACFGTopologicalSort.cpp index 881e48ea1b..222a9774a8 100644 --- a/libyul/backends/evm/SSACFGTopologicalSort.cpp +++ b/libyul/backends/evm/ssa/SSACFGTopologicalSort.cpp @@ -16,9 +16,9 @@ */ // SPDX-License-Identifier: GPL-3.0 -#include +#include -using namespace solidity::yul; +using namespace solidity::yul::ssa; ForwardSSACFGTopologicalSort::ForwardSSACFGTopologicalSort(SSACFG const& _cfg): m_cfg(_cfg), diff --git a/libyul/backends/evm/SSACFGTopologicalSort.h b/libyul/backends/evm/ssa/SSACFGTopologicalSort.h similarity index 96% rename from libyul/backends/evm/SSACFGTopologicalSort.h rename to libyul/backends/evm/ssa/SSACFGTopologicalSort.h index 025054b950..5eb12545d7 100644 --- a/libyul/backends/evm/SSACFGTopologicalSort.h +++ b/libyul/backends/evm/ssa/SSACFGTopologicalSort.h @@ -18,13 +18,13 @@ #pragma once -#include +#include #include #include #include -namespace solidity::yul +namespace solidity::yul::ssa { /// Performs a topological sort on the forward CFG (no back/cross edges) diff --git a/test/libyul/SSAControlFlowGraphTest.cpp b/test/libyul/SSAControlFlowGraphTest.cpp index 087a79e974..18f1c7c258 100644 --- a/test/libyul/SSAControlFlowGraphTest.cpp +++ b/test/libyul/SSAControlFlowGraphTest.cpp @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include @@ -70,13 +70,13 @@ TestCase::TestResult SSAControlFlowGraphTest::run(std::ostream& _stream, std::st return TestResult::FatalError; } - std::unique_ptr controlFlow = SSAControlFlowGraphBuilder::build( + std::unique_ptr controlFlow = ssa::SSACFGBuilder::build( *yulStack.parserResult()->analysisInfo, yulStack.dialect(), yulStack.parserResult()->code()->root(), true ); - ControlFlowLiveness liveness(*controlFlow); + ssa::ControlFlowLiveness liveness(*controlFlow); m_obtainedResult = controlFlow->toDot(&liveness); auto result = checkResult(_stream, _linePrefix, _formatted); From 2f5dc0d5fc2bedc268b0979dec0a591f0a197538 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 29 Sep 2025 17:10:09 +0200 Subject: [PATCH 021/243] SSA CFG liveness contains (max) variable usage counts. If a variable is referenced downstream, a counter gets incremented. If it gets used different amount of times in different branches, we take the max of these. The number reflects how many times a value is going to be used downstream _at most_. --- libyul/backends/evm/ssa/LivenessAnalysis.cpp | 209 +++++++++++++++--- libyul/backends/evm/ssa/LivenessAnalysis.h | 84 ++++++- libyul/backends/evm/ssa/SSACFG.cpp | 4 +- .../backends/evm/ssa/SSACFGJsonExporter.cpp | 2 +- .../standard_yul_cfg_json_export/output.json | 20 +- test/cmdlineTests/yul_cfg_json_export/output | 20 +- .../libyul/yulSSAControlFlowGraph/complex.yul | 50 ++--- .../yulSSAControlFlowGraph/complex2.yul | 52 ++--- .../default_initialized_variable.yul | 6 +- .../yulSSAControlFlowGraph/function.yul | 8 +- test/libyul/yulSSAControlFlowGraph/if.yul | 6 +- .../yulSSAControlFlowGraph/nested_for.yul | 94 ++++---- .../nested_function.yul | 24 +- test/libyul/yulSSAControlFlowGraph/switch.yul | 12 +- 14 files changed, 399 insertions(+), 192 deletions(-) diff --git a/libyul/backends/evm/ssa/LivenessAnalysis.cpp b/libyul/backends/evm/ssa/LivenessAnalysis.cpp index 49f077feb3..42d2cf39c3 100644 --- a/libyul/backends/evm/ssa/LivenessAnalysis.cpp +++ b/libyul/backends/evm/ssa/LivenessAnalysis.cpp @@ -20,7 +20,10 @@ #include +#include +#include #include + #include #include @@ -28,38 +31,154 @@ using namespace solidity::yul::ssa; namespace { -constexpr auto literalsFilter(SSACFG const& _cfg) +constexpr auto excludingLiteralsFilter(SSACFG const& _cfg) +{ + return [&_cfg](LivenessAnalysis::LivenessData::Value const& _valueId) -> bool + { + return !std::holds_alternative(_cfg.valueInfo(_valueId)); + }; +} +constexpr auto unreachableFilter(SSACFG const& _cfg) { - return [&_cfg](SSACFG::ValueId const& _valueId) -> bool + return [&_cfg](LivenessAnalysis::LivenessData::Value const& _valueId) -> bool { - return !std::holds_alternative(_cfg.valueInfo(_valueId));; + return std::holds_alternative(_cfg.valueInfo(_valueId)); }; } } -std::set LivenessAnalysis::blockExitValues(SSACFG::BlockId const& _blockId) const +bool LivenessAnalysis::LivenessData::contains(Value const& _valueId) const +{ + return findEntry(_valueId) != m_liveCounts.end(); +} + +LivenessAnalysis::LivenessData::Count LivenessAnalysis::LivenessData::count(Value const& _valueId) const +{ + if ( + auto const it = findEntry(_valueId); + it != m_liveCounts.end() + ) + return it->second; + return 0; +} + +LivenessAnalysis::LivenessData::LiveCounts::const_iterator LivenessAnalysis::LivenessData::begin() const +{ + return m_liveCounts.begin(); +} + +LivenessAnalysis::LivenessData::LiveCounts::const_iterator LivenessAnalysis::LivenessData::end() const +{ + return m_liveCounts.end(); +} + +LivenessAnalysis::LivenessData::LiveCounts::size_type LivenessAnalysis::LivenessData::size() const +{ + return m_liveCounts.size(); +} + +bool LivenessAnalysis::LivenessData::empty() const { return m_liveCounts.empty(); } + +void LivenessAnalysis::LivenessData::insert(Value const& _value, Count _count) +{ + if (_count == 0) + return; + + auto it = findEntry(_value); + if (it != m_liveCounts.end()) + it->second += _count; + else + m_liveCounts.emplace_back(_value, _count); +} + +LivenessAnalysis::LivenessData& LivenessAnalysis::LivenessData::maxUnion(LivenessData const& _other) +{ + for (auto const& [value, count]: _other.m_liveCounts) + { + auto it = findEntry(value); + if (it != m_liveCounts.end()) + it->second = std::max(it->second, count); + else + m_liveCounts.emplace_back(value, count); + } + return *this; +} + +LivenessAnalysis::LivenessData& LivenessAnalysis::LivenessData::operator+=(LivenessData const& _other) { - std::set result; - util::GenericVisitor exitVisitor { + for (auto const& [valueId, count]: _other.m_liveCounts) + insert(valueId, count); + return *this; +} + +LivenessAnalysis::LivenessData& LivenessAnalysis::LivenessData::operator-=(LivenessData const& _other) +{ + std::erase_if(m_liveCounts, [&](auto const& entry) { return _other.contains(entry.first); }); + return *this; +} + +void LivenessAnalysis::LivenessData::erase(Value const& _value) +{ + if ( + auto const it = findEntry(_value); + it != m_liveCounts.end() + ) + m_liveCounts.erase(it); +} + +void LivenessAnalysis::LivenessData::remove(Value const& _value, Count _count) +{ + if (_count == 0) + return; + + auto it = findEntry(_value); + if (it != m_liveCounts.end()) + { + if (it->second <= _count) + m_liveCounts.erase(it); + else + it->second -= _count; + } +} + + +LivenessAnalysis::LivenessData LivenessAnalysis::blockExitValues(SSACFG::BlockId const& _blockId) const +{ + LivenessData result; + util::GenericVisitor exitVisitor{ [](SSACFG::BasicBlock::MainExit const&) {}, - [&](SSACFG::BasicBlock::FunctionReturn const& _functionReturn) { - result += _functionReturn.returnValues | ranges::views::filter(literalsFilter(m_cfg)); + [&](SSACFG::BasicBlock::FunctionReturn const& _functionReturn) + { + for (auto const& valueId: _functionReturn.returnValues | ranges::views::filter(excludingLiteralsFilter(m_cfg))) + result.insert(valueId); }, - [&](SSACFG::BasicBlock::JumpTable const& _jt) { - if (literalsFilter(m_cfg)(_jt.value)) - result.emplace(_jt.value); + [&](SSACFG::BasicBlock::JumpTable const& _jt) + { + if (excludingLiteralsFilter(m_cfg)(_jt.value)) + result.insert(_jt.value); }, [](SSACFG::BasicBlock::Jump const&) {}, - [&](SSACFG::BasicBlock::ConditionalJump const& _conditionalJump) { - if (literalsFilter(m_cfg)(_conditionalJump.condition)) - result.emplace(_conditionalJump.condition); + [&](SSACFG::BasicBlock::ConditionalJump const& _conditionalJump) + { + if (excludingLiteralsFilter(m_cfg)(_conditionalJump.condition)) + result.insert(_conditionalJump.condition); }, - [](SSACFG::BasicBlock::Terminated const&) {} - }; + [](SSACFG::BasicBlock::Terminated const&) {}}; std::visit(exitVisitor, m_cfg.block(_blockId).exit); return result; } + +LivenessAnalysis::LivenessData::LiveCounts::iterator LivenessAnalysis::LivenessData::findEntry(Value const& _value) +{ + return ranges::find_if(m_liveCounts, [&](auto const& _entry) { return _entry.first == _value; }); +} + +LivenessAnalysis::LivenessData::LiveCounts::const_iterator LivenessAnalysis::LivenessData::findEntry(Value const& _value) const +{ + return ranges::find_if(m_liveCounts, [&](auto const& _entry) { return _entry.first == _value; }); +} + LivenessAnalysis::LivenessAnalysis(SSACFG const& _cfg): m_cfg(_cfg), m_topologicalSort(_cfg), @@ -75,6 +194,14 @@ LivenessAnalysis::LivenessAnalysis(SSACFG const& _cfg): fillOperationsLiveOut(); } +LivenessAnalysis::LivenessData LivenessAnalysis::used(SSACFG::BlockId const _blockId) const +{ + auto used = liveIn(_blockId); + for (auto const& [valueId, count]: liveOut(_blockId)) + used.remove(valueId, count); + return used; +} + void LivenessAnalysis::runDagDfs() { // SSA Book, Algorithm 9.2 @@ -85,7 +212,7 @@ void LivenessAnalysis::runDagDfs() auto const& block = m_cfg.block(blockId); // live <- PhiUses(B) - std::set live{}; + LivenessData live{}; block.forEachExit( [&](SSACFG::BlockId const& _successor) { @@ -105,15 +232,21 @@ void LivenessAnalysis::runDagDfs() block.forEachExit( [&](SSACFG::BlockId const& _successor) { if (!m_topologicalSort.backEdge(blockId, _successor)) - live += m_liveIns[_successor.value] - m_cfg.block(_successor).phis; + { + // LiveIn(S) - PhiDefs(S) + auto liveInWithoutPhiDefs = m_liveIns[_successor.value]; + for (auto const& phiId: m_cfg.block(_successor).phis) + liveInWithoutPhiDefs.erase(phiId); + live.maxUnion(liveInWithoutPhiDefs); + } }); if (std::holds_alternative(block.exit)) - live += std::get(block.exit).returnValues - | ranges::views::filter(literalsFilter(m_cfg)); + for (auto const& returnValue: std::get(block.exit).returnValues | ranges::views::filter(excludingLiteralsFilter(m_cfg))) + live.insert(returnValue); // clean out unreachables - live = live | ranges::views::filter([&](auto const& valueId) { return !std::holds_alternative(m_cfg.valueInfo(valueId)); }) | ranges::to; + live.eraseIf([&](auto const& _entry) { return unreachableFilter(m_cfg)(_entry.first); }); // LiveOut(B) <- live m_liveOuts[blockId.value] = live; @@ -126,35 +259,39 @@ void LivenessAnalysis::runDagDfs() for (auto const& op: block.operations | ranges::views::reverse) { // remove variables defined at p from live - live -= op.outputs | ranges::views::filter(literalsFilter(m_cfg)) | ranges::to; + live.eraseAll(op.outputs | ranges::views::filter(excludingLiteralsFilter(m_cfg)) | ranges::to); // add uses at p to live - live += op.inputs | ranges::views::filter(literalsFilter(m_cfg)) | ranges::to; + live.insertAll(op.inputs | ranges::views::filter(excludingLiteralsFilter(m_cfg)) | ranges::to); } } // livein(b) <- live \cup PhiDefs(B) - m_liveIns[blockId.value] = live + block.phis; + for (auto const& phi: block.phis) + live.insert(phi); + m_liveIns[blockId.value] = live; } } void LivenessAnalysis::runLoopTreeDfs(size_t const _loopHeader) { // SSA Book, Algorithm 9.3 - if (m_loopNestingForest.loopNodes().count(_loopHeader) > 0) + if (m_loopNestingForest.loopNodes().contains(_loopHeader)) { // the loop header block id auto const& block = m_cfg.block(SSACFG::BlockId{_loopHeader}); // LiveLoop <- LiveIn(B_N) - PhiDefs(B_N) - auto liveLoop = m_liveIns[_loopHeader] - block.phis; + auto liveLoop = m_liveIns[_loopHeader]; + for (auto const& phi: block.phis) + liveLoop.erase(phi); // must be live out of header if live in of children - m_liveOuts[_loopHeader] += liveLoop; + m_liveOuts[_loopHeader].maxUnion(liveLoop); // for each blockId \in children(loopHeader) for (size_t blockIdValue = 0; blockIdValue < m_cfg.numBlocks(); ++blockIdValue) if (m_loopNestingForest.loopParents()[blockIdValue] == _loopHeader) { // propagate loop liveness information down to the loop header's children - m_liveIns[blockIdValue] += liveLoop; - m_liveOuts[blockIdValue] += liveLoop; + m_liveIns[blockIdValue].maxUnion(liveLoop); + m_liveOuts[blockIdValue].maxUnion(liveLoop); runLoopTreeDfs(blockIdValue); } @@ -163,21 +300,23 @@ void LivenessAnalysis::runLoopTreeDfs(size_t const _loopHeader) void LivenessAnalysis::fillOperationsLiveOut() { - for (size_t blockIdValue = 0; blockIdValue < m_cfg.numBlocks(); ++blockIdValue) + for (SSACFG::BlockId blockId{0}; blockId.value < m_cfg.numBlocks(); ++blockId.value) { - SSACFG::BlockId const blockId{blockIdValue}; auto const& operations = m_cfg.block(blockId).operations; - auto& liveOuts = m_operationLiveOuts[blockIdValue]; + auto& liveOuts = m_operationLiveOuts[blockId.value]; liveOuts.resize(operations.size()); if (!operations.empty()) { - auto live = m_liveOuts[blockIdValue] + blockExitValues(blockId); + auto live = m_liveOuts[blockId.value]; + live += blockExitValues(blockId); auto rit = liveOuts.rbegin(); for (auto const& op: operations | ranges::views::reverse) { *rit = live; - live -= op.outputs | ranges::views::filter(literalsFilter(m_cfg)) | ranges::to; - live += op.inputs | ranges::views::filter(literalsFilter(m_cfg)) | ranges::to; + for (auto const& output: op.outputs | ranges::views::filter(excludingLiteralsFilter(m_cfg))) + live.erase(output); + for (auto const& input: op.inputs | ranges::views::filter(excludingLiteralsFilter(m_cfg))) + live.insert(input); ++rit; } } diff --git a/libyul/backends/evm/ssa/LivenessAnalysis.h b/libyul/backends/evm/ssa/LivenessAnalysis.h index 74b7f20fa1..6462af86d8 100644 --- a/libyul/backends/evm/ssa/LivenessAnalysis.h +++ b/libyul/backends/evm/ssa/LivenessAnalysis.h @@ -22,8 +22,6 @@ #include #include -#include -#include #include namespace solidity::yul::ssa @@ -35,19 +33,89 @@ namespace solidity::yul::ssa class LivenessAnalysis { public: - using LivenessData = std::set; + class LivenessData + { + public: + using Count = std::uint32_t; + using Value = SSACFG::ValueId; + using LiveCounts = std::vector>; + + LivenessData() = default; + template Sentinel> + LivenessData(Iter begin, Sentinel end): m_liveCounts(begin, end) { + } + explicit LivenessData(LiveCounts&& _liveCounts): m_liveCounts(std::move(_liveCounts)) {} + + bool contains(Value const& _valueId) const; + Count count(Value const& _valueId) const; + LiveCounts::const_iterator begin() const; + LiveCounts::const_iterator end() const; + LiveCounts::size_type size() const; + bool empty() const; + + // Core modification + /// Add value with count (default 1), incrementing if already present + void insert(Value const& _value, Count _count = 1); + /// Remove value completely regardless of count + void erase(Value const& _value); + /// Decrement value count, removing if count reaches zero + void remove(Value const& _value, Count _count = 1); + + // Set operations + /// Add all entries from other, summing counts + LivenessData& operator+=(LivenessData const& _other); + /// Remove all values present in other + LivenessData& operator-=(LivenessData const& _other); + /// Union with other, taking max count for each value + LivenessData& maxUnion(LivenessData const& _other); + + // Bulk operations + /// Insert all values from range with count 1 each + template + void insertAll(Range const& _values) + { + for (auto const& value: _values) + insert(value); + } + + /// Erase all values from range + template + void eraseAll(Range const& _values) + { + for (auto const& value: _values) + erase(value); + } + + // Conditional removal + /// Remove all entries matching predicate + template + void eraseIf(Predicate&& _predicate) + { + std::erase_if(m_liveCounts, std::forward(_predicate)); + } + + private: + LiveCounts::iterator findEntry(Value const& _value); + LiveCounts::const_iterator findEntry(Value const& _value) const; + + /// Usage counts represent the total number of times each variable will be used + /// downstream across all possible execution paths from this program point. + LiveCounts m_liveCounts; + }; explicit LivenessAnalysis(SSACFG const& _cfg); - LivenessData const& liveIn(SSACFG::BlockId _blockId) const { return m_liveIns[_blockId.value]; } - LivenessData const& liveOut(SSACFG::BlockId _blockId) const { return m_liveOuts[_blockId.value]; } + LivenessData const& liveIn(SSACFG::BlockId const _blockId) const { return m_liveIns[_blockId.value]; } + LivenessData const& liveOut(SSACFG::BlockId const _blockId) const { return m_liveOuts[_blockId.value]; } + LivenessData used(SSACFG::BlockId _blockId) const; std::vector const& operationsLiveOut(SSACFG::BlockId _blockId) const { return m_operationLiveOuts[_blockId.value]; } ForwardSSACFGTopologicalSort const& topologicalSort() const { return m_topologicalSort; } -private: + SSACFG const& cfg() const { return m_cfg; } +private: void runDagDfs(); - void runLoopTreeDfs(size_t _loopHeader); + void runLoopTreeDfs(std::size_t _loopHeader); void fillOperationsLiveOut(); - std::set blockExitValues(SSACFG::BlockId const& _blockId) const; + LivenessData blockExitValues(SSACFG::BlockId const& _blockId) const; SSACFG const& m_cfg; ForwardSSACFGTopologicalSort m_topologicalSort; diff --git a/libyul/backends/evm/ssa/SSACFG.cpp b/libyul/backends/evm/ssa/SSACFG.cpp index 1807534248..9a98a08ba5 100644 --- a/libyul/backends/evm/ssa/SSACFG.cpp +++ b/libyul/backends/evm/ssa/SSACFG.cpp @@ -145,11 +145,11 @@ class SSACFGPrinter ); m_result << fmt::format( "LiveIn: {}\\l\\\n", - fmt::join(m_liveness->liveIn(_id) | ranges::views::transform(valueToString), ",") + fmt::join(m_liveness->liveIn(_id) | ranges::views::transform([&](auto const& liveIn) { return valueToString(SSACFG::ValueId{liveIn.first}) + fmt::format("[{}]", liveIn.second); }), ", ") ); m_result << fmt::format( "LiveOut: {}\\l\\n", - fmt::join(m_liveness->liveOut(_id) | ranges::views::transform(valueToString), ",") + fmt::join(m_liveness->liveOut(_id) | ranges::views::transform([&](auto const& liveOut) { return valueToString(SSACFG::ValueId{liveOut.first}) + fmt::format("[{}]", liveOut.second); }), ", ") ); } else diff --git a/libyul/backends/evm/ssa/SSACFGJsonExporter.cpp b/libyul/backends/evm/ssa/SSACFGJsonExporter.cpp index 58a72d05af..68f65d377e 100644 --- a/libyul/backends/evm/ssa/SSACFGJsonExporter.cpp +++ b/libyul/backends/evm/ssa/SSACFGJsonExporter.cpp @@ -132,7 +132,7 @@ Json SSACFGJsonExporter::exportBlock(SSACFG const& _cfg, SSACFG::BlockId _entryI Json SSACFGJsonExporter::toJson(SSACFG const& _cfg, SSACFG::BlockId _blockId, LivenessAnalysis const* _liveness) { - auto const valueToString = [&](SSACFG::ValueId const& valueId) { return varToString(_cfg, valueId); }; + auto const valueToString = [&](LivenessAnalysis::LivenessData::LiveCounts::value_type const& _live) { return varToString(_cfg, _live.first); }; Json blockJson = Json::object(); auto const& block = _cfg.block(_blockId); diff --git a/test/cmdlineTests/standard_yul_cfg_json_export/output.json b/test/cmdlineTests/standard_yul_cfg_json_export/output.json index ac416d3317..07b1c3f639 100644 --- a/test/cmdlineTests/standard_yul_cfg_json_export/output.json +++ b/test/cmdlineTests/standard_yul_cfg_json_export/output.json @@ -901,8 +901,8 @@ ], "out": [ "v0", - "v18", - "v24" + "v24", + "v18" ] }, "type": "BuiltinCall" @@ -992,8 +992,8 @@ "liveness": { "in": [ "v0", - "v18", - "v24" + "v24", + "v18" ], "out": [ "v41" @@ -1238,8 +1238,8 @@ "v59" ], "out": [ - "v46", - "v64" + "v64", + "v46" ] } }, @@ -1393,8 +1393,8 @@ "v46" ], "out": [ - "v46", - "v67" + "v67", + "v46" ] }, "type": "BuiltinCall" @@ -1529,8 +1529,8 @@ "v46" ], "out": [ - "v46", - "v70" + "v70", + "v46" ] }, "type": "BuiltinCall" diff --git a/test/cmdlineTests/yul_cfg_json_export/output b/test/cmdlineTests/yul_cfg_json_export/output index 1e22b84628..cf93e76fd5 100644 --- a/test/cmdlineTests/yul_cfg_json_export/output +++ b/test/cmdlineTests/yul_cfg_json_export/output @@ -901,8 +901,8 @@ Yul Control Flow Graph: ], "out": [ "v0", - "v18", - "v24" + "v24", + "v18" ] }, "type": "BuiltinCall" @@ -992,8 +992,8 @@ Yul Control Flow Graph: "liveness": { "in": [ "v0", - "v18", - "v24" + "v24", + "v18" ], "out": [ "v41" @@ -1238,8 +1238,8 @@ Yul Control Flow Graph: "v59" ], "out": [ - "v46", - "v64" + "v64", + "v46" ] } }, @@ -1393,8 +1393,8 @@ Yul Control Flow Graph: "v46" ], "out": [ - "v46", - "v67" + "v67", + "v46" ] }, "type": "BuiltinCall" @@ -1529,8 +1529,8 @@ Yul Control Flow Graph: "v46" ], "out": [ - "v46", - "v70" + "v70", + "v46" ] }, "type": "BuiltinCall" diff --git a/test/libyul/yulSSAControlFlowGraph/complex.yul b/test/libyul/yulSSAControlFlowGraph/complex.yul index ecb1b7cfea..e4f29d07e4 100644 --- a/test/libyul/yulSSAControlFlowGraph/complex.yul +++ b/test/libyul/yulSSAControlFlowGraph/complex.yul @@ -62,15 +62,15 @@ // c := f(v0, v1)"]; // FunctionEntry_f_0 -> Block1_0; // Block1_0 [label="\ -// Block 0; (0, max 17)\nLiveIn: v0,v1\l\ -// LiveOut: v0,v1,v4\l\nv4 := 42\l\ +// Block 0; (0, max 17)\nLiveIn: v1[1], v0[1]\l\ +// LiveOut: v4[1], v1[1], v0[1]\l\nv4 := 42\l\ // "]; // Block1_0 -> Block1_0Exit [arrowhead=none]; // Block1_0Exit [label="Jump" shape=oval]; // Block1_0Exit -> Block1_1 [style="solid"]; // Block1_1 [label="\ -// Block 1; (1, max 17)\nLiveIn: v0,v1,v6\l\ -// LiveOut: v0,v1,v6\l\nv6 := φ(\l\ +// Block 1; (1, max 17)\nLiveIn: v6[4], v1[1], v0[1]\l\ +// LiveOut: v6[2], v1[1], v0[1]\l\nv6 := φ(\l\ // Block 0 => v4,\l\ // Block 21 => v44\l\ // )\l\ @@ -81,8 +81,8 @@ // Block1_1Exit:0 -> Block1_4 [style="solid"]; // Block1_1Exit:1 -> Block1_2 [style="solid"]; // Block1_2 [label="\ -// Block 2; (2, max 17)\nLiveIn: v0,v1,v6\l\ -// LiveOut: v0,v1,v6,v8\l\nv8 := mload(v6)\l\ +// Block 2; (2, max 17)\nLiveIn: v6[2], v1[1], v0[1]\l\ +// LiveOut: v6[1], v1[1], v8[3], v0[1]\l\nv8 := mload(v6)\l\ // v9 := eq(0, v8)\l\ // "]; // Block1_2 -> Block1_2Exit; @@ -103,8 +103,8 @@ // Block1_6Exit [label="Jump" shape=oval]; // Block1_6Exit -> Block1_4 [style="solid"]; // Block1_7 [label="\ -// Block 7; (5, max 17)\nLiveIn: v0,v1,v6,v8\l\ -// LiveOut: v0,v1,v6,v8\l\nv14 := eq(1, v8)\l\ +// Block 7; (5, max 17)\nLiveIn: v6[1], v1[1], v8[3], v0[1]\l\ +// LiveOut: v6[1], v1[1], v8[2], v0[1]\l\nv14 := eq(1, v8)\l\ // "]; // Block1_7 -> Block1_7Exit; // Block1_7Exit [label="{ If v14 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; @@ -117,8 +117,8 @@ // Block1_9Exit [label="FunctionReturn[0]"]; // Block1_9 -> Block1_9Exit; // Block1_10 [label="\ -// Block 10; (7, max 17)\nLiveIn: v0,v1,v6,v8\l\ -// LiveOut: v0,v1,v6,v8\l\nv21 := eq(2, v8)\l\ +// Block 10; (7, max 17)\nLiveIn: v6[1], v1[1], v8[2], v0[1]\l\ +// LiveOut: v6[1], v1[1], v8[1], v0[1]\l\nv21 := eq(2, v8)\l\ // "]; // Block1_10 -> Block1_10Exit; // Block1_10Exit [label="{ If v21 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; @@ -132,31 +132,31 @@ // Block1_12Exit [label="Terminated"]; // Block1_12 -> Block1_12Exit; // Block1_13 [label="\ -// Block 13; (9, max 17)\nLiveIn: v0,v1,v6,v8\l\ -// LiveOut: v0,v1,v6\l\nv26 := eq(3, v8)\l\ +// Block 13; (9, max 17)\nLiveIn: v6[1], v1[1], v8[1], v0[1]\l\ +// LiveOut: v6[1], v1[1], v0[1]\l\nv26 := eq(3, v8)\l\ // "]; // Block1_13 -> Block1_13Exit; // Block1_13Exit [label="{ If v26 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_13Exit:0 -> Block1_16 [style="solid"]; // Block1_13Exit:1 -> Block1_15 [style="solid"]; // Block1_15 [label="\ -// Block 15; (10, max 14)\nLiveIn: v0,v1,v6\l\ -// LiveOut: v0,v1,v6\l\nsstore(2056, 8)\l\ +// Block 15; (10, max 14)\nLiveIn: v6[1], v1[1], v0[1]\l\ +// LiveOut: v6[1], v1[1], v0[1]\l\nsstore(2056, 8)\l\ // "]; // Block1_15 -> Block1_15Exit [arrowhead=none]; // Block1_15Exit [label="Jump" shape=oval]; // Block1_15Exit -> Block1_5 [style="solid"]; // Block1_16 [label="\ -// Block 16; (15, max 17)\nLiveIn: v0,v1,v6\l\ -// LiveOut: v0,v1,v6\l\nv30 := mload(v1)\l\ +// Block 16; (15, max 17)\nLiveIn: v6[1], v1[1], v0[1]\l\ +// LiveOut: v6[1], v1[1], v0[1]\l\nv30 := mload(v1)\l\ // "]; // Block1_16 -> Block1_16Exit; // Block1_16Exit [label="{ If v30 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_16Exit:0 -> Block1_18 [style="solid"]; // Block1_16Exit:1 -> Block1_17 [style="solid"]; // Block1_5 [label="\ -// Block 5; (11, max 14)\nLiveIn: v0,v1,v6\l\ -// LiveOut: v0,v1,v6\l\nsstore(2827, 11)\l\ +// Block 5; (11, max 14)\nLiveIn: v6[1], v1[1], v0[1]\l\ +// LiveOut: v6[1], v1[1], v0[1]\l\nsstore(2827, 11)\l\ // "]; // Block1_5 -> Block1_5Exit [arrowhead=none]; // Block1_5Exit [label="Jump" shape=oval]; @@ -168,15 +168,15 @@ // Block1_17Exit [label="Terminated"]; // Block1_17 -> Block1_17Exit; // Block1_18 [label="\ -// Block 18; (17, max 17)\nLiveIn: v0,v1,v6\l\ -// LiveOut: v0,v1,v6\l\nsstore(2570, 10)\l\ +// Block 18; (17, max 17)\nLiveIn: v6[1], v1[1], v0[1]\l\ +// LiveOut: v6[1], v1[1], v0[1]\l\nsstore(2570, 10)\l\ // "]; // Block1_18 -> Block1_18Exit [arrowhead=none]; // Block1_18Exit [label="Jump" shape=oval]; // Block1_18Exit -> Block1_5 [style="solid"]; // Block1_3 [label="\ -// Block 3; (12, max 14)\nLiveIn: v0,v1,v6\l\ -// LiveOut: v0,v1,v44\l\nv44 := add(1, v6)\l\ +// Block 3; (12, max 14)\nLiveIn: v6[1], v1[1], v0[1]\l\ +// LiveOut: v44[1], v1[1], v0[1]\l\nv44 := add(1, v6)\l\ // v45 := calldataload(v44)\l\ // "]; // Block1_3 -> Block1_3Exit; @@ -184,14 +184,14 @@ // Block1_3Exit:0 -> Block1_21 [style="solid"]; // Block1_3Exit:1 -> Block1_20 [style="solid"]; // Block1_20 [label="\ -// Block 20; (13, max 13)\nLiveIn: v44\l\ +// Block 20; (13, max 13)\nLiveIn: v44[1]\l\ // LiveOut: \l\nsstore(v44, 0)\l\ // "]; // Block1_20Exit [label="FunctionReturn[0]"]; // Block1_20 -> Block1_20Exit; // Block1_21 [label="\ -// Block 21; (14, max 14)\nLiveIn: v0,v1,v44\l\ -// LiveOut: v0,v1,v44\l\nsstore(65535, 255)\l\ +// Block 21; (14, max 14)\nLiveIn: v44[1], v1[1], v0[1]\l\ +// LiveOut: v44[1], v1[1], v0[1]\l\nsstore(65535, 255)\l\ // "]; // Block1_21 -> Block1_21Exit [arrowhead=none]; // Block1_21Exit [label="Jump" shape=oval]; diff --git a/test/libyul/yulSSAControlFlowGraph/complex2.yul b/test/libyul/yulSSAControlFlowGraph/complex2.yul index d4e8b432d6..6e94670baf 100644 --- a/test/libyul/yulSSAControlFlowGraph/complex2.yul +++ b/test/libyul/yulSSAControlFlowGraph/complex2.yul @@ -78,15 +78,15 @@ // c := f(v0, v1)"]; // FunctionEntry_f_0 -> Block1_0; // Block1_0 [label="\ -// Block 0; (0, max 17)\nLiveIn: v0,v1\l\ -// LiveOut: v0,v1,v4\l\nv4 := 42\l\ +// Block 0; (0, max 17)\nLiveIn: v1[1], v0[1]\l\ +// LiveOut: v4[1], v1[1], v0[1]\l\nv4 := 42\l\ // "]; // Block1_0 -> Block1_0Exit [arrowhead=none]; // Block1_0Exit [label="Jump" shape=oval]; // Block1_0Exit -> Block1_1 [style="solid"]; // Block1_1 [label="\ -// Block 1; (1, max 17)\nLiveIn: v0,v1,v6\l\ -// LiveOut: v0,v1,v6\l\nv6 := φ(\l\ +// Block 1; (1, max 17)\nLiveIn: v6[4], v1[1], v0[1]\l\ +// LiveOut: v6[2], v1[1], v0[1]\l\nv6 := φ(\l\ // Block 0 => v4,\l\ // Block 21 => v44\l\ // )\l\ @@ -97,8 +97,8 @@ // Block1_1Exit:0 -> Block1_4 [style="solid"]; // Block1_1Exit:1 -> Block1_2 [style="solid"]; // Block1_2 [label="\ -// Block 2; (2, max 17)\nLiveIn: v0,v1,v6\l\ -// LiveOut: v0,v1,v6,v8\l\nv8 := mload(v6)\l\ +// Block 2; (2, max 17)\nLiveIn: v6[2], v1[1], v0[1]\l\ +// LiveOut: v6[1], v1[1], v8[3], v0[1]\l\nv8 := mload(v6)\l\ // v9 := eq(0, v8)\l\ // "]; // Block1_2 -> Block1_2Exit; @@ -107,7 +107,7 @@ // Block1_2Exit:1 -> Block1_6 [style="solid"]; // Block1_4 [label="\ // Block 4; (4, max 4)\nLiveIn: \l\ -// LiveOut: v78\l\nsstore(3084, 12)\l\ +// LiveOut: v78[1]\l\nsstore(3084, 12)\l\ // v78 := 27\l\ // "]; // Block1_4Exit [label="FunctionReturn[v78]"]; @@ -120,8 +120,8 @@ // Block1_6Exit [label="Jump" shape=oval]; // Block1_6Exit -> Block1_4 [style="solid"]; // Block1_7 [label="\ -// Block 7; (5, max 17)\nLiveIn: v0,v1,v6,v8\l\ -// LiveOut: v0,v1,v6,v8\l\nv14 := eq(1, v8)\l\ +// Block 7; (5, max 17)\nLiveIn: v6[1], v1[1], v8[3], v0[1]\l\ +// LiveOut: v6[1], v1[1], v8[2], v0[1]\l\nv14 := eq(1, v8)\l\ // "]; // Block1_7 -> Block1_7Exit; // Block1_7Exit [label="{ If v14 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; @@ -134,8 +134,8 @@ // Block1_9Exit [label="FunctionReturn[0]"]; // Block1_9 -> Block1_9Exit; // Block1_10 [label="\ -// Block 10; (7, max 17)\nLiveIn: v0,v1,v6,v8\l\ -// LiveOut: v0,v1,v6,v8\l\nv21 := eq(2, v8)\l\ +// Block 10; (7, max 17)\nLiveIn: v6[1], v1[1], v8[2], v0[1]\l\ +// LiveOut: v6[1], v1[1], v8[1], v0[1]\l\nv21 := eq(2, v8)\l\ // "]; // Block1_10 -> Block1_10Exit; // Block1_10Exit [label="{ If v21 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; @@ -149,31 +149,31 @@ // Block1_12Exit [label="Terminated"]; // Block1_12 -> Block1_12Exit; // Block1_13 [label="\ -// Block 13; (9, max 17)\nLiveIn: v0,v1,v6,v8\l\ -// LiveOut: v0,v1,v6\l\nv26 := eq(3, v8)\l\ +// Block 13; (9, max 17)\nLiveIn: v6[1], v1[1], v8[1], v0[1]\l\ +// LiveOut: v6[1], v1[1], v0[1]\l\nv26 := eq(3, v8)\l\ // "]; // Block1_13 -> Block1_13Exit; // Block1_13Exit [label="{ If v26 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_13Exit:0 -> Block1_16 [style="solid"]; // Block1_13Exit:1 -> Block1_15 [style="solid"]; // Block1_15 [label="\ -// Block 15; (10, max 14)\nLiveIn: v0,v1,v6\l\ -// LiveOut: v0,v1,v6\l\nsstore(2056, 8)\l\ +// Block 15; (10, max 14)\nLiveIn: v6[1], v1[1], v0[1]\l\ +// LiveOut: v6[1], v1[1], v0[1]\l\nsstore(2056, 8)\l\ // "]; // Block1_15 -> Block1_15Exit [arrowhead=none]; // Block1_15Exit [label="Jump" shape=oval]; // Block1_15Exit -> Block1_5 [style="solid"]; // Block1_16 [label="\ -// Block 16; (15, max 17)\nLiveIn: v0,v1,v6\l\ -// LiveOut: v0,v1,v6\l\nv30 := mload(v1)\l\ +// Block 16; (15, max 17)\nLiveIn: v6[1], v1[1], v0[1]\l\ +// LiveOut: v6[1], v1[1], v0[1]\l\nv30 := mload(v1)\l\ // "]; // Block1_16 -> Block1_16Exit; // Block1_16Exit [label="{ If v30 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_16Exit:0 -> Block1_18 [style="solid"]; // Block1_16Exit:1 -> Block1_17 [style="solid"]; // Block1_5 [label="\ -// Block 5; (11, max 14)\nLiveIn: v0,v1,v6\l\ -// LiveOut: v0,v1,v6\l\nsstore(2827, 11)\l\ +// Block 5; (11, max 14)\nLiveIn: v6[1], v1[1], v0[1]\l\ +// LiveOut: v6[1], v1[1], v0[1]\l\nsstore(2827, 11)\l\ // "]; // Block1_5 -> Block1_5Exit [arrowhead=none]; // Block1_5Exit [label="Jump" shape=oval]; @@ -185,15 +185,15 @@ // Block1_17Exit [label="Terminated"]; // Block1_17 -> Block1_17Exit; // Block1_18 [label="\ -// Block 18; (17, max 17)\nLiveIn: v0,v1,v6\l\ -// LiveOut: v0,v1,v6\l\nsstore(2570, 10)\l\ +// Block 18; (17, max 17)\nLiveIn: v6[1], v1[1], v0[1]\l\ +// LiveOut: v6[1], v1[1], v0[1]\l\nsstore(2570, 10)\l\ // "]; // Block1_18 -> Block1_18Exit [arrowhead=none]; // Block1_18Exit [label="Jump" shape=oval]; // Block1_18Exit -> Block1_5 [style="solid"]; // Block1_3 [label="\ -// Block 3; (12, max 14)\nLiveIn: v0,v1,v6\l\ -// LiveOut: v0,v1,v44\l\nv44 := add(1, v6)\l\ +// Block 3; (12, max 14)\nLiveIn: v6[1], v1[1], v0[1]\l\ +// LiveOut: v44[1], v1[1], v0[1]\l\nv44 := add(1, v6)\l\ // v45 := calldataload(v44)\l\ // "]; // Block1_3 -> Block1_3Exit; @@ -201,14 +201,14 @@ // Block1_3Exit:0 -> Block1_21 [style="solid"]; // Block1_3Exit:1 -> Block1_20 [style="solid"]; // Block1_20 [label="\ -// Block 20; (13, max 13)\nLiveIn: v44\l\ +// Block 20; (13, max 13)\nLiveIn: v44[1]\l\ // LiveOut: \l\nsstore(v44, 0)\l\ // "]; // Block1_20Exit [label="FunctionReturn[0]"]; // Block1_20 -> Block1_20Exit; // Block1_21 [label="\ -// Block 21; (14, max 14)\nLiveIn: v0,v1,v44\l\ -// LiveOut: v0,v1,v44\l\nsstore(65535, 255)\l\ +// Block 21; (14, max 14)\nLiveIn: v44[1], v1[1], v0[1]\l\ +// LiveOut: v44[1], v1[1], v0[1]\l\nsstore(65535, 255)\l\ // "]; // Block1_21 -> Block1_21Exit [arrowhead=none]; // Block1_21Exit [label="Jump" shape=oval]; diff --git a/test/libyul/yulSSAControlFlowGraph/default_initialized_variable.yul b/test/libyul/yulSSAControlFlowGraph/default_initialized_variable.yul index 92ea33382b..799bd94ca0 100644 --- a/test/libyul/yulSSAControlFlowGraph/default_initialized_variable.yul +++ b/test/libyul/yulSSAControlFlowGraph/default_initialized_variable.yul @@ -15,7 +15,7 @@ // Entry0 -> Block0_0; // Block0_0 [label="\ // Block 0; (0, max 2)\nLiveIn: \l\ -// LiveOut: v1\l\nv1 := 0\l\ +// LiveOut: v1[1]\l\nv1 := 0\l\ // v3 := mload(42)\l\ // "]; // Block0_0 -> Block0_0Exit; @@ -24,13 +24,13 @@ // Block0_0Exit:1 -> Block0_1 [style="solid"]; // Block0_1 [label="\ // Block 1; (1, max 2)\nLiveIn: \l\ -// LiveOut: v5\l\nv5 := 5\l\ +// LiveOut: v5[1]\l\nv5 := 5\l\ // "]; // Block0_1 -> Block0_1Exit [arrowhead=none]; // Block0_1Exit [label="Jump" shape=oval]; // Block0_1Exit -> Block0_2 [style="solid"]; // Block0_2 [label="\ -// Block 2; (2, max 2)\nLiveIn: v6\l\ +// Block 2; (2, max 2)\nLiveIn: v6[3]\l\ // LiveOut: \l\nv6 := φ(\l\ // Block 0 => v1,\l\ // Block 1 => v5\l\ diff --git a/test/libyul/yulSSAControlFlowGraph/function.yul b/test/libyul/yulSSAControlFlowGraph/function.yul index e2d8c1d523..e125836762 100644 --- a/test/libyul/yulSSAControlFlowGraph/function.yul +++ b/test/libyul/yulSSAControlFlowGraph/function.yul @@ -37,8 +37,8 @@ // r := f(v0, v1)"]; // FunctionEntry_f_0 -> Block1_0; // Block1_0 [label="\ -// Block 0; (0, max 0)\nLiveIn: v0,v1\l\ -// LiveOut: v4\l\nv3 := add(v1, v0)\l\ +// Block 0; (0, max 0)\nLiveIn: v0[2], v1[1]\l\ +// LiveOut: v4[1]\l\nv3 := add(v1, v0)\l\ // v4 := sub(v0, v3)\l\ // "]; // Block1_0Exit [label="FunctionReturn[v4]"]; @@ -56,7 +56,7 @@ // h(v0)"]; // FunctionEntry_h_0 -> Block3_0; // Block3_0 [label="\ -// Block 0; (0, max 0)\nLiveIn: v0\l\ +// Block 0; (0, max 0)\nLiveIn: v0[1]\l\ // LiveOut: \l\nv2 := f(0, v0)\l\ // h(v2)\l\ // "]; @@ -67,7 +67,7 @@ // FunctionEntry_i_0 -> Block4_0; // Block4_0 [label="\ // Block 0; (0, max 0)\nLiveIn: \l\ -// LiveOut: v2,v4\l\nv2 := 514\l\ +// LiveOut: v2[1], v4[1]\l\nv2 := 514\l\ // v4 := 771\l\ // "]; // Block4_0Exit [label="FunctionReturn[v2, v4]"]; diff --git a/test/libyul/yulSSAControlFlowGraph/if.yul b/test/libyul/yulSSAControlFlowGraph/if.yul index f8639b2cc3..16fc8957d3 100644 --- a/test/libyul/yulSSAControlFlowGraph/if.yul +++ b/test/libyul/yulSSAControlFlowGraph/if.yul @@ -16,7 +16,7 @@ // Entry0 -> Block0_0; // Block0_0 [label="\ // Block 0; (0, max 2)\nLiveIn: \l\ -// LiveOut: v1\l\nv1 := calldataload(3)\l\ +// LiveOut: v1[1]\l\nv1 := calldataload(3)\l\ // v3 := mload(42)\l\ // "]; // Block0_0 -> Block0_0Exit; @@ -25,13 +25,13 @@ // Block0_0Exit:1 -> Block0_1 [style="solid"]; // Block0_1 [label="\ // Block 1; (1, max 2)\nLiveIn: \l\ -// LiveOut: v5\l\nv5 := calldataload(77)\l\ +// LiveOut: v5[1]\l\nv5 := calldataload(77)\l\ // "]; // Block0_1 -> Block0_1Exit [arrowhead=none]; // Block0_1Exit [label="Jump" shape=oval]; // Block0_1Exit -> Block0_2 [style="solid"]; // Block0_2 [label="\ -// Block 2; (2, max 2)\nLiveIn: v6\l\ +// Block 2; (2, max 2)\nLiveIn: v6[2]\l\ // LiveOut: \l\nv6 := φ(\l\ // Block 0 => v1,\l\ // Block 1 => v5\l\ diff --git a/test/libyul/yulSSAControlFlowGraph/nested_for.yul b/test/libyul/yulSSAControlFlowGraph/nested_for.yul index 6efb3552a1..8d5060951d 100644 --- a/test/libyul/yulSSAControlFlowGraph/nested_for.yul +++ b/test/libyul/yulSSAControlFlowGraph/nested_for.yul @@ -27,14 +27,14 @@ // Entry0 -> Block0_0; // Block0_0 [label="\ // Block 0; (0, max 24)\nLiveIn: \l\ -// LiveOut: v1\l\nv1 := 0\l\ +// LiveOut: v1[1]\l\nv1 := 0\l\ // "]; // Block0_0 -> Block0_0Exit [arrowhead=none]; // Block0_0Exit [label="Jump" shape=oval]; // Block0_0Exit -> Block0_1 [style="solid"]; // Block0_1 [label="\ -// Block 1; (1, max 24)\nLiveIn: v3\l\ -// LiveOut: v3\l\nv3 := φ(\l\ +// Block 1; (1, max 24)\nLiveIn: v3[3]\l\ +// LiveOut: v3[1]\l\nv3 := φ(\l\ // Block 0 => v1,\l\ // Block 3 => v43\l\ // )\l\ @@ -45,8 +45,8 @@ // Block0_1Exit:0 -> Block0_4 [style="solid"]; // Block0_1Exit:1 -> Block0_2 [style="solid"]; // Block0_2 [label="\ -// Block 2; (2, max 23)\nLiveIn: v3\l\ -// LiveOut: v3,v5\l\nv5 := 0\l\ +// Block 2; (2, max 23)\nLiveIn: v3[1]\l\ +// LiveOut: v5[1], v3[1]\l\nv5 := 0\l\ // "]; // Block0_2 -> Block0_2Exit [arrowhead=none]; // Block0_2Exit [label="Jump" shape=oval]; @@ -57,8 +57,8 @@ // Block0_4Exit [label="MainExit"]; // Block0_4 -> Block0_4Exit; // Block0_5 [label="\ -// Block 5; (3, max 23)\nLiveIn: v3,v6\l\ -// LiveOut: v3,v6\l\nv6 := φ(\l\ +// Block 5; (3, max 23)\nLiveIn: v6[3], v3[1]\l\ +// LiveOut: v6[1], v3[1]\l\nv6 := φ(\l\ // Block 2 => v5,\l\ // Block 7 => v42\l\ // )\l\ @@ -69,21 +69,21 @@ // Block0_5Exit:0 -> Block0_8 [style="solid"]; // Block0_5Exit:1 -> Block0_6 [style="solid"]; // Block0_6 [label="\ -// Block 6; (4, max 21)\nLiveIn: v3,v6\l\ -// LiveOut: v3,v6,v8\l\nv8 := 0\l\ +// Block 6; (4, max 21)\nLiveIn: v6[1], v3[1]\l\ +// LiveOut: v8[1], v6[1], v3[1]\l\nv8 := 0\l\ // "]; // Block0_6 -> Block0_6Exit [arrowhead=none]; // Block0_6Exit [label="Jump" shape=oval]; // Block0_6Exit -> Block0_9 [style="solid"]; // Block0_8 [label="\ -// Block 8; (22, max 23)\nLiveIn: v3\l\ -// LiveOut: v3\l\n"]; +// Block 8; (22, max 23)\nLiveIn: v3[1]\l\ +// LiveOut: v3[1]\l\n"]; // Block0_8 -> Block0_8Exit [arrowhead=none]; // Block0_8Exit [label="Jump" shape=oval]; // Block0_8Exit -> Block0_3 [style="solid"]; // Block0_9 [label="\ -// Block 9; (5, max 21)\nLiveIn: v3,v6,v9\l\ -// LiveOut: v3,v6,v9\l\nv9 := φ(\l\ +// Block 9; (5, max 21)\nLiveIn: v9[3], v6[1], v3[1]\l\ +// LiveOut: v9[1], v6[1], v3[1]\l\nv9 := φ(\l\ // Block 6 => v8,\l\ // Block 11 => v38\l\ // )\l\ @@ -94,51 +94,51 @@ // Block0_9Exit:0 -> Block0_12 [style="solid"]; // Block0_9Exit:1 -> Block0_10 [style="solid"]; // Block0_3 [label="\ -// Block 3; (23, max 23)\nLiveIn: v3\l\ -// LiveOut: v43\l\nv43 := add(1, v3)\l\ +// Block 3; (23, max 23)\nLiveIn: v3[1]\l\ +// LiveOut: v43[1]\l\nv43 := add(1, v3)\l\ // "]; // Block0_3 -> Block0_3Exit [arrowhead=none]; // Block0_3Exit [label="Jump" shape=oval]; // Block0_3Exit -> Block0_1 [style="dashed"]; // Block0_10 [label="\ -// Block 10; (6, max 19)\nLiveIn: v3,v6,v9\l\ -// LiveOut: v3,v6,v9\l\nv11 := mload(0)\l\ +// Block 10; (6, max 19)\nLiveIn: v9[1], v6[1], v3[1]\l\ +// LiveOut: v9[1], v6[1], v3[1]\l\nv11 := mload(0)\l\ // "]; // Block0_10 -> Block0_10Exit; // Block0_10Exit [label="{ If v11 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block0_10Exit:0 -> Block0_14 [style="solid"]; // Block0_10Exit:1 -> Block0_13 [style="solid"]; // Block0_12 [label="\ -// Block 12; (20, max 21)\nLiveIn: v3,v6\l\ -// LiveOut: v3,v6\l\n"]; +// Block 12; (20, max 21)\nLiveIn: v6[1], v3[1]\l\ +// LiveOut: v6[1], v3[1]\l\n"]; // Block0_12 -> Block0_12Exit [arrowhead=none]; // Block0_12Exit [label="Jump" shape=oval]; // Block0_12Exit -> Block0_7 [style="solid"]; // Block0_13 [label="\ -// Block 13; (7, max 19)\nLiveIn: v3,v6,v9\l\ -// LiveOut: v3,v6,v9,v12\l\nv12 := 0\l\ +// Block 13; (7, max 19)\nLiveIn: v9[1], v6[1], v3[1]\l\ +// LiveOut: v12[1], v9[1], v6[1], v3[1]\l\nv12 := 0\l\ // "]; // Block0_13 -> Block0_13Exit [arrowhead=none]; // Block0_13Exit [label="Jump" shape=oval]; // Block0_13Exit -> Block0_15 [style="solid"]; // Block0_14 [label="\ -// Block 14; (12, max 19)\nLiveIn: v3,v6,v9\l\ -// LiveOut: v3,v6,v9\l\nv24 := mload(1)\l\ +// Block 14; (12, max 19)\nLiveIn: v9[1], v6[1], v3[1]\l\ +// LiveOut: v9[1], v6[1], v3[1]\l\nv24 := mload(1)\l\ // "]; // Block0_14 -> Block0_14Exit; // Block0_14Exit [label="{ If v24 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block0_14Exit:0 -> Block0_20 [style="solid"]; // Block0_14Exit:1 -> Block0_19 [style="solid"]; // Block0_7 [label="\ -// Block 7; (21, max 21)\nLiveIn: v3,v6\l\ -// LiveOut: v3,v42\l\nv42 := add(1, v6)\l\ +// Block 7; (21, max 21)\nLiveIn: v6[1], v3[1]\l\ +// LiveOut: v42[1], v3[1]\l\nv42 := add(1, v6)\l\ // "]; // Block0_7 -> Block0_7Exit [arrowhead=none]; // Block0_7Exit [label="Jump" shape=oval]; // Block0_7Exit -> Block0_5 [style="dashed"]; // Block0_15 [label="\ -// Block 15; (8, max 19)\nLiveIn: v3,v6,v9,v13\l\ -// LiveOut: v3,v6,v9,v13\l\nv13 := φ(\l\ +// Block 15; (8, max 19)\nLiveIn: v13[4], v9[1], v6[1], v3[1]\l\ +// LiveOut: v13[2], v9[1], v6[1], v3[1]\l\nv13 := φ(\l\ // Block 13 => v12,\l\ // Block 17 => v21\l\ // )\l\ @@ -149,21 +149,21 @@ // Block0_15Exit:0 -> Block0_18 [style="solid"]; // Block0_15Exit:1 -> Block0_16 [style="solid"]; // Block0_19 [label="\ -// Block 19; (13, max 19)\nLiveIn: v3,v6,v9\l\ -// LiveOut: v3,v6,v9,v25\l\nv25 := 0\l\ +// Block 19; (13, max 19)\nLiveIn: v9[1], v6[1], v3[1]\l\ +// LiveOut: v25[1], v9[1], v6[1], v3[1]\l\nv25 := 0\l\ // "]; // Block0_19 -> Block0_19Exit [arrowhead=none]; // Block0_19Exit [label="Jump" shape=oval]; // Block0_19Exit -> Block0_21 [style="solid"]; // Block0_20 [label="\ -// Block 20; (18, max 19)\nLiveIn: v3,v6,v9\l\ -// LiveOut: v3,v6,v9\l\n"]; +// Block 20; (18, max 19)\nLiveIn: v9[1], v6[1], v3[1]\l\ +// LiveOut: v9[1], v6[1], v3[1]\l\n"]; // Block0_20 -> Block0_20Exit [arrowhead=none]; // Block0_20Exit [label="Jump" shape=oval]; // Block0_20Exit -> Block0_11 [style="solid"]; // Block0_16 [label="\ -// Block 16; (9, max 10)\nLiveIn: v3,v6,v9,v13\l\ -// LiveOut: v3,v6,v9,v13\l\nv18 := add(v6, v3)\l\ +// Block 16; (9, max 10)\nLiveIn: v13[2], v9[1], v6[1], v3[1]\l\ +// LiveOut: v13[1], v9[1], v6[1], v3[1]\l\nv18 := add(v6, v3)\l\ // v19 := add(v9, v18)\l\ // sstore(v19, v13)\l\ // "]; @@ -171,14 +171,14 @@ // Block0_16Exit [label="Jump" shape=oval]; // Block0_16Exit -> Block0_17 [style="solid"]; // Block0_18 [label="\ -// Block 18; (11, max 19)\nLiveIn: v3,v6,v9\l\ -// LiveOut: v3,v6,v9\l\n"]; +// Block 18; (11, max 19)\nLiveIn: v9[1], v6[1], v3[1]\l\ +// LiveOut: v9[1], v6[1], v3[1]\l\n"]; // Block0_18 -> Block0_18Exit [arrowhead=none]; // Block0_18Exit [label="Jump" shape=oval]; // Block0_18Exit -> Block0_14 [style="solid"]; // Block0_21 [label="\ -// Block 21; (14, max 19)\nLiveIn: v3,v6,v9,v26\l\ -// LiveOut: v3,v6,v9,v26\l\nv26 := φ(\l\ +// Block 21; (14, max 19)\nLiveIn: v26[4], v9[1], v6[1], v3[1]\l\ +// LiveOut: v26[2], v9[1], v6[1], v3[1]\l\nv26 := φ(\l\ // Block 19 => v25,\l\ // Block 23 => v33\l\ // )\l\ @@ -189,22 +189,22 @@ // Block0_21Exit:0 -> Block0_24 [style="solid"]; // Block0_21Exit:1 -> Block0_22 [style="solid"]; // Block0_11 [label="\ -// Block 11; (19, max 19)\nLiveIn: v3,v6,v9\l\ -// LiveOut: v3,v6,v38\l\nv38 := add(1, v9)\l\ +// Block 11; (19, max 19)\nLiveIn: v9[1], v6[1], v3[1]\l\ +// LiveOut: v38[1], v6[1], v3[1]\l\nv38 := add(1, v9)\l\ // "]; // Block0_11 -> Block0_11Exit [arrowhead=none]; // Block0_11Exit [label="Jump" shape=oval]; // Block0_11Exit -> Block0_9 [style="dashed"]; // Block0_17 [label="\ -// Block 17; (10, max 10)\nLiveIn: v3,v6,v9,v13\l\ -// LiveOut: v3,v6,v9,v21\l\nv21 := add(1, v13)\l\ +// Block 17; (10, max 10)\nLiveIn: v13[1], v9[1], v6[1], v3[1]\l\ +// LiveOut: v21[1], v9[1], v6[1], v3[1]\l\nv21 := add(1, v13)\l\ // "]; // Block0_17 -> Block0_17Exit [arrowhead=none]; // Block0_17Exit [label="Jump" shape=oval]; // Block0_17Exit -> Block0_15 [style="dashed"]; // Block0_22 [label="\ -// Block 22; (15, max 16)\nLiveIn: v3,v6,v9,v26\l\ -// LiveOut: v3,v6,v9,v26\l\nv31 := add(v6, v3)\l\ +// Block 22; (15, max 16)\nLiveIn: v26[2], v9[1], v6[1], v3[1]\l\ +// LiveOut: v26[1], v9[1], v6[1], v3[1]\l\nv31 := add(v6, v3)\l\ // v32 := add(v9, v31)\l\ // sstore(v32, v26)\l\ // "]; @@ -212,14 +212,14 @@ // Block0_22Exit [label="Jump" shape=oval]; // Block0_22Exit -> Block0_23 [style="solid"]; // Block0_24 [label="\ -// Block 24; (17, max 19)\nLiveIn: v3,v6,v9\l\ -// LiveOut: v3,v6,v9\l\n"]; +// Block 24; (17, max 19)\nLiveIn: v9[1], v6[1], v3[1]\l\ +// LiveOut: v9[1], v6[1], v3[1]\l\n"]; // Block0_24 -> Block0_24Exit [arrowhead=none]; // Block0_24Exit [label="Jump" shape=oval]; // Block0_24Exit -> Block0_20 [style="solid"]; // Block0_23 [label="\ -// Block 23; (16, max 16)\nLiveIn: v3,v6,v9,v26\l\ -// LiveOut: v3,v6,v9,v33\l\nv33 := add(1, v26)\l\ +// Block 23; (16, max 16)\nLiveIn: v26[1], v9[1], v6[1], v3[1]\l\ +// LiveOut: v33[1], v9[1], v6[1], v3[1]\l\nv33 := add(1, v26)\l\ // "]; // Block0_23 -> Block0_23Exit [arrowhead=none]; // Block0_23Exit [label="Jump" shape=oval]; diff --git a/test/libyul/yulSSAControlFlowGraph/nested_function.yul b/test/libyul/yulSSAControlFlowGraph/nested_function.yul index 037e1f006c..4387e54aa9 100644 --- a/test/libyul/yulSSAControlFlowGraph/nested_function.yul +++ b/test/libyul/yulSSAControlFlowGraph/nested_function.yul @@ -46,8 +46,8 @@ // r := f(v0, v1)"]; // FunctionEntry_f_0 -> Block1_0; // Block1_0 [label="\ -// Block 0; (0, max 0)\nLiveIn: v0,v1\l\ -// LiveOut: v4\l\nv3 := add(v1, v0)\l\ +// Block 0; (0, max 0)\nLiveIn: v0[2], v1[1]\l\ +// LiveOut: v4[1]\l\nv3 := add(v1, v0)\l\ // v4 := sub(v0, v3)\l\ // "]; // Block1_0Exit [label="FunctionReturn[v4]"]; @@ -69,7 +69,7 @@ // FunctionEntry_z_0 -> Block3_0; // Block3_0 [label="\ // Block 0; (0, max 0)\nLiveIn: \l\ -// LiveOut: v1\l\nv1 := w()\l\ +// LiveOut: v1[1]\l\nv1 := w()\l\ // "]; // Block3_0Exit [label="FunctionReturn[v1]"]; // Block3_0 -> Block3_0Exit; @@ -78,7 +78,7 @@ // FunctionEntry_w_0 -> Block4_0; // Block4_0 [label="\ // Block 0; (0, max 0)\nLiveIn: \l\ -// LiveOut: v2\l\nv2 := 1\l\ +// LiveOut: v2[1]\l\nv2 := 1\l\ // "]; // Block4_0Exit [label="FunctionReturn[v2]"]; // Block4_0 -> Block4_0Exit; @@ -87,7 +87,7 @@ // FunctionEntry_v_0 -> Block5_0; // Block5_0 [label="\ // Block 0; (0, max 0)\nLiveIn: \l\ -// LiveOut: v1\l\nv1 := w()\l\ +// LiveOut: v1[1]\l\nv1 := w()\l\ // "]; // Block5_0Exit [label="FunctionReturn[v1]"]; // Block5_0 -> Block5_0Exit; @@ -96,7 +96,7 @@ // FunctionEntry_w_0 -> Block6_0; // Block6_0 [label="\ // Block 0; (0, max 0)\nLiveIn: \l\ -// LiveOut: v2\l\nv2 := 17\l\ +// LiveOut: v2[1]\l\nv2 := 17\l\ // "]; // Block6_0Exit [label="FunctionReturn[v2]"]; // Block6_0 -> Block6_0Exit; @@ -113,14 +113,14 @@ // Block7_0Exit:1 -> Block7_1 [style="solid"]; // Block7_1 [label="\ // Block 1; (1, max 2)\nLiveIn: \l\ -// LiveOut: v3\l\nv3 := cycle2()\l\ +// LiveOut: v3[1]\l\nv3 := cycle2()\l\ // "]; // Block7_1 -> Block7_1Exit [arrowhead=none]; // Block7_1Exit [label="Jump" shape=oval]; // Block7_1Exit -> Block7_2 [style="solid"]; // Block7_2 [label="\ -// Block 2; (2, max 2)\nLiveIn: v4\l\ -// LiveOut: v4\l\nv4 := φ(\l\ +// Block 2; (2, max 2)\nLiveIn: v4[3]\l\ +// LiveOut: v4[1]\l\nv4 := φ(\l\ // Block 0 => 0,\l\ // Block 1 => v3\l\ // )\l\ @@ -140,14 +140,14 @@ // Block8_0Exit:1 -> Block8_1 [style="solid"]; // Block8_1 [label="\ // Block 1; (1, max 2)\nLiveIn: \l\ -// LiveOut: v3\l\nv3 := cycle1()\l\ +// LiveOut: v3[1]\l\nv3 := cycle1()\l\ // "]; // Block8_1 -> Block8_1Exit [arrowhead=none]; // Block8_1Exit [label="Jump" shape=oval]; // Block8_1Exit -> Block8_2 [style="solid"]; // Block8_2 [label="\ -// Block 2; (2, max 2)\nLiveIn: v4\l\ -// LiveOut: v4\l\nv4 := φ(\l\ +// Block 2; (2, max 2)\nLiveIn: v4[3]\l\ +// LiveOut: v4[1]\l\nv4 := φ(\l\ // Block 0 => 0,\l\ // Block 1 => v3\l\ // )\l\ diff --git a/test/libyul/yulSSAControlFlowGraph/switch.yul b/test/libyul/yulSSAControlFlowGraph/switch.yul index 10573b0122..5a670467e0 100644 --- a/test/libyul/yulSSAControlFlowGraph/switch.yul +++ b/test/libyul/yulSSAControlFlowGraph/switch.yul @@ -23,7 +23,7 @@ // Entry0 -> Block0_0; // Block0_0 [label="\ // Block 0; (0, max 5)\nLiveIn: \l\ -// LiveOut: v3\l\nv1 := calldataload(3)\l\ +// LiveOut: v3[1]\l\nv1 := calldataload(3)\l\ // v3 := sload(0)\l\ // v4 := eq(0, v3)\l\ // "]; @@ -33,13 +33,13 @@ // Block0_0Exit:1 -> Block0_2 [style="solid"]; // Block0_2 [label="\ // Block 2; (1, max 2)\nLiveIn: \l\ -// LiveOut: v6\l\nv6 := calldataload(77)\l\ +// LiveOut: v6[1]\l\nv6 := calldataload(77)\l\ // "]; // Block0_2 -> Block0_2Exit [arrowhead=none]; // Block0_2Exit [label="Jump" shape=oval]; // Block0_2Exit -> Block0_1 [style="solid"]; // Block0_3 [label="\ -// Block 3; (3, max 5)\nLiveIn: v3\l\ +// Block 3; (3, max 5)\nLiveIn: v3[1]\l\ // LiveOut: \l\nv7 := eq(1, v3)\l\ // "]; // Block0_3 -> Block0_3Exit; @@ -47,7 +47,7 @@ // Block0_3Exit:0 -> Block0_5 [style="solid"]; // Block0_3Exit:1 -> Block0_4 [style="solid"]; // Block0_1 [label="\ -// Block 1; (2, max 2)\nLiveIn: v13\l\ +// Block 1; (2, max 2)\nLiveIn: v13[2]\l\ // LiveOut: \l\nv13 := φ(\l\ // Block 2 => v6,\l\ // Block 4 => v10,\l\ @@ -59,14 +59,14 @@ // Block0_1 -> Block0_1Exit; // Block0_4 [label="\ // Block 4; (4, max 4)\nLiveIn: \l\ -// LiveOut: v10\l\nv10 := calldataload(88)\l\ +// LiveOut: v10[1]\l\nv10 := calldataload(88)\l\ // "]; // Block0_4 -> Block0_4Exit [arrowhead=none]; // Block0_4Exit [label="Jump" shape=oval]; // Block0_4Exit -> Block0_1 [style="solid"]; // Block0_5 [label="\ // Block 5; (5, max 5)\nLiveIn: \l\ -// LiveOut: v12\l\nv12 := calldataload(99)\l\ +// LiveOut: v12[1]\l\nv12 := calldataload(99)\l\ // "]; // Block0_5 -> Block0_5Exit [arrowhead=none]; // Block0_5Exit [label="Jump" shape=oval]; From 4baeaa02c4a05e1aede29fb00f55a80d8b27cff6 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 6 Oct 2025 15:01:16 +0200 Subject: [PATCH 022/243] docs: ignore sphinx syntax warnings --- docs/conf.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 44da93dba5..3f7dc3e350 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -172,6 +172,10 @@ def get_github_username_repo(url): # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False +suppress_warnings = [ + # Suppress sphinx-syntax warnings, in particular `diagram descriptions can't have custom resolver_data` + 'sphinx_syntax', +] # -- Options for HTML output ---------------------------------------------- From e734de85b7ab1ccf1638d83caf07a1e30b3a5dfc Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Tue, 7 Oct 2025 08:57:53 +0200 Subject: [PATCH 023/243] ci: bump macos.m1.medium.gen1 with xcode 15.0.0 to macos.m4pro.medium with xcode 15.4.0 --- .circleci/config.yml | 8 ++++---- .circleci/osx_install_dependencies.sh | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 076c7b8d19..cf08579425 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -514,12 +514,12 @@ defaults: - base_osx: &base_osx macos: - xcode: "15.0.0" - resource_class: macos.m1.medium.gen1 + xcode: 15.4.0 + resource_class: m4pro.medium environment: &base_osx_env TERM: xterm - MAKEFLAGS: -j5 - CPUs: 5 + MAKEFLAGS: -j8 + CPUs: 8 - base_ubuntu_clang: &base_ubuntu_clang docker: diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh index f47e08210c..b449c4781b 100755 --- a/.circleci/osx_install_dependencies.sh +++ b/.circleci/osx_install_dependencies.sh @@ -48,8 +48,6 @@ function validate_checksum { if [ ! -f /usr/local/lib/libz3.a ] # if this file does not exists (cache was not restored), rebuild dependencies then - brew uninstall temurin17 - brew untap homebrew/homebrew-cask-versions brew update brew upgrade brew install cmake From 11614754832d85cc7f739c895690fd88236fa5c1 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 8 Oct 2025 08:00:43 +0200 Subject: [PATCH 024/243] Bump zeppelin node version to lts --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cf08579425..48d86a97c4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -744,7 +744,7 @@ defaults: name: t_native_test_ext_zeppelin project: zeppelin binary_type: native - image: cimg/node:18.16 + image: cimg/node:lts resource_class: large - job_native_test_ext_ens: &job_native_test_ext_ens From 173a5341ac87709db612315f8809ef5784932954 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 6 Oct 2025 11:25:04 +0200 Subject: [PATCH 025/243] testing: run yul optimizer tests on all code subobjects --- test/libyul/YulOptimizerTestCommon.cpp | 24 +++++++----- .../fullSuite/sub_objects.yul | 39 +++++++++++++++++++ 2 files changed, 53 insertions(+), 10 deletions(-) create mode 100644 test/libyul/yulOptimizerTests/fullSuite/sub_objects.yul diff --git a/test/libyul/YulOptimizerTestCommon.cpp b/test/libyul/YulOptimizerTestCommon.cpp index 19b2176c10..cd54318c19 100644 --- a/test/libyul/YulOptimizerTestCommon.cpp +++ b/test/libyul/YulOptimizerTestCommon.cpp @@ -66,6 +66,8 @@ #include +#include + #include using namespace solidity; @@ -81,15 +83,6 @@ YulOptimizerTestCommon::YulOptimizerTestCommon(std::shared_ptr _ob { soltestAssert(m_object && m_optimizedObject); - if ( - std::any_of( - m_object->subObjects.begin(), - m_object->subObjects.end(), - [](auto const& subObject) { return dynamic_cast(subObject.get()) == nullptr;} - ) - ) - solUnimplemented("The current implementation of YulOptimizerTests ignores subobjects that are not Data."); - m_namedSteps = { {"disambiguator", [&]() { return disambiguate(); }}, {"nameDisplacer", [&]() { @@ -496,8 +489,19 @@ void YulOptimizerTestCommon::setStep(std::string const& _optimizerStep) bool YulOptimizerTestCommon::runStep() { - if (m_namedSteps.count(m_optimizerStep)) + if (m_namedSteps.contains(m_optimizerStep)) { + for (auto const& [ix, subNode]: m_object->subObjects | ranges::views::enumerate) + if (auto const subObject = std::dynamic_pointer_cast(subNode)) + { + YulOptimizerTestCommon subTest(subObject); + subTest.setStep(m_optimizerStep); + subTest.run(); + auto const optimizedSubObject = std::dynamic_pointer_cast(m_optimizedObject->subObjects[ix]); + yulAssert(optimizedSubObject); + optimizedSubObject->setCode(subTest.m_optimizedObject->code()); + } + auto block = m_namedSteps[m_optimizerStep](); m_optimizedObject->setCode(std::make_shared(*m_object->dialect(), std::move(block))); } diff --git a/test/libyul/yulOptimizerTests/fullSuite/sub_objects.yul b/test/libyul/yulOptimizerTests/fullSuite/sub_objects.yul new file mode 100644 index 0000000000..08c64fd4c2 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/sub_objects.yul @@ -0,0 +1,39 @@ +// nested object to show that optimizations are applied at all levels +object "A" { + code + { + mstore(43, 33) // removed by optimizer as it's transient and otherwise unused + } + + object "B" + { + code + { + let y := 42 + sstore(y, y) // persists + mstore(y, y) // removed + } + + object "C" + { + code + { + let x := 55 + sstore(x, x) // persists + mstore(x, x) // removed + } + } + } +} +// ---- +// step: fullSuite +// +// object "A" { +// code { { } } +// object "B" { +// code { { sstore(42, 42) } } +// object "C" { +// code { { sstore(55, 55) } } +// } +// } +// } From 2787cd3765bee066a0278637cd225e179f83060e Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Wed, 8 Oct 2025 10:49:32 +0200 Subject: [PATCH 026/243] ssacfg: add bridge finder --- libyul/CMakeLists.txt | 1 + libyul/backends/evm/ssa/BridgeFinder.h | 129 +++++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 libyul/backends/evm/ssa/BridgeFinder.h diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index e70a6e7b67..78fef861bd 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -73,6 +73,7 @@ add_library(yul backends/evm/StackLayoutGenerator.h backends/evm/VariableReferenceCounter.h backends/evm/VariableReferenceCounter.cpp + backends/evm/ssa/BridgeFinder.h backends/evm/ssa/ControlFlow.cpp backends/evm/ssa/ControlFlow.h backends/evm/ssa/LivenessAnalysis.cpp diff --git a/libyul/backends/evm/ssa/BridgeFinder.h b/libyul/backends/evm/ssa/BridgeFinder.h new file mode 100644 index 0000000000..6a7ebeef9d --- /dev/null +++ b/libyul/backends/evm/ssa/BridgeFinder.h @@ -0,0 +1,129 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + + +#pragma once + +#include + +#include + +#include +#include +#include + +namespace solidity::yul::ssa +{ +/// Detect bridges according to Algorithm 1 of https://arxiv.org/pdf/2108.07346.pdf. +/// +/// A bridge in an undirected graph is an edge whose removal increases the number of connected components. In control +/// flow analysis, bridge vertices are critical articulation points where removing the vertex would disconnect +/// reachable code from unreachable code. This implementation adapts the bridge-finding algorithm to directed control +/// flow graphs by treating them as undirected, then validating edge directionality to identify vertices that gate +/// access to subsequent blocks. +/// +/// We use bridges to determine blocks in which it is fine to introduce junk slots on the stack at any point in time. +class BridgeFinder +{ +public: + explicit BridgeFinder(SSACFG const& _cfg): + m_cfg(_cfg), + m_bridgeVertex(_cfg.numBlocks()), + m_visited(_cfg.numBlocks()), + m_disc(_cfg.numBlocks()), + m_low(_cfg.numBlocks()) + { + size_t time = 0; + dfs(time, _cfg.entry, std::nullopt); + } + + bool bridgeVertex(SSACFG::BlockId const& _blockId) const + { + return m_bridgeVertex[_blockId.value]; + } + +private: + void dfs(size_t& _time, SSACFG::BlockId const& _vertex, std::optional const& _parent) + { + m_visited[_vertex.value] = true; + m_disc[_vertex.value] = _time; + m_low[_vertex.value] = _time; + ++_time; + + auto const& currentBlock = m_cfg.block(_vertex); + currentBlock.forEachExit([&](SSACFG::BlockId const& _exit) + { + processNeighbor(_exit, _time, _vertex, currentBlock.entries, _parent); + }); + + for (SSACFG::BlockId const neighbor: currentBlock.entries) + processNeighbor(neighbor, _time, _vertex, currentBlock.entries, _parent); + } + + void processNeighbor( + SSACFG::BlockId const& _neighbor, + size_t& _time, + SSACFG::BlockId const& _vertex, + std::set const& _vertexEntries, + std::optional const& _parent + ) + { + if (_neighbor == _parent) + return; + + if (!m_visited[_neighbor.value]) + { + dfs(_time, _neighbor, _vertex); + m_low[_vertex.value] = std::min(m_low[_vertex.value], m_low[_neighbor.value]); + if (m_low[_neighbor.value] > m_disc[_vertex.value]) + { + // vertex <-> neighbor is a bridge in the undirected graph + bool const edgeNeighborToVertex = _vertexEntries.contains(_neighbor); + bool const edgeVertexToNeighbor = m_cfg.block(_neighbor).entries.contains(_vertex); + + // special case: if it's the entry itself, we mark it as bridge vertex (provided correct orientation), + // so that functions which do nothing but revert have their whole tree marked as such (sans loops) + if (!_parent) + m_bridgeVertex[_vertex.value] = edgeVertexToNeighbor; + // Since we are not really undirected, check if we don't have a cycle (u -> v and v -> u) and see, + // which edge really exists here. + // Then record the targeted vertex as bridge vertex. + if (edgeVertexToNeighbor && !edgeNeighborToVertex) + // bridge vertex -> neighbor + m_bridgeVertex[_neighbor.value] = true; + else if (edgeNeighborToVertex && !edgeVertexToNeighbor) + // bridge neighbor -> vertex + m_bridgeVertex[_vertex.value] = true; + } + } + else + m_low[_vertex.value] = std::min(m_low[_vertex.value], m_disc[_neighbor.value]); + } + + SSACFG const& m_cfg; + // determines whether a vertex is a bridge vertex. optimizing for performance over space with u8. + std::vector m_bridgeVertex; + // determines whether a vertex is visited. optimizing for performance over space with u8. + std::vector m_visited; + // vertex discovery time + std::vector m_disc; + // minimum discovery time of subtree - if m_low[child] > m_low[parent], we have a bridge + std::vector m_low; +}; + +} From 19a78fbd06ffff2d49927f1bac9d235d61a246a4 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Wed, 8 Oct 2025 11:17:10 +0200 Subject: [PATCH 027/243] ssacfg: add termination path analysis --- libyul/CMakeLists.txt | 2 + .../evm/ssa/JunkAdmittingBlocksFinder.cpp | 73 +++++++++++++++++++ .../evm/ssa/JunkAdmittingBlocksFinder.h | 46 ++++++++++++ libyul/backends/evm/ssa/SSACFG.h | 52 +++++++++---- 4 files changed, 160 insertions(+), 13 deletions(-) create mode 100644 libyul/backends/evm/ssa/JunkAdmittingBlocksFinder.cpp create mode 100644 libyul/backends/evm/ssa/JunkAdmittingBlocksFinder.h diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 78fef861bd..15b06a6235 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -76,6 +76,8 @@ add_library(yul backends/evm/ssa/BridgeFinder.h backends/evm/ssa/ControlFlow.cpp backends/evm/ssa/ControlFlow.h + backends/evm/ssa/JunkAdmittingBlocksFinder.cpp + backends/evm/ssa/JunkAdmittingBlocksFinder.h backends/evm/ssa/LivenessAnalysis.cpp backends/evm/ssa/LivenessAnalysis.h backends/evm/ssa/SSACFGLoopNestingForest.cpp diff --git a/libyul/backends/evm/ssa/JunkAdmittingBlocksFinder.cpp b/libyul/backends/evm/ssa/JunkAdmittingBlocksFinder.cpp new file mode 100644 index 0000000000..d7c9202ef4 --- /dev/null +++ b/libyul/backends/evm/ssa/JunkAdmittingBlocksFinder.cpp @@ -0,0 +1,73 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include + +namespace solidity::yul::ssa +{ + +JunkAdmittingBlocksFinder::JunkAdmittingBlocksFinder(SSACFG const& _cfg, ForwardSSACFGTopologicalSort const& _topologicalSort): + m_blockAllowsJunk(_cfg.numBlocks(), false) +{ + // special case: only one block here, we mark it as junkable in case it's not a function return + if (_topologicalSort.preOrder().size() == 1) + { + SSACFG::BlockId const id {_topologicalSort.preOrder().front()}; + m_blockAllowsJunk[id.value] = !_cfg.block(id).isFunctionReturnBlock(); + return; + } + + // Find all bridges, i.e., vertices, which upon removal increase the number of connected components. + // Translated to SSA CFGs this means: + // - control flow that enters a bridge vertex never returns to a previously visited block + // - there is no parallel path to a child of the vertex, ie, adding junk is fine in terms of stack balance + BridgeFinder const bridgeFinder(_cfg); + + // of the bridge vertices, we have the exclude the ones that can lead to a function return + std::vector toVisit; + for (auto const blockIndex: _topologicalSort.preOrder()) + { + SSACFG::BlockId const blockId {blockIndex}; + m_blockAllowsJunk[blockIndex] = bridgeFinder.bridgeVertex(blockId) || _cfg.block(blockId).isTerminationBlock(); + if (_cfg.block(blockId).isFunctionReturnBlock()) + toVisit.emplace_back(SSACFG::BlockId{blockIndex}); + } + + std::vector visited(_cfg.numBlocks(), false); + while (!toVisit.empty()) + { + auto const blockId = toVisit.back(); + auto const& block = _cfg.block(blockId); + toVisit.pop_back(); + + m_blockAllowsJunk[blockId.value] = false; + visited[blockId.value] = true; + for (auto const& entry: block.entries) + if (!visited[entry.value]) + toVisit.emplace_back(entry); + } +} + +bool JunkAdmittingBlocksFinder::allowsAdditionOfJunk(SSACFG::BlockId const& _blockId) const +{ + return m_blockAllowsJunk[_blockId.value]; +} + +} diff --git a/libyul/backends/evm/ssa/JunkAdmittingBlocksFinder.h b/libyul/backends/evm/ssa/JunkAdmittingBlocksFinder.h new file mode 100644 index 0000000000..e567603dd3 --- /dev/null +++ b/libyul/backends/evm/ssa/JunkAdmittingBlocksFinder.h @@ -0,0 +1,46 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include + +#include +#include + +namespace solidity::yul::ssa +{ + +/// Identifies blocks where stack balance constraints can be relaxed. +/// These are blocks that inevitably terminate down the line (i.e., there is no path to a function return exit) and +/// which are "bridge vertices". For a bridge, the graph decomposes into `G1` and `G2` with a singular edge `e=(v1->v2)` +/// between them. Therefore, traversal into `G2` cannot escape back into `G1` and in particular there cannot be a +/// parallel path into G2 that has relaxed constraints with respect to introducing junk. Consequently, there cannot be +/// a situation in which a junk-bloated stack has to be unified with a slimmer stack layout stemming from another path +/// into `G2`. +class JunkAdmittingBlocksFinder +{ +public: + explicit JunkAdmittingBlocksFinder(SSACFG const& _cfg, ForwardSSACFGTopologicalSort const& _topologicalSort); + bool allowsAdditionOfJunk(SSACFG::BlockId const& _blockId) const; +private: + std::vector m_blockAllowsJunk; +}; + +} diff --git a/libyul/backends/evm/ssa/SSACFG.h b/libyul/backends/evm/ssa/SSACFG.h index 32768ca6bd..5ea8247ce3 100644 --- a/libyul/backends/evm/ssa/SSACFG.h +++ b/libyul/backends/evm/ssa/SSACFG.h @@ -52,17 +52,14 @@ class SSACFG struct BlockId { size_t value = std::numeric_limits::max(); - bool operator<(BlockId const& _rhs) const { return value < _rhs.value; } - bool operator==(BlockId const& _rhs) const { return value == _rhs.value; } - bool operator!=(BlockId const& _rhs) const { return value != _rhs.value; } + auto operator<=>(BlockId const&) const = default; }; struct ValueId { - size_t value = std::numeric_limits::max(); - bool hasValue() const { return value != std::numeric_limits::max(); } - bool operator<(ValueId const& _rhs) const { return value < _rhs.value; } - bool operator==(ValueId const& _rhs) const { return value == _rhs.value; } - bool operator!=(ValueId const& _rhs) const { return value != _rhs.value; } + using ValueType = size_t; + ValueType value = std::numeric_limits::max(); + bool hasValue() const { return value != std::numeric_limits::max(); } + auto operator<=>(ValueId const&) const = default; }; struct BuiltinCall @@ -93,26 +90,26 @@ class SSACFG struct MainExit {}; struct ConditionalJump { - langutil::DebugData::ConstPtr debugData; + langutil::DebugData::ConstPtr debugData{}; ValueId condition; BlockId nonZero; BlockId zero; }; struct Jump { - langutil::DebugData::ConstPtr debugData; + langutil::DebugData::ConstPtr debugData{}; BlockId target; }; struct JumpTable { - langutil::DebugData::ConstPtr debugData; + langutil::DebugData::ConstPtr debugData{}; ValueId value; std::map cases; BlockId defaultCase; }; struct FunctionReturn { - langutil::DebugData::ConstPtr debugData; + langutil::DebugData::ConstPtr debugData{}; std::vector returnValues; }; struct Terminated {}; @@ -138,6 +135,26 @@ class SSACFG _callable(jumpTable->defaultCase); } } + + bool isMainExitBlock() const + { + return std::holds_alternative(exit); + } + + bool isTerminationBlock() const + { + return std::holds_alternative(exit); + } + + bool isFunctionReturnBlock() const + { + return std::holds_alternative(exit); + } + + bool isJumpBlock() const + { + return std::holds_alternative(exit); + } }; BlockId makeBlock(langutil::DebugData::ConstPtr _debugData) { @@ -148,6 +165,7 @@ class SSACFG BasicBlock& block(BlockId _id) { return m_blocks.at(_id.value); } BasicBlock const& block(BlockId _id) const { return m_blocks.at(_id.value); } size_t numBlocks() const { return m_blocks.size(); } + private: std::vector m_blocks; public: @@ -170,13 +188,21 @@ class SSACFG { return std::holds_alternative(valueInfo(_var)); } + bool isPhiValue(ValueId const _var) const + { + return std::holds_alternative(valueInfo(_var)); + } ValueInfo& valueInfo(ValueId const _var) { return m_valueInfos.at(_var.value); } ValueInfo const& valueInfo(ValueId const _var) const { - return m_valueInfos.at(_var.value); + return valueInfo(_var.value); + } + ValueInfo const& valueInfo(ValueId::ValueType const _var) const + { + return m_valueInfos.at(_var); } ValueId newPhi(BlockId const _definingBlock) { From 9bc0122405c5cb785e070dde7b91c9b1a39be67c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 10 Oct 2025 17:19:06 +0200 Subject: [PATCH 028/243] Consistency tweaks and syntax cleanup in scripts that deal with solc version --- scripts/build.sh | 18 ++++---- scripts/ci/build.sh | 16 +++---- scripts/ci/build_emscripten.sh | 13 +++--- scripts/create_source_tarball.sh | 72 ++++++++++++++++---------------- 4 files changed, 57 insertions(+), 62 deletions(-) diff --git a/scripts/build.sh b/scripts/build.sh index 6edd60bd9b..5d7765e6b2 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,26 +1,26 @@ #!/usr/bin/env bash set -e -ROOTDIR="$(dirname "$0")/.." +ROOTDIR=$(dirname "$0")/.. BUILDDIR="${ROOTDIR}/build" -if [[ $# -eq 0 ]]; then +if (( $# == 0 )); then BUILD_TYPE=Release else BUILD_TYPE="$1" fi -if [[ "$(git tag --points-at HEAD 2>/dev/null)" == v* ]]; then - touch "${ROOTDIR}/prerelease.txt" +if [[ $(git tag --points-at HEAD 2> /dev/null) == v* ]]; then + touch "${ROOTDIR}/prerelease.txt" fi -mkdir -p "${BUILDDIR}" -cd "${BUILDDIR}" +mkdir -p "$BUILDDIR" +cd "$BUILDDIR" cmake .. -DCMAKE_BUILD_TYPE="$BUILD_TYPE" "${@:2}" make -j2 -if [[ "${CI}" == "" ]]; then - echo "Installing ..." - sudo make install +if [[ $CI == "" ]]; then + echo "Installing ..." + sudo make install fi diff --git a/scripts/ci/build.sh b/scripts/ci/build.sh index e1d119a064..1acec5a201 100755 --- a/scripts/ci/build.sh +++ b/scripts/ci/build.sh @@ -9,27 +9,23 @@ prerelease_source="${1:-ci}" cd "${ROOTDIR}" -# shellcheck disable=SC2166 -if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" -o -n "$FORCE_RELEASE" ] -then - echo -n >prerelease.txt +if [[ $CIRCLE_BRANCH == release || -n $CIRCLE_TAG || -n $FORCE_RELEASE ]]; then + echo -n > prerelease.txt else # Use last commit date rather than build date to avoid ending up with builds for # different platforms having different version strings (and therefore producing different bytecode) # if the CI is triggered just before midnight. - TZ=UTC git show --quiet --date="format-local:%Y.%-m.%-d" --format="${prerelease_source}.%cd" >prerelease.txt + TZ=UTC git show --quiet --date="format-local:%Y.%-m.%-d" --format="${prerelease_source}.%cd" > prerelease.txt fi -if [ -n "$CIRCLE_SHA1" ] -then - echo -n "$CIRCLE_SHA1" >commit_hash.txt +if [[ -n $CIRCLE_SHA1 ]]; then + echo -n "$CIRCLE_SHA1" > commit_hash.txt fi mkdir -p build cd build -# shellcheck disable=SC2166 -[ -n "$COVERAGE" -a "$CIRCLE_BRANCH" != release -a -z "$CIRCLE_TAG" ] && CMAKE_OPTIONS="$CMAKE_OPTIONS -DCOVERAGE=ON" +[[ -n $COVERAGE && $CIRCLE_BRANCH != release && -z $CIRCLE_TAG ]] && CMAKE_OPTIONS="$CMAKE_OPTIONS -DCOVERAGE=ON" # shellcheck disable=SC2086 cmake .. -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Release}" $CMAKE_OPTIONS diff --git a/scripts/ci/build_emscripten.sh b/scripts/ci/build_emscripten.sh index e4d25f195f..836c4d41c5 100755 --- a/scripts/ci/build_emscripten.sh +++ b/scripts/ci/build_emscripten.sh @@ -45,19 +45,16 @@ function build() { cd "${ROOT_DIR}" - # shellcheck disable=SC2166 - if [[ "$CIRCLE_BRANCH" = release || -n "$CIRCLE_TAG" || -n "$FORCE_RELEASE" || "$(git tag --points-at HEAD 2>/dev/null)" == v* ]] - then - echo -n >prerelease.txt + if [[ $CIRCLE_BRANCH == release || -n $CIRCLE_TAG || -n $FORCE_RELEASE || $(git tag --points-at HEAD 2> /dev/null) == v* ]]; then + echo -n > prerelease.txt else # Use last commit date rather than build date to avoid ending up with builds for # different platforms having different version strings (and therefore producing different bytecode) # if the CI is triggered just before midnight. - TZ=UTC git show --quiet --date="format-local:%Y.%-m.%-d" --format="${prerelease_source}.%cd" >prerelease.txt + TZ=UTC git show --quiet --date="format-local:%Y.%-m.%-d" --format="${prerelease_source}.%cd" > prerelease.txt fi - if [ -n "$CIRCLE_SHA1" ] - then - echo -n "$CIRCLE_SHA1" >commit_hash.txt + if [[ -n $CIRCLE_SHA1 ]]; then + echo -n "$CIRCLE_SHA1" > commit_hash.txt fi # Disable warnings for unqualified `move()` calls, introduced and enabled by diff --git a/scripts/create_source_tarball.sh b/scripts/create_source_tarball.sh index f5e2892aca..1a30992453 100755 --- a/scripts/create_source_tarball.sh +++ b/scripts/create_source_tarball.sh @@ -1,39 +1,41 @@ -#!/usr/bin/env sh -# - -set -e +#!/usr/bin/env bash +set -euo pipefail REPO_ROOT="$(dirname "$0")"/.. -( - cd "$REPO_ROOT" - version=$(scripts/get_version.sh) - commithash=$(git rev-parse --short=8 HEAD) - commitdate=$(git show --format=%ci HEAD | head -n 1 | cut - -b1-10 | sed -e 's/-0?/./' | sed -e 's/-0?/./') +cd "$REPO_ROOT" +version=$(scripts/get_version.sh) +commit_hash=$(git rev-parse --short=8 HEAD) +commit_date=$(git show --format=%ci HEAD | head -n 1 | cut - -b1-10 | sed -e 's/-0?/./' | sed -e 's/-0?/./') + +# File exists and has zero size -> not a prerelease +if [[ -e prerelease.txt && ! -s prerelease.txt ]]; then + version_string="$version" +else + version_string="${version}-nightly-${commit_date}-${commit_hash}" +fi + +TEMPDIR=$(mktemp -d -t "solc-src-tarball-XXXXXX") +SOLDIR="${TEMPDIR}/solidity_${version_string}/" +mkdir "$SOLDIR" + +# Ensure that submodules are initialized. +git submodule update --init --recursive +# Store the current source +git checkout-index --all --prefix="$SOLDIR" +# shellcheck disable=SC2016 +SOLDIR="$SOLDIR" git submodule foreach 'git checkout-index --all --prefix="${SOLDIR}/${sm_path}/"' - # file exists and has zero size -> not a prerelease - if [ -e prerelease.txt ] && [ ! -s prerelease.txt ] - then - versionstring="$version" - else - versionstring="$version-nightly-$commitdate-$commithash" - fi +# Include the commit hash and prerelease suffix in the tarball +echo "$commit_hash" > "${SOLDIR}/commit_hash.txt" +[[ -e prerelease.txt && ! -s prerelease.txt ]] && cp prerelease.txt "${SOLDIR}/" - TEMPDIR=$(mktemp -d) - SOLDIR="$TEMPDIR/solidity_$versionstring/" - mkdir "$SOLDIR" - # Ensure that submodules are initialized. - git submodule update --init --recursive - # Store the current source - git checkout-index -a --prefix="$SOLDIR" - # shellcheck disable=SC2016 - SOLDIR="$SOLDIR" git submodule foreach 'git checkout-index -a --prefix="$SOLDIR/$sm_path/"' - # Store the commit hash - echo "$commithash" > "$SOLDIR/commit_hash.txt" - if [ -e prerelease.txt ] && [ ! -s prerelease.txt ] - then - cp prerelease.txt "$SOLDIR/" - fi - mkdir -p "$REPO_ROOT/upload" - tar --owner 0 --group 0 -czf "$REPO_ROOT/upload/solidity_$versionstring.tar.gz" -C "$TEMPDIR" "solidity_$versionstring" - rm -r "$TEMPDIR" -) +mkdir -p "$REPO_ROOT/upload" +tar \ + --owner 0 \ + --group 0 \ + --create \ + --gzip \ + --file "${REPO_ROOT}/upload/solidity_${version_string}.tar.gz" \ + --directory "$TEMPDIR" \ + "solidity_${version_string}" +rm -r "$TEMPDIR" From 893cf591152b580869d965f8abad2b812bf45517 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Mon, 13 Oct 2025 17:24:45 +0200 Subject: [PATCH 029/243] Remove ubuntu 22.04 buildpackdeps and ci --- .github/workflows/buildpack-deps.yml | 3 +- scripts/ci/buildpack-deps_test_ubuntu2204.sh | 1 - .../buildpack-deps/Dockerfile.ubuntu2204 | 108 ------------------ 3 files changed, 1 insertion(+), 111 deletions(-) delete mode 120000 scripts/ci/buildpack-deps_test_ubuntu2204.sh delete mode 100644 scripts/docker/buildpack-deps/Dockerfile.ubuntu2204 diff --git a/.github/workflows/buildpack-deps.yml b/.github/workflows/buildpack-deps.yml index 5e49f2f326..0671c5b398 100644 --- a/.github/workflows/buildpack-deps.yml +++ b/.github/workflows/buildpack-deps.yml @@ -9,7 +9,6 @@ on: paths: - 'scripts/docker/buildpack-deps/Dockerfile.emscripten' - 'scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz' - - 'scripts/docker/buildpack-deps/Dockerfile.ubuntu2204' - 'scripts/docker/buildpack-deps/Dockerfile.ubuntu2404' - 'scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang' @@ -26,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - image_variant: [emscripten, ubuntu.clang.ossfuzz, ubuntu2004, ubuntu2204, ubuntu2404, ubuntu2404.clang] + image_variant: [emscripten, ubuntu.clang.ossfuzz, ubuntu2004, ubuntu2404, ubuntu2404.clang] steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 diff --git a/scripts/ci/buildpack-deps_test_ubuntu2204.sh b/scripts/ci/buildpack-deps_test_ubuntu2204.sh deleted file mode 120000 index c07a74de4f..0000000000 --- a/scripts/ci/buildpack-deps_test_ubuntu2204.sh +++ /dev/null @@ -1 +0,0 @@ -build.sh \ No newline at end of file diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2204 b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2204 deleted file mode 100644 index be3daee909..0000000000 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2204 +++ /dev/null @@ -1,108 +0,0 @@ -# vim:syntax=dockerfile -#------------------------------------------------------------------------------ -# Dockerfile for building and testing Solidity Compiler on CI -# Target: Ubuntu 22.04 (Jammy Jellyfish) -# URL: https://hub.docker.com/r/ethereum/solidity-buildpack-deps -# -# This file is part of solidity. -# -# solidity is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# solidity is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with solidity. If not, see -# -# (c) 2016-2025 solidity contributors. -#------------------------------------------------------------------------------ -FROM buildpack-deps:jammy AS base -LABEL version="3" - -ARG DEBIAN_FRONTEND=noninteractive - -RUN set -ex; \ - dist=$(grep DISTRIB_CODENAME /etc/lsb-release | cut -d= -f2); \ - echo "deb http://ppa.launchpad.net/ethereum/cpp-build-deps/ubuntu $dist main" >> /etc/apt/sources.list ; \ - apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1c52189c923f6ca9 ; \ - apt-get update; \ - apt-get install -qqy --no-install-recommends \ - build-essential \ - cmake \ - jq \ - clang \ - libboost-filesystem-dev \ - libboost-program-options-dev \ - libboost-system-dev \ - libboost-test-dev \ - libcln-dev \ - locales-all \ - lsof \ - ninja-build \ - python3-pip \ - python3-sphinx \ - software-properties-common \ - sudo \ - unzip \ - zip; \ - pip3 install \ - codecov \ - colorama \ - deepdiff \ - parsec \ - pygments-lexer-solidity \ - pylint \ - requests \ - tabulate \ - z3-solver; - -# Eldarica -RUN set -ex; \ - apt-get update; \ - apt-get install -qqy \ - openjdk-11-jre; \ - eldarica_version="2.1"; \ - wget "https://github.com/uuverifiers/eldarica/releases/download/v${eldarica_version}/eldarica-bin-${eldarica_version}.zip" -O /opt/eld_binaries.zip; \ - test "$(sha256sum /opt/eld_binaries.zip)" = "0ac43f45c0925383c9d2077f62bbb515fd792375f3b2b101b30c9e81dcd7785c /opt/eld_binaries.zip"; \ - unzip /opt/eld_binaries.zip -d /opt; \ - rm -f /opt/eld_binaries.zip; - -# CVC5 -RUN set -ex; \ - cvc5_version="1.2.0"; \ - cvc5_archive_name="cvc5-Linux-x86_64-static"; \ - wget "https://github.com/cvc5/cvc5/releases/download/cvc5-${cvc5_version}/${cvc5_archive_name}.zip" -O /opt/cvc5.zip; \ - test "$(sha256sum /opt/cvc5.zip)" = "d18f174ff9a11923c32c3f871f844ed16bd77a28f51050b8e7c8d821c98a1c2e /opt/cvc5.zip"; \ - unzip -j /opt/cvc5.zip "${cvc5_archive_name}/bin/cvc5" -d /usr/bin; \ - rm -f /opt/cvc5.zip; - -# Z3 -RUN set -ex; \ - z3_version="4.13.3"; \ - z3_archive_name="z3-${z3_version}-x64-glibc-2.35"; \ - wget "https://github.com/Z3Prover/z3/releases/download/z3-${z3_version}/${z3_archive_name}.zip" -O /opt/z3.zip; \ - test "$(sha256sum /opt/z3.zip)" = "32c7377026733c9d7b33c21cd77a68f50ba682367207b031a6bfd80140a8722f /opt/z3.zip"; \ - unzip -j /opt/z3.zip "${z3_archive_name}/bin/z3" -d /usr/bin; \ - rm -f /opt/z3.zip; - -FROM base AS libraries - -# EVMONE -RUN set -ex; \ - wget -O /usr/src/evmone.tar.gz https://github.com/ipsilon/evmone/releases/download/v0.13.0/evmone-0.13.0-linux-x86_64.tar.gz; \ - test "$(sha256sum /usr/src/evmone.tar.gz)" = "94efc7fa27ff94018003ad95a62c20b17a45f027d434eb642c8e345e8d4d3cad /usr/src/evmone.tar.gz"; \ - cd /usr; \ - tar -xf /usr/src/evmone.tar.gz; \ - rm -rf /usr/src/evmone.tar.gz - -FROM base -COPY --from=libraries /usr/lib /usr/lib -COPY --from=libraries /usr/bin /usr/bin -COPY --from=libraries /usr/include /usr/include -COPY --from=libraries /opt/eldarica /opt/eldarica -ENV PATH="$PATH:/opt/eldarica" From 0ec7faf3dfb23296dc6130da45839ffcd8a09bdd Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Tue, 14 Oct 2025 12:57:39 +0200 Subject: [PATCH 030/243] Remove ubuntu 2004 from buildpackdeps workflow --- .github/workflows/buildpack-deps.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/buildpack-deps.yml b/.github/workflows/buildpack-deps.yml index 0671c5b398..c4c84dae16 100644 --- a/.github/workflows/buildpack-deps.yml +++ b/.github/workflows/buildpack-deps.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - image_variant: [emscripten, ubuntu.clang.ossfuzz, ubuntu2004, ubuntu2404, ubuntu2404.clang] + image_variant: [emscripten, ubuntu.clang.ossfuzz, ubuntu2404, ubuntu2404.clang] steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 From d8b4bd871135cbb08d77366709778e73669c7f76 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Tue, 14 Oct 2025 13:00:29 +0200 Subject: [PATCH 031/243] Use z3-solver python package instead of z3 binaries --- .../docker/buildpack-deps/Dockerfile.ubuntu2404 | 14 +++----------- .../buildpack-deps/Dockerfile.ubuntu2404.clang | 14 +++----------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 index 1edf6f37e4..a67fd248c5 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 @@ -22,7 +22,7 @@ # (c) 2016-2024 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:noble AS base -LABEL version="4" +LABEL version="5" ARG DEBIAN_FRONTEND=noninteractive @@ -31,6 +31,7 @@ ARG DEBIAN_FRONTEND=noninteractive ENV PIP_BREAK_SYSTEM_PACKAGES 1 RUN set -ex; \ + z3_version="4.13.3"; \ dist=$(grep DISTRIB_CODENAME /etc/lsb-release | cut -d= -f2); \ echo "deb http://ppa.launchpad.net/ethereum/cpp-build-deps/ubuntu $dist main" >> /etc/apt/sources.list ; \ apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1c52189c923f6ca9 ; \ @@ -62,7 +63,7 @@ RUN set -ex; \ pylint \ requests \ tabulate \ - z3-solver; + z3-solver=="${z3_version}"; # Eldarica RUN set -ex; \ @@ -84,15 +85,6 @@ RUN set -ex; \ unzip -j /opt/cvc5.zip "${cvc5_archive_name}/bin/cvc5" -d /usr/bin; \ rm -f /opt/cvc5.zip; -# Z3 -RUN set -ex; \ - z3_version="4.13.3"; \ - z3_archive_name="z3-${z3_version}-x64-glibc-2.35"; \ - wget "https://github.com/Z3Prover/z3/releases/download/z3-${z3_version}/${z3_archive_name}.zip" -O /opt/z3.zip; \ - test "$(sha256sum /opt/z3.zip)" = "32c7377026733c9d7b33c21cd77a68f50ba682367207b031a6bfd80140a8722f /opt/z3.zip"; \ - unzip -j /opt/z3.zip "${z3_archive_name}/bin/z3" -d /usr/bin; \ - rm -f /opt/z3.zip; - FROM base AS libraries # EVMONE diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang index 3622cc7eba..ec339cbcb3 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang @@ -22,7 +22,7 @@ # (c) 2016-2024 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:noble AS base -LABEL version="5" +LABEL version="6" ARG DEBIAN_FRONTEND=noninteractive @@ -31,6 +31,7 @@ ARG DEBIAN_FRONTEND=noninteractive ENV PIP_BREAK_SYSTEM_PACKAGES 1 RUN set -ex; \ + z3_version="4.13.3"; \ dist=$(grep DISTRIB_CODENAME /etc/lsb-release | cut -d= -f2); \ echo "deb http://ppa.launchpad.net/ethereum/cpp-build-deps/ubuntu $dist main" >> /etc/apt/sources.list ; \ apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1c52189c923f6ca9 ; \ @@ -60,7 +61,7 @@ RUN set -ex; \ pylint \ requests \ tabulate \ - z3-solver; + z3-solver=="${z3_version}"; # Eldarica RUN set -ex; \ @@ -83,15 +84,6 @@ RUN set -ex; \ unzip -j /opt/cvc5.zip "${cvc5_archive_name}/bin/cvc5" -d /usr/bin; \ rm -f /opt/cvc5.zip; -# Z3 -RUN set -ex; \ - z3_version="4.13.3"; \ - z3_archive_name="z3-${z3_version}-x64-glibc-2.35"; \ - wget "https://github.com/Z3Prover/z3/releases/download/z3-${z3_version}/${z3_archive_name}.zip" -O /opt/z3.zip; \ - test "$(sha256sum /opt/z3.zip)" = "32c7377026733c9d7b33c21cd77a68f50ba682367207b031a6bfd80140a8722f /opt/z3.zip"; \ - unzip -j /opt/z3.zip "${z3_archive_name}/bin/z3" -d /usr/bin; \ - rm -f /opt/z3.zip; - FROM base AS libraries ENV CC clang From ab3ab422fc4e4532fc1802bd172f663b0018f65f Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Wed, 15 Oct 2025 14:34:19 +0200 Subject: [PATCH 032/243] Use a read-only GitHub token when retrieving PR information --- .circleci/config.yml | 8 ++++---- scripts/common/rest_api_helpers.py | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 48d86a97c4..dfa64175de 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -142,8 +142,8 @@ commands: FOUNDRY_REPO="foundry-rs/foundry" FOUNDRY_VERSION="<< parameters.version >>" # Make authenticated requests when the Github token is available - if [[ -n "$GITHUB_ACCESS_TOKEN" ]]; then - EXTRA_HEADERS=(--header 'Authorization: Bearer '"${GITHUB_ACCESS_TOKEN}") + if [[ -n "$GITHUB_READ_TOKEN" ]]; then + EXTRA_HEADERS=(--header 'Authorization: Bearer '"${GITHUB_READ_TOKEN}") fi FOUNDRY_RELEASE_SHA=$(curl \ --silent \ @@ -1418,8 +1418,8 @@ jobs: name: Retrieve EDR latest release tag command: | # Make authenticated requests when the Github token is available - if [[ -n "$GITHUB_ACCESS_TOKEN" ]]; then - EXTRA_HEADERS=(--header "Authorization: Bearer ${GITHUB_ACCESS_TOKEN}") + if [[ -n "$GITHUB_READ_TOKEN" ]]; then + EXTRA_HEADERS=(--header "Authorization: Bearer ${GITHUB_READ_TOKEN}") fi EDR_LATEST_RELEASE_TAG=$( curl \ diff --git a/scripts/common/rest_api_helpers.py b/scripts/common/rest_api_helpers.py index caae2b5db8..9b2f942616 100644 --- a/scripts/common/rest_api_helpers.py +++ b/scripts/common/rest_api_helpers.py @@ -84,10 +84,13 @@ def __init__(self, project_slug: str, debug_requests: bool): self.debug_requests = debug_requests def pull_request(self, pr_id: int) -> dict: + # GITHUB_READ_TOKEN should be a PAT with read-only permissions + # This is used by CircleCI and local scripts to avoid GitHub API rate limits + headers = {'Authorization': f'Bearer {environ.get("GITHUB_READ_TOKEN")}'} if 'GITHUB_READ_TOKEN' in environ else {} return query_api( f'{self.BASE_URL}/repos/{self.project_slug}/pulls/{pr_id}', {}, - {}, + headers, self.debug_requests ) From b36dd21cc27271731ff3eec75903594f4c67afdf Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Thu, 9 Oct 2025 14:33:43 -0300 Subject: [PATCH 033/243] bump evmone to 0.16.0 in ubuntu images --- .../buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz | 12 ++++-------- scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 | 8 ++++---- .../buildpack-deps/Dockerfile.ubuntu2404.clang | 8 ++++---- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz b/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz index c14039d90d..5a96e6889d 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz @@ -22,7 +22,7 @@ # (c) 2016-2021 solidity contributors. #------------------------------------------------------------------------------ FROM gcr.io/oss-fuzz-base/base-builder AS base -LABEL version="11" +LABEL version="12" ARG DEBIAN_FRONTEND=noninteractive @@ -64,10 +64,6 @@ RUN apt-get update; \ requests \ tabulate; -# Ensure that the expected version of Clang is installed, as the base image may update it in the future: -# https://github.com/google/oss-fuzz/blob/5e96edbdf285045cc82dbca5600cbe994a3b1a74/infra/base-images/base-clang/checkout_build_install_llvm.sh#L57 -RUN test "$(clang -dumpversion | cut -d. -f1)" = "18" || (echo "Error: Clang 18 not found!" && exit 1) - FROM base AS libraries # Boost @@ -78,8 +74,8 @@ RUN set -ex; \ tar -xf boost.tar.bz2; \ rm boost.tar.bz2; \ cd boost_1_83_0; \ - export CXXFLAGS="-std=c++20 -nostdinc++ -I/usr/lib/llvm-18/include/c++/v1 -pthread"; \ - export LDFLAGS="-stdlib=libc++ -L/usr/lib/llvm-18/lib"; \ + export CXXFLAGS="$CXXFLAGS -std=c++20 -nostdinc++ -I/usr/local/include/c++/v1 -I/usr/local/include/x86_64-unknown-linux-gnu/c++/v1 -pthread"; \ + export LDFLAGS="$LDFLAGS -stdlib=libc++ -lpthread"; \ ./bootstrap.sh --with-toolset=clang --prefix=/usr; \ ./b2 toolset=clang \ cxxflags="${CXXFLAGS}" \ @@ -143,7 +139,7 @@ RUN set -ex; \ # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.13.0" --recurse-submodules https://github.com/ipsilon/evmone.git; \ + git clone --branch="v0.16.0" --recurse-submodules https://github.com/ipsilon/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 index a67fd248c5..9cf381f760 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 @@ -22,13 +22,13 @@ # (c) 2016-2024 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:noble AS base -LABEL version="5" +LABEL version="6" ARG DEBIAN_FRONTEND=noninteractive # From Python3.11, pip requires a virtual environment, and will thus terminate when installing packages globally. # Since we're building this image from scratch, it's perfectly fine to use the below flag. -ENV PIP_BREAK_SYSTEM_PACKAGES 1 +ENV PIP_BREAK_SYSTEM_PACKAGES=1 RUN set -ex; \ z3_version="4.13.3"; \ @@ -89,8 +89,8 @@ FROM base AS libraries # EVMONE RUN set -ex; \ - wget -O /usr/src/evmone.tar.gz https://github.com/ipsilon/evmone/releases/download/v0.13.0/evmone-0.13.0-linux-x86_64.tar.gz; \ - test "$(sha256sum /usr/src/evmone.tar.gz)" = "94efc7fa27ff94018003ad95a62c20b17a45f027d434eb642c8e345e8d4d3cad /usr/src/evmone.tar.gz"; \ + wget -O /usr/src/evmone.tar.gz https://github.com/ipsilon/evmone/releases/download/v0.16.0/evmone-0.16.0-linux-x86_64.tar.gz; \ + test "$(sha256sum /usr/src/evmone.tar.gz)" = "09a59e07bde21bbf08b821db9097ef4db9ff06f1484b7b14497a16a701e72233 /usr/src/evmone.tar.gz"; \ cd /usr; \ tar -xf /usr/src/evmone.tar.gz; \ rm -rf /usr/src/evmone.tar.gz diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang index ec339cbcb3..aec6fe6e26 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang @@ -22,13 +22,13 @@ # (c) 2016-2024 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:noble AS base -LABEL version="6" +LABEL version="7" ARG DEBIAN_FRONTEND=noninteractive # From Python3.11, pip requires a virtual environment, and will thus terminate when installing packages globally. # Since we're building this image from scratch, it's perfectly fine to use the below flag. -ENV PIP_BREAK_SYSTEM_PACKAGES 1 +ENV PIP_BREAK_SYSTEM_PACKAGES=1 RUN set -ex; \ z3_version="4.13.3"; \ @@ -91,8 +91,8 @@ ENV CXX clang++ # EVMONE RUN set -ex; \ - wget -O /usr/src/evmone.tar.gz https://github.com/ipsilon/evmone/releases/download/v0.13.0/evmone-0.13.0-linux-x86_64.tar.gz; \ - test "$(sha256sum /usr/src/evmone.tar.gz)" = "94efc7fa27ff94018003ad95a62c20b17a45f027d434eb642c8e345e8d4d3cad /usr/src/evmone.tar.gz"; \ + wget -O /usr/src/evmone.tar.gz https://github.com/ipsilon/evmone/releases/download/v0.16.0/evmone-0.16.0-linux-x86_64.tar.gz; \ + test "$(sha256sum /usr/src/evmone.tar.gz)" = "09a59e07bde21bbf08b821db9097ef4db9ff06f1484b7b14497a16a701e72233 /usr/src/evmone.tar.gz"; \ cd /usr; \ tar -xf /usr/src/evmone.tar.gz; \ rm -rf /usr/src/evmone.tar.gz From 0544fd1c4914c4d238897cb8e11ed8bafee052c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 10 Oct 2025 15:44:04 +0200 Subject: [PATCH 034/243] ReleaseChecklist: Announcing releases on EF Discord is no longer possible --- ReleaseChecklist.md | 1 - 1 file changed, 1 deletion(-) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index f0148cd0e6..9680ead14b 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -116,7 +116,6 @@ At least a day before the release: - [ ] Announce on [Fosstodon](https://fosstodon.org/@solidity/), including links to the release and the blog post. - [ ] Share the announcement on Reddit in [`/r/ethdev`](https://reddit.com/r/ethdev/), cross-posted to [`/r/ethereum`](https://reddit.com/r/ethereum/). - [ ] Share the announcement on the [Solidity forum](https://forum.soliditylang.org) in the `Announcements` category. -- [ ] Share the announcement on [Project Updates](https://discord.com/channels/420394352083337236/798974456704925696) - [ ] Share the announcement on [`#solidity` channel on Matrix](https://matrix.to/#/#ethereum_solidity:gitter.im) - [ ] Share the announcement on [`#solc-tooling`](https://matrix.to/#/#solc-tooling:matrix.org) - [ ] If anything went wrong this time, mention it in [Learning from Past Releases](https://notes.argot.org/@solidity-release-mistakes). From 6cd81cee0ac053a1ac641caf1cd8739a078e187d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 10 Oct 2025 16:04:51 +0200 Subject: [PATCH 035/243] ReleaseChecklist: Wording tweaks --- ReleaseChecklist.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 9680ead14b..36aab323af 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -1,4 +1,4 @@ -## Checklist for making a release: +## Checklist for making a Solidity release ### Requirements - [ ] GitHub account with access to [solidity](https://github.com/argotorg/solidity), [solc-js](https://github.com/argotorg/solc-js), @@ -59,19 +59,19 @@ At least a day before the release: - [ ] Thank voluntary contributors in the GitHub release notes. Use `scripts/list_contributors.sh v` to get initial list of names. Remove different variants of the same name manually before using the output. -- [ ] Check that all tests on the latest commit in `develop` are green. +- [ ] Check that all tests on the latest commit on `develop` are green. - [ ] Click the `Publish release` button on the release page, creating the tag. **Important: Must not be done before all the PRs, including changelog cleanup and date, are merged.** - [ ] Wait for the CI runs on the tag itself. ### Upload Release Artifacts and Publish Binaries - [ ] Switch to the tag that archives have to be created for. -- [ ] Create the `prerelease.txt` file: (`echo -n > prerelease.txt`). -- [ ] Run `scripts/create_source_tarball.sh` while being on the tag to create the source tarball. This will create the tarball in a directory called `upload`. -- [ ] Take the tarball from the upload directory (its name should be `solidity_x.x.x.tar.gz`, otherwise `prerelease.txt` was missing in the step before) and upload the source tarball to the release page. -- [ ] Take the `github-binaries.tar` tarball from `c_release_binaries` run of the tagged commit in circle-ci and add all binaries from it to the release page. +- [ ] Create the `prerelease.txt` file: `echo -n > prerelease.txt`. +- [ ] Run `scripts/create_source_tarball.sh` to create the source tarball. This will create the tarball in a directory called `upload`. +- [ ] Take the tarball from the upload directory (its name should be `solidity_x.x.x.tar.gz`, otherwise `prerelease.txt` was missing in the step before) and upload it to the release page. +- [ ] Take the `github-binaries.tar` tarball from `c_release_binaries` run of the tagged commit on Circle CI and add all binaries from it to the release page. Make sure it contains four binaries: `solc-windows.exe`, `solc-macos`, `solc-static-linux` and `soljson.js`. -- [ ] Take the `solc-bin-binaries.tar` tarball from `c_release_binaries` run of the tagged commit in circle-ci and add all binaries from it to solc-bin. +- [ ] Take the `solc-bin-binaries.tar` tarball from `c_release_binaries` run of the tagged commit on Circle CI and add all binaries from it to solc-bin. - [ ] Run `npm install` if you've got a clean checkout of the solc-bin repo. - [ ] Run `npm run update -- --reuse-hashes` in `solc-bin` and verify that the script has updated `list.js`, `list.txt` and `list.json` files correctly and that symlinks to the new release have been added in `solc-bin/wasm/` and `solc-bin/emscripten-wasm32/`. - [ ] Create a pull request in solc-bin and merge. @@ -101,7 +101,7 @@ At least a day before the release: - [ ] Increment the version number, create a pull request for that, merge it after tests succeeded. - [ ] Create a tag using `git tag --annotate v$VERSION` and push it with `git push --tags`. - [ ] Wait for the CI runs on the tag itself. -- [ ] Take the `solc-x.y.z.tgz` artifact from `build-package` run on the tagged commit in circle-ci. +- [ ] Take the `solc-x.y.z.tgz` artifact from `build-package` run on the tagged commit on Circle CI. Inspect the tarball to ensure that it contains an up-to-date compiler binary (`soljson.js`). - [ ] Run `npm publish solc-x.y.z.tgz` to publish the newly created tarball. @@ -116,8 +116,8 @@ At least a day before the release: - [ ] Announce on [Fosstodon](https://fosstodon.org/@solidity/), including links to the release and the blog post. - [ ] Share the announcement on Reddit in [`/r/ethdev`](https://reddit.com/r/ethdev/), cross-posted to [`/r/ethereum`](https://reddit.com/r/ethereum/). - [ ] Share the announcement on the [Solidity forum](https://forum.soliditylang.org) in the `Announcements` category. -- [ ] Share the announcement on [`#solidity` channel on Matrix](https://matrix.to/#/#ethereum_solidity:gitter.im) -- [ ] Share the announcement on [`#solc-tooling`](https://matrix.to/#/#solc-tooling:matrix.org) +- [ ] Share the announcement on [`#solidity` channel on Matrix](https://matrix.to/#/#ethereum_solidity:gitter.im). +- [ ] Share the announcement on [`#solc-tooling`](https://matrix.to/#/#solc-tooling:matrix.org). - [ ] If anything went wrong this time, mention it in [Learning from Past Releases](https://notes.argot.org/@solidity-release-mistakes). - [ ] Bump vendored dependencies. - [ ] Lean back, wait for bug reports and repeat from step 1 :). From b05924eee5b035cded1ddf89fd1cf513e431727a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 10 Oct 2025 16:05:18 +0200 Subject: [PATCH 036/243] ReleaseChecklist: Add a separate checklist for prereleases --- ReleaseChecklist.md | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 36aab323af..1b038ee96b 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -1,6 +1,6 @@ -## Checklist for making a Solidity release +# Checklist for making a Solidity release -### Requirements +## Requirements - [ ] GitHub account with access to [solidity](https://github.com/argotorg/solidity), [solc-js](https://github.com/argotorg/solc-js), [solc-bin](https://github.com/argotorg/solc-bin), [solidity-website](https://github.com/argotorg/solidity-website). - [ ] DockerHub account with push rights to the [`solc` image](https://hub.docker.com/r/ethereum/solc). @@ -12,6 +12,8 @@ - [ ] Access to the [solidity_lang Twitter account](https://twitter.com/solidity_lang). - [ ] [Reddit](https://www.reddit.com) account that is at least 10 days old with a minimum of 20 comment karma (`/r/ethereum` requirements). +## Full release + ### Pre-flight checks At least a day before the release: - [ ] Run `make linkcheck` from within `docs/` and fix any broken links it finds. @@ -121,3 +123,32 @@ At least a day before the release: - [ ] If anything went wrong this time, mention it in [Learning from Past Releases](https://notes.argot.org/@solidity-release-mistakes). - [ ] Bump vendored dependencies. - [ ] Lean back, wait for bug reports and repeat from step 1 :). + +## Prerelease +- [ ] Check that all tests on the latest commit on `develop` or `breaking` branch (whichever was chosen for the prerelease) are green. +- [ ] Create a [release on GitHub](https://github.com/argotorg/solidity/releases/new). + - Set the target to the `develop` or `breaking` branch and the tag to the new version with a prerelease suffix, e.g. `v0.8.5-pre.6`. + Version matches the next release (`develop`) or the next breaking release (`breaking`). + The prerelease number in the suffix is 1-based, sequential, resets after a full release and is counted separately for `develop` and `breaking`. + - Include the following warning: `**The release is still in progress. You may see broken links and binaries may not yet be available from all sources.**`. + - Include the current, incomplete changelog. + - Check the `Set as a pre-release` box. + - Click the `Publish release` button on the release page, creating the tag. +- [ ] Wait for the CI runs on the tag itself. +- [ ] Switch to the tag that archives have to be created for. +- [ ] Create the `prerelease.txt` file: `scripts/prerelease_suffix.sh pre "$(git describe --tags --exact-match)" > prerelease.txt`. +- [ ] Run `scripts/create_source_tarball.sh` to create the source tarball. This will create the tarball in a directory called `upload`. +- [ ] Take the tarball from the upload directory (its name should be `solidity_x.x.x-pre.N.tar.gz`, if `prerelease.txt` was created correctly) and upload it to the release page. +- [ ] Take the `github-binaries.tar` tarball from `c_release_binaries` run of the tagged commit on Circle CI and add all binaries from it to the release page. + Make sure it contains four binaries: `solc-windows.exe`, `solc-macos`, `solc-static-linux` and `soljson.js`. +- [ ] Take the `solc-bin-binaries.tar` tarball from `c_release_binaries` run of the tagged commit on Circle CI and add all binaries from it to solc-bin. +- [ ] Run `npm install` if you've got a clean checkout of the solc-bin repo. +- [ ] Run `npm run update -- --reuse-hashes` in `solc-bin` and verify that the script has updated `list.js`, `list.txt` and `list.json` files correctly and that symlinks to the new release have been added in `solc-bin/wasm/` and `solc-bin/emscripten-wasm32/`. +- [ ] Create a pull request in solc-bin and merge. +- [ ] Remove "still in progress" warning from the [release notes](https://github.com/argotorg/solidity/releases). +- [ ] Mention it on [Twitter](https://twitter.com/solidity_lang). +- [ ] Mention it on [Fosstodon](https://fosstodon.org/@solidity/). +- [ ] Mention it on [`#solidity` channel on Matrix](https://matrix.to/#/#ethereum_solidity:gitter.im). +- [ ] Mention it on [`#solc-tooling`](https://matrix.to/#/#solc-tooling:matrix.org). +- [ ] If anything went wrong this time, mention it in [Learning from Past Releases](https://notes.argot.org/@solidity-release-mistakes). +- [ ] Lean back, wait for bug reports and repeat from step 1 :). From b02d17691fc66f3e0d85487028f6466df3b12db0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 14 Oct 2025 19:11:27 +0200 Subject: [PATCH 037/243] installing-solidity.rst: Update information about prerelases --- docs/installing-solidity.rst | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 6c6d6029a1..c9286952ea 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -14,13 +14,12 @@ addition, patch-level releases with major release 0 (i.e. 0.x.y) will not contain breaking changes. That means code that compiles with version 0.x.y can be expected to compile with 0.x.z where z > y. -In addition to releases, we provide **nightly development builds** to make -it easy for developers to try out upcoming features and -provide early feedback. Note, however, that while the nightly builds are usually -very stable, they contain bleeding-edge code from the development branch and are -not guaranteed to be always working. Despite our best efforts, they might -contain undocumented and/or broken changes that will not become a part of an -actual release. They are not meant for production use. +In addition to releases, we provide **prereleases** and **nightly development builds** to make it +easy for developers to try out upcoming features and provide early feedback. +Note that such builds contain bleeding-edge code from the development branch and are not guaranteed +to be of the same quality as full releases. +Despite our best efforts, they might contain undocumented and/or broken changes that will not +become a part of an actual release. They are not meant for production use. When deploying contracts, you should use the latest released version of Solidity. This is because breaking changes, as well as new features and bug fixes are introduced regularly. @@ -544,7 +543,7 @@ The Version String in Detail The Solidity version string contains four parts: - the version number -- pre-release tag, usually set to ``develop.YYYY.MM.DD`` or ``nightly.YYYY.MM.DD`` +- pre-release tag, usually set to ``develop.YYYY.MM.DD``, ``pre.N`` or ``nightly.YYYY.MM.DD`` - commit in the format of ``commit.GITHASH`` - platform, which has an arbitrary number of items, containing details about the platform and compiler @@ -553,9 +552,11 @@ If there are local modifications, the commit will be postfixed with ``.mod``. These parts are combined as required by SemVer, where the Solidity pre-release tag equals to the SemVer pre-release and the Solidity commit and platform combined make up the SemVer build metadata. -A release example: ``0.4.8+commit.60cc1668.Emscripten.clang``. +Examples: -A pre-release example: ``0.4.9-nightly.2017.1.17+commit.6ecb4aa3.Emscripten.clang`` +- release: ``0.4.8+commit.60cc1668.Emscripten.clang`` +- pre-release: ``0.4.9-pre.3+commit.fb60450bc.Emscripten.clang`` +- nightly build: ``0.4.9-nightly.2017.1.17+commit.6ecb4aa3.Emscripten.clang`` Important Information About Versioning ====================================== @@ -563,14 +564,14 @@ Important Information About Versioning After a release is made, the patch version level is bumped, because we assume that only patch level changes follow. When changes are merged, the version should be bumped according to SemVer and the severity of the change. Finally, a release is always made with the version -of the current nightly build, but without the ``prerelease`` specifier. +of the current build, but without the ``prerelease`` specifier. Example: 1. The 0.4.0 release is made. -2. The nightly build has a version of 0.4.1 from now on. +2. Nightly builds and preerelases have a version of 0.4.1 from now on. 3. Non-breaking changes are introduced --> no change in version. 4. A breaking change is introduced --> version is bumped to 0.5.0. 5. The 0.5.0 release is made. -This behavior works well with the :ref:`version pragma `. +This behavior works well with the :ref:`version pragma `. From 5b0f5f01e1838f6f47a7271e03287bf570f4abd0 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Mon, 6 Oct 2025 12:26:44 -0300 Subject: [PATCH 038/243] bump evmone version in CI to 0.16.0 --- .circleci/config.yml | 12 ++++++------ .circleci/osx_install_dependencies.sh | 4 ++-- scripts/install_evmone.ps1 | 2 +- test/Common.h | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 48d86a97c4..0931fae8e8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,16 +13,16 @@ parameters: default: "solbuildpackpusher/solidity-buildpack-deps@sha256:1f387a77be889f65a2a25986a5c5eccc88cec23fabe6aeaf351790751145c81e" ubuntu-2404-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu2404-3 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:ef6a91d7f1434c67fb9e05c6136d80f71c0ad9198479e1a88e3437680993cda4" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu2404-6 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:c0412c53e59ce0c96bde4c08e7332ea12e4cadba9bbac829621947897fa21272" ubuntu-2404-clang-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu2404.clang-4 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:97fb2d1bc002b3624161f539a1d29543c0e6c6f4d9a61f611b9b60e99e18f377" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu2404.clang-7 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:7466ed23590cf14e60da884894723bcdab064742f017dcfcce015999b4f9143b" ubuntu-clang-ossfuzz-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu.clang.ossfuzz-10 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:bd55d9a3b13c88608709ec442188c414d30f4c49f23dc2ce8b76bf8c90603fc7" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu.clang.ossfuzz-12 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:c7088af5082bf764244a6251ecf3dd29d280d2cd2cd071f011f7f32e0326f426" emscripten-docker-image: type: string # NOTE: Please remember to update the `scripts/build_emscripten.sh` whenever the hash of this image changes. diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh index b449c4781b..dd5a8397b1 100755 --- a/.circleci/osx_install_dependencies.sh +++ b/.circleci/osx_install_dependencies.sh @@ -108,10 +108,10 @@ then rm -rf "$z3_dir" # evmone - evmone_version="0.13.0" + evmone_version="0.16.0" evmone_package="evmone-${evmone_version}-darwin-arm64.tar.gz" wget "https://github.com/ipsilon/evmone/releases/download/v${evmone_version}/${evmone_package}" - validate_checksum "$evmone_package" 49fe6cc35e0e13c48ca2f29a6b85a47f7b25dcd427e14254000d3bc29cddf2a6 + validate_checksum "$evmone_package" d26bcf7ada6c712b669ee70cbd8b534f80dadb6207fa15e15d1517d2b6823aa8 sudo tar xzpf "$evmone_package" -C /usr/local rm "$evmone_package" fi diff --git a/scripts/install_evmone.ps1 b/scripts/install_evmone.ps1 index 3e2a1f819f..9025d6957a 100644 --- a/scripts/install_evmone.ps1 +++ b/scripts/install_evmone.ps1 @@ -3,6 +3,6 @@ $ErrorActionPreference = "Stop" # Needed for Invoke-WebRequest to work via CI. $progressPreference = "silentlyContinue" -Invoke-WebRequest -URI "https://github.com/ipsilon/evmone/releases/download/v0.13.0/evmone-0.13.0-windows-amd64.zip" -OutFile "evmone.zip" +Invoke-WebRequest -URI "https://github.com/ipsilon/evmone/releases/download/v0.16.0/evmone-0.16.0-windows-amd64.zip" -OutFile "evmone.zip" tar -xf evmone.zip "bin/evmone.dll" mv bin/evmone.dll deps/ diff --git a/test/Common.h b/test/Common.h index a8a0a4e839..1ab7509b92 100644 --- a/test/Common.h +++ b/test/Common.h @@ -39,13 +39,13 @@ namespace solidity::test #ifdef _WIN32 static constexpr auto evmoneFilename = "evmone.dll"; -static constexpr auto evmoneDownloadLink = "https://github.com/ipsilon/evmone/releases/download/v0.13.0/evmone-0.13.0-windows-amd64.zip"; +static constexpr auto evmoneDownloadLink = "https://github.com/ipsilon/evmone/releases/download/v0.16.0/evmone-0.16.0-windows-amd64.zip"; #elif defined(__APPLE__) static constexpr auto evmoneFilename = "libevmone.dylib"; -static constexpr auto evmoneDownloadLink = "https://github.com/ipsilon/evmone/releases/download/v0.13.0/evmone-0.13.0-darwin-arm64.tar.gz"; +static constexpr auto evmoneDownloadLink = "https://github.com/ipsilon/evmone/releases/download/v0.16.0/evmone-0.16.0-darwin-arm64.tar.gz"; #else static constexpr auto evmoneFilename = "libevmone.so"; -static constexpr auto evmoneDownloadLink = "https://github.com/ipsilon/evmone/releases/download/v0.13.0/evmone-0.13.0-linux-x86_64.tar.gz"; +static constexpr auto evmoneDownloadLink = "https://github.com/ipsilon/evmone/releases/download/v0.16.0/evmone-0.16.0-linux-x86_64.tar.gz"; #endif struct ConfigException: public util::Exception {}; From b36da102110e5abd12a25eda581183b1da66af8f Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Wed, 15 Oct 2025 12:26:15 -0300 Subject: [PATCH 039/243] Remove EOF from soltest flags --- .circleci/soltest_all.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.circleci/soltest_all.sh b/.circleci/soltest_all.sh index 56aa1c3be6..86e62c333d 100755 --- a/.circleci/soltest_all.sh +++ b/.circleci/soltest_all.sh @@ -40,7 +40,8 @@ IFS=" " read -ra EVM_VALUES <<< "${1:-${DEFAULT_EVM_VALUES[@]}}" DEFAULT_EVM=prague OPTIMIZE_VALUES=(0 1) -EOF_VERSIONS=(0 1) +# TODO: EOF is marked as experimental in evmone. Reenable when proper handling for that is added here. +EOF_VERSIONS=(0) # Run for ABI encoder v1, without SMTChecker tests. EVM="${DEFAULT_EVM}" \ From b90f21e9328696db9316d6de7b64d8b5592f68b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 15 Oct 2025 13:25:28 +0200 Subject: [PATCH 040/243] Remove the unused install_cmake.sh script --- scripts/install_cmake.sh | 40 ---------------------------------------- 1 file changed, 40 deletions(-) delete mode 100755 scripts/install_cmake.sh diff --git a/scripts/install_cmake.sh b/scripts/install_cmake.sh deleted file mode 100755 index 0533de7234..0000000000 --- a/scripts/install_cmake.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env sh - -# This script downloads the CMake binary and installs it in ~/.local directory -# (the cmake executable will be in ~/.local/bin). -# This is mostly suitable for CIs, not end users. - -set -eu - -VERSION_MAJOR=3 -VERSION_MINOR=15 -VERSION_MICRO=2 -VERSION=$VERSION_MAJOR.$VERSION_MINOR.$VERSION_MICRO -PREFIX="/usr/local" - -OS=$(uname -s) -case $OS in -Linux) SHA256=f8cbec2abc433938bd9378b129d1d288bb33b8b5a277afe19644683af6e32a59;; -Darwin) SHA256=7ec056d641b8cbea98b220efdcc99da1991758a370063dcac3a0cd388d6b30b6;; -esac - -BIN=$PREFIX/bin - -PATH=$PREFIX/bin:$PATH - -if test -f $BIN/cmake && ($BIN/cmake --version | grep -q "$VERSION"); then - echo "CMake $VERSION already installed in $BIN" -else - FILE=cmake-$VERSION-$OS-x86_64.tar.gz - URL=https://cmake.org/files/v$VERSION_MAJOR.$VERSION_MINOR/$FILE - TMPFILE=$(mktemp --tmpdir "cmake-$VERSION-$OS-x86_64.XXXXXXXX.tar.gz") - echo "Downloading CMake ($URL)..." - wget "$URL" -O "$TMPFILE" -nv - if ! (shasum -a256 "$TMPFILE" | grep -q "$SHA256"); then - echo "Checksum mismatch ($TMPFILE)" - exit 1 - fi - mkdir -p "$PREFIX" - tar xzf "$TMPFILE" -C "$PREFIX" --strip 1 - rm "$TMPFILE" -fi From e4421c06ed1472d11275a7b1e92b7853b29069da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 15 Oct 2025 13:26:19 +0200 Subject: [PATCH 041/243] Rewrite build_win.ps1 in Bash --- .circleci/build_win.ps1 | 26 ----------------------- .circleci/config.yml | 3 +-- scripts/ci/build_win.sh | 46 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 28 deletions(-) delete mode 100644 .circleci/build_win.ps1 create mode 100755 scripts/ci/build_win.sh diff --git a/.circleci/build_win.ps1 b/.circleci/build_win.ps1 deleted file mode 100644 index e95caa7c1c..0000000000 --- a/.circleci/build_win.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -$ErrorActionPreference = "Stop" - -cd "$PSScriptRoot\.." - -if ("$Env:FORCE_RELEASE" -Or "$Env:CIRCLE_TAG") { - New-Item prerelease.txt -type file - Write-Host "Building release version." -} -else { - # Use last commit date rather than build date to avoid ending up with builds for - # different platforms having different version strings (and therefore producing different bytecode) - # if the CI is triggered just before midnight. - $last_commit_timestamp = git log -1 --date=unix --format=%cd HEAD - $last_commit_date = (Get-Date -Date "1970-01-01 00:00:00Z").toUniversalTime().addSeconds($last_commit_timestamp).ToString("yyyy.M.d") - -join("ci.", $last_commit_date) | out-file -encoding ascii prerelease.txt -} - -mkdir build -cd build -$boost_dir=(Resolve-Path $PSScriptRoot\..\deps\boost\lib\cmake\Boost-*) -..\deps\cmake\bin\cmake -G "Visual Studio 16 2019" -DBoost_DIR="$boost_dir\" -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded -DCMAKE_INSTALL_PREFIX="$PSScriptRoot\..\upload" .. -if ( -not $? ) { throw "CMake configure failed." } -msbuild solidity.sln /p:Configuration=Release /m:10 /v:minimal -if ( -not $? ) { throw "Build failed." } -..\deps\cmake\bin\cmake --build . -j 10 --target install --config Release -if ( -not $? ) { throw "Install target failed." } diff --git a/.circleci/config.yml b/.circleci/config.yml index 0931fae8e8..0b526ae70d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1640,8 +1640,7 @@ jobs: - .\deps - run: name: "Building solidity" - command: .circleci/build_win.ps1 - shell: powershell.exe + command: scripts/ci/build_win.sh - run: name: "Run solc.exe to make sure build was successful." command: .\build\solc\Release\solc.exe --version diff --git a/scripts/ci/build_win.sh b/scripts/ci/build_win.sh new file mode 100755 index 0000000000..647cb02d95 --- /dev/null +++ b/scripts/ci/build_win.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +set -euo pipefail + +(( $# <= 1 )) || { >&2 echo "Usage: $0 [PRERELEASE_SOURCE]"; exit 1; } +prerelease_source="${1:-ci}" +ROOTDIR="$(realpath "$(dirname "$0")/../../")" +FORCE_RELEASE="${FORCE_RELEASE:-}" +CIRCLE_TAG="${CIRCLE_TAG:-}" + +cd "$ROOTDIR" +if [[ $FORCE_RELEASE != "" || $CIRCLE_TAG != "" ]]; then + echo -n > prerelease.txt +else + # Use last commit date rather than build date to avoid ending up with builds for + # different platforms having different version strings (and therefore producing different bytecode) + # if the CI is triggered just before midnight. + # NOTE: The -local suffix makes git not use the timezone from the commit but instead convert to + # local one, which we explicitly set to UTC. + # NOTE: git --date is supposed to support the %-m/%-d format too, but it does not seem to + # work on Windows. Without it we get leading zeros for month and day. + last_commit_date=$(TZ=UTC git show --quiet --date="format-local:%Y-%m-%d" --format="%cd") + last_commit_date_stripped=$(date --date="$last_commit_date" "+%Y.%-m.%-d") + echo -n "${prerelease_source}.${last_commit_date_stripped}" > prerelease.txt +fi + +mkdir -p build/ +cd build/ + +# NOTE: Using an array to force Bash to do wildcard expansion +boost_dir=("${ROOTDIR}/deps/boost/lib/cmake/Boost-"*) + +"${ROOTDIR}/deps/cmake/bin/cmake" \ + -G "Visual Studio 16 2019" \ + -DBoost_DIR="${boost_dir[*]}" \ + -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded \ + -DCMAKE_INSTALL_PREFIX="${ROOTDIR}/uploads/" \ + .. +MSBuild.exe solidity.sln \ + -property:Configuration=Release \ + -maxCpuCount:10 \ + -verbosity:minimal +"${ROOTDIR}/deps/cmake/bin/cmake" \ + --build . \ + -j 10 \ + --target install \ + --config Release From 049980bfd1c9d99216b3d9853a1e66a9d0855342 Mon Sep 17 00:00:00 2001 From: Saw-mon & Natalie <3140080+Saw-mon-and-Natalie@users.noreply.github.com> Date: Wed, 16 Jul 2025 05:28:03 +0200 Subject: [PATCH 042/243] add CLZ to YUL build-in instruction for Osaka --- Changelog.md | 1 + docs/grammar/SolidityLexer.g4 | 2 +- docs/yul.rst | 6 +++-- libevmasm/Instruction.cpp | 2 ++ libevmasm/Instruction.h | 1 + libevmasm/SimplificationRule.h | 1 + liblangutil/EVMVersion.cpp | 2 ++ liblangutil/EVMVersion.h | 1 + libyul/AsmAnalysis.cpp | 2 ++ libyul/backends/evm/EVMDialect.cpp | 7 ++++++ scripts/test_antlr_grammar.sh | 2 ++ .../semanticTests/inlineAssembly/clz.sol | 25 +++++++++++++++++++ .../inlineAssembly/clz_pre_osaka.sol | 21 ++++++++++++++++ .../syntaxTests/inlineAssembly/clz.sol | 10 ++++++++ .../inlineAssembly/clz_pre_osaka.sol | 12 +++++++++ .../inlineAssembly/clz_reserved_osaka.sol | 14 +++++++++++ test/libyul/yulInterpreterTests/clz.yul | 15 +++++++++++ test/libyul/yulSyntaxTests/clz.yul | 16 ++++++++++++ test/libyul/yulSyntaxTests/clz_pre_osaka.yul | 14 +++++++++++ .../EVMInstructionInterpreter.cpp | 2 ++ 20 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/semanticTests/inlineAssembly/clz.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/clz_pre_osaka.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/clz.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/clz_pre_osaka.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/clz_reserved_osaka.sol create mode 100644 test/libyul/yulInterpreterTests/clz.yul create mode 100644 test/libyul/yulSyntaxTests/clz.yul create mode 100644 test/libyul/yulSyntaxTests/clz_pre_osaka.yul diff --git a/Changelog.md b/Changelog.md index 53039947bc..14752060a8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.8.31 (unreleased) Language Features: +* Yul: Introduce builtin ``clz(x)`` for counting leading zeoros. Compiler Features: * ethdebug: Experimental support for instructions and source locations under EOF. diff --git a/docs/grammar/SolidityLexer.g4 b/docs/grammar/SolidityLexer.g4 index ad3b040981..510b1f180a 100644 --- a/docs/grammar/SolidityLexer.g4 +++ b/docs/grammar/SolidityLexer.g4 @@ -300,7 +300,7 @@ YulHex: 'hex'; YulEVMBuiltin: 'stop' | 'add' | 'sub' | 'mul' | 'div' | 'sdiv' | 'mod' | 'smod' | 'exp' | 'not' | 'lt' | 'gt' | 'slt' | 'sgt' | 'eq' | 'iszero' | 'and' | 'or' | 'xor' | 'byte' - | 'shl' | 'shr' | 'sar' | 'addmod' | 'mulmod' | 'signextend' | 'keccak256' + | 'shl' | 'shr' | 'sar' | 'clz' | 'addmod' | 'mulmod' | 'signextend' | 'keccak256' | 'pop' | 'mload' | 'mstore' | 'mstore8' | 'sload' | 'sstore' | 'tload' | 'tstore'| 'msize' | 'gas' | 'address' | 'balance' | 'selfbalance' | 'caller' | 'callvalue' | 'calldataload' | 'calldatasize' | 'calldatacopy' | 'extcodesize' | 'extcodecopy' | 'returndatasize' diff --git a/docs/yul.rst b/docs/yul.rst index c81c9562a5..698d3de171 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -752,8 +752,8 @@ This document does not want to be a full description of the Ethereum virtual mac Please refer to a different document if you are interested in the precise semantics. Opcodes marked with ``-`` do not return a result and all others return exactly one value. -Opcodes marked with ``F``, ``H``, ``B``, ``C``, ``I``, ``L``, ``P`` and ``N`` are present since Frontier, -Homestead, Byzantium, Constantinople, Istanbul, London, Paris or Cancun respectively. +Opcodes marked with ``F``, ``H``, ``B``, ``C``, ``I``, ``L``, ``P``, ``N``, ``R`` and ``O`` are present since +Frontier, Homestead, Byzantium, Constantinople, Istanbul, London, Paris, Cancun, Prague or Osaka respectively. In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to but not including position ``b``, ``storage[p]`` signifies the storage contents at slot ``p``, and @@ -812,6 +812,8 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a +-------------------------+-----+---+-----------------------------------------------------------------+ | sar(x, y) | | C | signed arithmetic shift right y by x bits | +-------------------------+-----+---+-----------------------------------------------------------------+ +| clz(x) | | O | leading zeros of x in binary representation, 256 if x == 0 | ++-------------------------+-----+---+-----------------------------------------------------------------+ | addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetic, 0 if m == 0 | +-------------------------+-----+---+-----------------------------------------------------------------+ | mulmod(x, y, m) | | F | (x * y) % m with arbitrary precision arithmetic, 0 if m == 0 | diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index b556876357..e6648d5620 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -51,6 +51,7 @@ std::map> const solidity::evmasm::c_instru { "SHL", Instruction::SHL }, { "SHR", Instruction::SHR }, { "SAR", Instruction::SAR }, + { "CLZ", Instruction::CLZ }, { "ADDMOD", Instruction::ADDMOD }, { "MULMOD", Instruction::MULMOD }, { "SIGNEXTEND", Instruction::SIGNEXTEND }, @@ -219,6 +220,7 @@ static std::map const c_instructionInfo = {Instruction::SHL, {"SHL", 0, 2, 1, false, Tier::VeryLow}}, {Instruction::SHR, {"SHR", 0, 2, 1, false, Tier::VeryLow}}, {Instruction::SAR, {"SAR", 0, 2, 1, false, Tier::VeryLow}}, + {Instruction::CLZ, {"CLZ", 0, 1, 1, false, Tier::Low}}, {Instruction::ADDMOD, {"ADDMOD", 0, 3, 1, false, Tier::Mid}}, {Instruction::MULMOD, {"MULMOD", 0, 3, 1, false, Tier::Mid}}, {Instruction::SIGNEXTEND, {"SIGNEXTEND", 0, 2, 1, false, Tier::Low}}, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index cfdb85d2fa..d5b30f463c 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -61,6 +61,7 @@ enum class Instruction: uint8_t SHL, ///< bitwise SHL operation SHR, ///< bitwise SHR operation SAR, ///< bitwise SAR operation + CLZ, ///< count of leading zeros in binary representation KECCAK256 = 0x20, ///< compute KECCAK-256 hash diff --git a/libevmasm/SimplificationRule.h b/libevmasm/SimplificationRule.h index 6d34a50410..ea1baa9e53 100644 --- a/libevmasm/SimplificationRule.h +++ b/libevmasm/SimplificationRule.h @@ -98,6 +98,7 @@ struct EVMBuiltins static auto constexpr SHL = PatternGenerator{}; static auto constexpr SHR = PatternGenerator{}; static auto constexpr SAR = PatternGenerator{}; + static auto constexpr CLZ = PatternGenerator{}; static auto constexpr ADDMOD = PatternGenerator{}; static auto constexpr MULMOD = PatternGenerator{}; static auto constexpr SIGNEXTEND = PatternGenerator{}; diff --git a/liblangutil/EVMVersion.cpp b/liblangutil/EVMVersion.cpp index 4fbfe8de41..33f49ff912 100644 --- a/liblangutil/EVMVersion.cpp +++ b/liblangutil/EVMVersion.cpp @@ -42,6 +42,8 @@ bool EVMVersion::hasOpcode(Instruction _opcode, std::optional _eofVersi case Instruction::SHR: case Instruction::SAR: return hasBitwiseShifting(); + case Instruction::CLZ: + return hasCLZ(); case Instruction::CREATE2: return !_eofVersion.has_value() && hasCreate2(); case Instruction::EXTCODEHASH: diff --git a/liblangutil/EVMVersion.h b/liblangutil/EVMVersion.h index b5489de0ba..799fd2e0fa 100644 --- a/liblangutil/EVMVersion.h +++ b/liblangutil/EVMVersion.h @@ -133,6 +133,7 @@ class EVMVersion bool supportsReturndata() const { return *this >= byzantium(); } bool hasStaticCall() const { return *this >= byzantium(); } bool hasBitwiseShifting() const { return *this >= constantinople(); } + bool hasCLZ() const { return *this >= osaka(); } bool hasCreate2() const { return *this >= constantinople(); } bool hasExtCodeHash() const { return *this >= constantinople(); } bool hasChainID() const { return *this >= istanbul(); } diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index b36eb48b41..79d8bfd268 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -834,6 +834,8 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio errorForVM(7458_error, "only available for Constantinople-compatible"); else if (_instr == evmasm::Instruction::SAR && !m_evmVersion.hasBitwiseShifting()) errorForVM(2054_error, "only available for Constantinople-compatible"); + else if (_instr == evmasm::Instruction::CLZ && !m_evmVersion.hasCLZ()) + errorForVM(4948_error, "only available for Osaka-compatible"); else if (_instr == evmasm::Instruction::CREATE2 && !m_evmVersion.hasCreate2()) errorForVM(6166_error, "only available for Constantinople-compatible"); else if (_instr == evmasm::Instruction::EXTCODEHASH && !m_evmVersion.hasExtCodeHash()) diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index c903b9c4da..adb57f5774 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -132,6 +132,12 @@ std::set> createReservedIdentifiers(langutil::EVMVersio _evmVersion < langutil::EVMVersion::cancun() && (_instr == evmasm::Instruction::TSTORE || _instr == evmasm::Instruction::TLOAD); }; + // TODO remove this in 0.9.0. We allow creating functions or identifiers in Yul with the name + // clz for VMs before osaka. + auto clzException = [&](evmasm::Instruction _instr) -> bool + { + return _instr == evmasm::Instruction::CLZ && _evmVersion < langutil::EVMVersion::osaka(); + }; auto eofIdentifiersException = [&](evmasm::Instruction _instr) -> bool { @@ -154,6 +160,7 @@ std::set> createReservedIdentifiers(langutil::EVMVersio !blobBaseFeeException(instr.second) && !mcopyException(instr.second) && !transientStorageException(instr.second) && + !clzException(instr.second) && !eofIdentifiersException(instr.second) ) reserved.emplace(name); diff --git a/scripts/test_antlr_grammar.sh b/scripts/test_antlr_grammar.sh index 5256b81286..02e9a634f9 100755 --- a/scripts/test_antlr_grammar.sh +++ b/scripts/test_antlr_grammar.sh @@ -131,6 +131,8 @@ done < <( # Skipping a test with "let blobhash := ..." grep -v -E 'inlineAssembly/blobhash_pre_cancun.sol' | grep -v -E 'inlineAssembly/blobhash_pre_cancun_not_reserved.sol' | + # Skipping a test with "let clz := ..." + grep -v -E 'inlineAssembly/clz_pre_osaka.sol' | # Skipping tests with "let tstore/tload := ..." grep -v -E 'inlineAssembly/tload_tstore_not_reserved_before_cancun.sol' | # Skipping license error, unrelated to the grammar diff --git a/test/libsolidity/semanticTests/inlineAssembly/clz.sol b/test/libsolidity/semanticTests/inlineAssembly/clz.sol new file mode 100644 index 0000000000..ae4db69018 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/clz.sol @@ -0,0 +1,25 @@ +contract C { + function f() public view returns (bytes32 ret) { + assembly { + ret := clz(0) + } + } + + function g() public view returns (bytes32 ret) { + assembly { + ret := clz(1) + } + } + + function h() public view returns (bytes32 ret) { + assembly { + ret := clz(0x4000000000000000000000000000000000000000000000000000000000000000) + } + } +} +// ==== +// EVMVersion: >=osaka +// ---- +// f() -> 256 +// g() -> 255 +// h() -> 1 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/inlineAssembly/clz_pre_osaka.sol b/test/libsolidity/semanticTests/inlineAssembly/clz_pre_osaka.sol new file mode 100644 index 0000000000..ec01f7ccbb --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/clz_pre_osaka.sol @@ -0,0 +1,21 @@ +contract C { + function f() public pure returns (uint ret) { + assembly { + let clz := 1 + ret := clz + } + } + function g() public pure returns (uint ret) { + assembly { + function clz() -> r { + r := 1000 + } + ret := clz() + } + } +} +// ==== +// EVMVersion: 1 +// g() -> 1000 diff --git a/test/libsolidity/syntaxTests/inlineAssembly/clz.sol b/test/libsolidity/syntaxTests/inlineAssembly/clz.sol new file mode 100644 index 0000000000..720a010892 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/clz.sol @@ -0,0 +1,10 @@ +contract C { + function f(uint256 x) public pure returns (bytes32 ret) { + assembly { + ret := clz(x) + } + } +} +// ==== +// EVMVersion: >=osaka +// ---- diff --git a/test/libsolidity/syntaxTests/inlineAssembly/clz_pre_osaka.sol b/test/libsolidity/syntaxTests/inlineAssembly/clz_pre_osaka.sol new file mode 100644 index 0000000000..8fa3d543f1 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/clz_pre_osaka.sol @@ -0,0 +1,12 @@ +contract C { + function f(uint256 x) public pure returns (bytes32 ret) { + assembly { + ret := clz(x) + } + } +} +// ==== +// EVMVersion: =prague +// ---- +// TypeError 4948: (113-116): The "clz" instruction is only available for Osaka-compatible VMs (you are currently compiling for "prague"). +// DeclarationError 8678: (106-119): Variable count for assignment to "ret" does not match number of values (1 vs. 0) diff --git a/test/libsolidity/syntaxTests/inlineAssembly/clz_reserved_osaka.sol b/test/libsolidity/syntaxTests/inlineAssembly/clz_reserved_osaka.sol new file mode 100644 index 0000000000..697b48931d --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/clz_reserved_osaka.sol @@ -0,0 +1,14 @@ +contract C { + function f() public pure returns (uint ret) { + assembly { + function clz() -> r { + r := 1000 + } + ret := clz() + } + } +} +// ==== +// EVMVersion: >=osaka +// ---- +// ParserError 5568: (103-106): Cannot use builtin function name "clz" as identifier name. diff --git a/test/libyul/yulInterpreterTests/clz.yul b/test/libyul/yulInterpreterTests/clz.yul new file mode 100644 index 0000000000..eab0542d2a --- /dev/null +++ b/test/libyul/yulInterpreterTests/clz.yul @@ -0,0 +1,15 @@ +{ + sstore(0, clz(0)) + sstore(1, clz(1)) + sstore(2, clz(0xff)) +} +// ==== +// EVMVersion: >=osaka +// ---- +// Trace: +// Memory dump: +// Storage dump: +// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000100 +// 0000000000000000000000000000000000000000000000000000000000000001: 00000000000000000000000000000000000000000000000000000000000000ff +// 0000000000000000000000000000000000000000000000000000000000000002: 00000000000000000000000000000000000000000000000000000000000000f8 +// Transient storage dump: diff --git a/test/libyul/yulSyntaxTests/clz.yul b/test/libyul/yulSyntaxTests/clz.yul new file mode 100644 index 0000000000..ee183bc2bf --- /dev/null +++ b/test/libyul/yulSyntaxTests/clz.yul @@ -0,0 +1,16 @@ +{ + { + let clz := 1 + } + + { + function clz() {} + clz() + } +} + +// ==== +// EVMVersion: >=osaka +// ---- +// ParserError 5568: (20-23): Cannot use builtin function name "clz" as identifier name. +// ParserError 5568: (59-62): Cannot use builtin function name "clz" as identifier name. diff --git a/test/libyul/yulSyntaxTests/clz_pre_osaka.yul b/test/libyul/yulSyntaxTests/clz_pre_osaka.yul new file mode 100644 index 0000000000..e623fd771d --- /dev/null +++ b/test/libyul/yulSyntaxTests/clz_pre_osaka.yul @@ -0,0 +1,14 @@ +{ + { + let clz := 1 + } + + { + function clz() {} + clz() + } +} + +// ==== +// EVMVersion: Date: Thu, 24 Jul 2025 01:25:48 +0200 Subject: [PATCH 043/243] some minor adjustments for Changelog and Yul docs for CLZ --- Changelog.md | 2 +- docs/yul.rst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 14752060a8..748495074e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,7 +1,7 @@ ### 0.8.31 (unreleased) Language Features: -* Yul: Introduce builtin ``clz(x)`` for counting leading zeoros. +* Yul: Introduce builtin `clz(x)` for counting the number of leading zero bits in a 256-bit word. Compiler Features: * ethdebug: Experimental support for instructions and source locations under EOF. diff --git a/docs/yul.rst b/docs/yul.rst index 698d3de171..5c8086b001 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -752,8 +752,8 @@ This document does not want to be a full description of the Ethereum virtual mac Please refer to a different document if you are interested in the precise semantics. Opcodes marked with ``-`` do not return a result and all others return exactly one value. -Opcodes marked with ``F``, ``H``, ``B``, ``C``, ``I``, ``L``, ``P``, ``N``, ``R`` and ``O`` are present since -Frontier, Homestead, Byzantium, Constantinople, Istanbul, London, Paris, Cancun, Prague or Osaka respectively. +Opcodes marked with ``F``, ``H``, ``B``, ``C``, ``I``, ``L``, ``P``, ``N`` and ``O`` are present since +Frontier, Homestead, Byzantium, Constantinople, Istanbul, London, Paris, Cancun or Osaka respectively. In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to but not including position ``b``, ``storage[p]`` signifies the storage contents at slot ``p``, and @@ -812,7 +812,7 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a +-------------------------+-----+---+-----------------------------------------------------------------+ | sar(x, y) | | C | signed arithmetic shift right y by x bits | +-------------------------+-----+---+-----------------------------------------------------------------+ -| clz(x) | | O | leading zeros of x in binary representation, 256 if x == 0 | +| clz(x) | | O | number of leading zero bits of x, 256 if x == 0 | +-------------------------+-----+---+-----------------------------------------------------------------+ | addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetic, 0 if m == 0 | +-------------------------+-----+---+-----------------------------------------------------------------+ From 92429395bcf6326d9fbbdd53bfd2c945bb5a0420 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Thu, 16 Oct 2025 08:50:36 +0200 Subject: [PATCH 044/243] Yul: Implement CLZ exception in EVMDialect with `hasCLZ` function also resolve a whitespace issue in one test --- libyul/backends/evm/EVMDialect.cpp | 2 +- test/libyul/yulSyntaxTests/clz_pre_osaka.yul | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index adb57f5774..62f0d7a695 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -136,7 +136,7 @@ std::set> createReservedIdentifiers(langutil::EVMVersio // clz for VMs before osaka. auto clzException = [&](evmasm::Instruction _instr) -> bool { - return _instr == evmasm::Instruction::CLZ && _evmVersion < langutil::EVMVersion::osaka(); + return _instr == evmasm::Instruction::CLZ && !_evmVersion.hasCLZ(); }; auto eofIdentifiersException = [&](evmasm::Instruction _instr) -> bool diff --git a/test/libyul/yulSyntaxTests/clz_pre_osaka.yul b/test/libyul/yulSyntaxTests/clz_pre_osaka.yul index e623fd771d..7380965981 100644 --- a/test/libyul/yulSyntaxTests/clz_pre_osaka.yul +++ b/test/libyul/yulSyntaxTests/clz_pre_osaka.yul @@ -11,4 +11,4 @@ // ==== // EVMVersion: Date: Thu, 16 Oct 2025 11:53:26 +0200 Subject: [PATCH 045/243] Fix nested list indentation on the prerelease checklist --- ReleaseChecklist.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 1b038ee96b..727675fce5 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -127,13 +127,13 @@ At least a day before the release: ## Prerelease - [ ] Check that all tests on the latest commit on `develop` or `breaking` branch (whichever was chosen for the prerelease) are green. - [ ] Create a [release on GitHub](https://github.com/argotorg/solidity/releases/new). - - Set the target to the `develop` or `breaking` branch and the tag to the new version with a prerelease suffix, e.g. `v0.8.5-pre.6`. - Version matches the next release (`develop`) or the next breaking release (`breaking`). - The prerelease number in the suffix is 1-based, sequential, resets after a full release and is counted separately for `develop` and `breaking`. - - Include the following warning: `**The release is still in progress. You may see broken links and binaries may not yet be available from all sources.**`. - - Include the current, incomplete changelog. - - Check the `Set as a pre-release` box. - - Click the `Publish release` button on the release page, creating the tag. + - Set the target to the `develop` or `breaking` branch and the tag to the new version with a prerelease suffix, e.g. `v0.8.5-pre.6`. + Version matches the next release (`develop`) or the next breaking release (`breaking`). + The prerelease number in the suffix is 1-based, sequential, resets after a full release and is counted separately for `develop` and `breaking`. + - Include the following warning: `**The release is still in progress. You may see broken links and binaries may not yet be available from all sources.**`. + - Include the current, incomplete changelog. + - Check the `Set as a pre-release` box. + - Click the `Publish release` button on the release page, creating the tag. - [ ] Wait for the CI runs on the tag itself. - [ ] Switch to the tag that archives have to be created for. - [ ] Create the `prerelease.txt` file: `scripts/prerelease_suffix.sh pre "$(git describe --tags --exact-match)" > prerelease.txt`. From b53554f9dbe45a32fae9c7ada518beb3b8a3d2c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 10 Oct 2025 22:14:47 +0200 Subject: [PATCH 046/243] docs/conf.py: Fix wrong assumption that missing prerelease.txt represents a release --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 3f7dc3e350..74e45fa2a6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -126,7 +126,7 @@ def get_github_username_repo(url): with open('../CMakeLists.txt', 'r', encoding='utf8') as f: version = re.search('PROJECT_VERSION "([^"]+)"', f.read()).group(1) # The full version, including alpha/beta/rc tags. -if not os.path.isfile('../prerelease.txt') or os.path.getsize('../prerelease.txt') == 0: +if os.path.isfile('../prerelease.txt') and os.path.getsize('../prerelease.txt') == 0: release = version else: # This is a prerelease version From 6c068b3049f7351d22ca02ae401d68ab22c2d8ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 15 Oct 2025 19:29:10 +0200 Subject: [PATCH 047/243] docs/conf.py: Include the content of prerelease.txt in the version string --- docs/conf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 74e45fa2a6..e71535b9a7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -128,8 +128,10 @@ def get_github_username_repo(url): # The full version, including alpha/beta/rc tags. if os.path.isfile('../prerelease.txt') and os.path.getsize('../prerelease.txt') == 0: release = version +elif os.path.isfile('../prerelease.txt'): + with open('../prerelease.txt', 'r', encoding='utf8') as prerelease_file: + release = version + '-' + prerelease_file.read() else: - # This is a prerelease version release = version + '-develop' # The language for content autogenerated by Sphinx. Refer to documentation From 15964caad18dd06a41ddf039c330f0e60213870d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 10 Oct 2025 21:13:40 +0200 Subject: [PATCH 048/243] Remove special-casing of the `release` branch - I think we used to have such a branch in the past, but we no longer do and it being handled specially would be completely unexpected by everyone at this point. --- .circleci/config.yml | 2 +- scripts/ci/build.sh | 4 ++-- scripts/ci/build_emscripten.sh | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 58bcb2b9fb..bb8acb97b1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -187,7 +187,7 @@ commands: - run: name: Store commit hash and prerelease command: | - if [[ $CIRCLE_BRANCH == release || -n $CIRCLE_TAG ]]; then + if [[ -n $CIRCLE_TAG ]]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; diff --git a/scripts/ci/build.sh b/scripts/ci/build.sh index 1acec5a201..451638371f 100755 --- a/scripts/ci/build.sh +++ b/scripts/ci/build.sh @@ -9,7 +9,7 @@ prerelease_source="${1:-ci}" cd "${ROOTDIR}" -if [[ $CIRCLE_BRANCH == release || -n $CIRCLE_TAG || -n $FORCE_RELEASE ]]; then +if [[ -n $CIRCLE_TAG || -n $FORCE_RELEASE ]]; then echo -n > prerelease.txt else # Use last commit date rather than build date to avoid ending up with builds for @@ -25,7 +25,7 @@ fi mkdir -p build cd build -[[ -n $COVERAGE && $CIRCLE_BRANCH != release && -z $CIRCLE_TAG ]] && CMAKE_OPTIONS="$CMAKE_OPTIONS -DCOVERAGE=ON" +[[ -n $COVERAGE && -z $CIRCLE_TAG ]] && CMAKE_OPTIONS="$CMAKE_OPTIONS -DCOVERAGE=ON" # shellcheck disable=SC2086 cmake .. -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Release}" $CMAKE_OPTIONS diff --git a/scripts/ci/build_emscripten.sh b/scripts/ci/build_emscripten.sh index 836c4d41c5..71fc888ea3 100755 --- a/scripts/ci/build_emscripten.sh +++ b/scripts/ci/build_emscripten.sh @@ -45,7 +45,7 @@ function build() { cd "${ROOT_DIR}" - if [[ $CIRCLE_BRANCH == release || -n $CIRCLE_TAG || -n $FORCE_RELEASE || $(git tag --points-at HEAD 2> /dev/null) == v* ]]; then + if [[ -n $CIRCLE_TAG || -n $FORCE_RELEASE || $(git tag --points-at HEAD 2> /dev/null) == v* ]]; then echo -n > prerelease.txt else # Use last commit date rather than build date to avoid ending up with builds for From 5c21b7fad3a4348743c36a6433b01b776776a4a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 10 Oct 2025 19:19:49 +0200 Subject: [PATCH 049/243] Do not store CIRCLE_SHA1 in commit_hash.txt - It is not necessary, because when missing, our CMake config will fetch it from git. --- .circleci/config.yml | 9 ++++----- scripts/ci/build.sh | 4 ---- scripts/ci/build_emscripten.sh | 3 --- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index bb8acb97b1..fc267010a3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -182,17 +182,16 @@ commands: # -------------------------------------------------------------------------- # Build Commands - setup_prerelease_commit_hash: + setup_prerelease: steps: - run: - name: Store commit hash and prerelease + name: Store prerelease suffix command: | if [[ -n $CIRCLE_TAG ]]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi - echo -n "$CIRCLE_SHA1" > commit_hash.txt install_and_check_minimum_requirements: parameters: @@ -1115,7 +1114,7 @@ jobs: <<: *base_ubuntu_clang_large steps: - checkout - - setup_prerelease_commit_hash + - setup_prerelease - run_build_ossfuzz - persist_ossfuzz_executables_to_workspace - matrix_notify_failure_unless_pr @@ -1225,7 +1224,7 @@ jobs: <<: *base_ubuntu2404_small steps: - checkout - - setup_prerelease_commit_hash + - setup_prerelease - run: name: Install build system dependencies command: | diff --git a/scripts/ci/build.sh b/scripts/ci/build.sh index 451638371f..fae017d326 100755 --- a/scripts/ci/build.sh +++ b/scripts/ci/build.sh @@ -18,10 +18,6 @@ else TZ=UTC git show --quiet --date="format-local:%Y.%-m.%-d" --format="${prerelease_source}.%cd" > prerelease.txt fi -if [[ -n $CIRCLE_SHA1 ]]; then - echo -n "$CIRCLE_SHA1" > commit_hash.txt -fi - mkdir -p build cd build diff --git a/scripts/ci/build_emscripten.sh b/scripts/ci/build_emscripten.sh index 71fc888ea3..9da9f9bc59 100755 --- a/scripts/ci/build_emscripten.sh +++ b/scripts/ci/build_emscripten.sh @@ -53,9 +53,6 @@ function build() { # if the CI is triggered just before midnight. TZ=UTC git show --quiet --date="format-local:%Y.%-m.%-d" --format="${prerelease_source}.%cd" > prerelease.txt fi - if [[ -n $CIRCLE_SHA1 ]]; then - echo -n "$CIRCLE_SHA1" > commit_hash.txt - fi # Disable warnings for unqualified `move()` calls, introduced and enabled by # default in clang-16 which is what the emscripten docker image uses. From 2b950bb878b4accb08567d2296179a16c8411257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 10 Oct 2025 19:08:08 +0200 Subject: [PATCH 050/243] create_source_tarball.sh: Fail when commit_hash.txt is present instead of ignoring it --- scripts/create_source_tarball.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/create_source_tarball.sh b/scripts/create_source_tarball.sh index 1a30992453..af13899cdb 100755 --- a/scripts/create_source_tarball.sh +++ b/scripts/create_source_tarball.sh @@ -2,6 +2,9 @@ set -euo pipefail REPO_ROOT="$(dirname "$0")"/.. +# shellcheck source=scripts/common.sh +source "${REPO_ROOT}/scripts/common.sh" + cd "$REPO_ROOT" version=$(scripts/get_version.sh) commit_hash=$(git rev-parse --short=8 HEAD) @@ -14,6 +17,11 @@ else version_string="${version}-nightly-${commit_date}-${commit_hash}" fi +# The only purpose of commit_hash.txt is to make it possible to build the compiler without git. +# It is not meant as an override of the real hash. +[[ ! -e commit_hash.txt ]] || \ + fail "commit_hash.txt is present in the repository root, but will not be used to override the commit hash for the source package." + TEMPDIR=$(mktemp -d -t "solc-src-tarball-XXXXXX") SOLDIR="${TEMPDIR}/solidity_${version_string}/" mkdir "$SOLDIR" From a3874f09541b1a47ab354378a63d490e6ec7b1ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 10 Oct 2025 18:56:42 +0200 Subject: [PATCH 051/243] create_source_tarball.sh: Use prerelease name from prerelease.txt when available instead of hard-coding "nightly" --- scripts/create_source_tarball.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/create_source_tarball.sh b/scripts/create_source_tarball.sh index af13899cdb..28e41edf0a 100755 --- a/scripts/create_source_tarball.sh +++ b/scripts/create_source_tarball.sh @@ -13,6 +13,8 @@ commit_date=$(git show --format=%ci HEAD | head -n 1 | cut - -b1-10 | sed -e 's/ # File exists and has zero size -> not a prerelease if [[ -e prerelease.txt && ! -s prerelease.txt ]]; then version_string="$version" +elif [[ -e prerelease.txt ]]; then + version_string="${version}-$(cat prerelease.txt)-${commit_hash}" else version_string="${version}-nightly-${commit_date}-${commit_hash}" fi From af138bf2ce5934adae7316418ea3c29ed84bea1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 15 Oct 2025 18:51:12 +0200 Subject: [PATCH 052/243] create_source_tarball.sh: Use UTC timezone when determining the prerelease date --- scripts/create_source_tarball.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/create_source_tarball.sh b/scripts/create_source_tarball.sh index 28e41edf0a..c175b5045f 100755 --- a/scripts/create_source_tarball.sh +++ b/scripts/create_source_tarball.sh @@ -8,7 +8,7 @@ source "${REPO_ROOT}/scripts/common.sh" cd "$REPO_ROOT" version=$(scripts/get_version.sh) commit_hash=$(git rev-parse --short=8 HEAD) -commit_date=$(git show --format=%ci HEAD | head -n 1 | cut - -b1-10 | sed -e 's/-0?/./' | sed -e 's/-0?/./') +commit_date=$(TZ=UTC git show --quiet --date="format-local:%Y.%-m.%-d" --format="%cd") # File exists and has zero size -> not a prerelease if [[ -e prerelease.txt && ! -s prerelease.txt ]]; then From 8fe0df0761ef47c004488da0a00a30ce1fc86336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 10 Oct 2025 19:43:39 +0200 Subject: [PATCH 053/243] Unify the prerelease suffix logic across all scripts and CI --- .circleci/config.yml | 6 +----- scripts/build.sh | 6 ++++-- scripts/ci/build.sh | 9 +-------- scripts/ci/build_emscripten.sh | 9 +-------- scripts/ci/build_win.sh | 15 +-------------- scripts/prerelease_suffix.sh | 27 +++++++++++++++++++++++++++ 6 files changed, 35 insertions(+), 37 deletions(-) create mode 100755 scripts/prerelease_suffix.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index fc267010a3..e5f4d3cb3b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -187,11 +187,7 @@ commands: - run: name: Store prerelease suffix command: | - if [[ -n $CIRCLE_TAG ]]; then - echo -n > prerelease.txt; - else - date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; - fi + "scripts/prerelease_suffix.sh" nightly "$CIRCLE_TAG" > prerelease.txt install_and_check_minimum_requirements: parameters: diff --git a/scripts/build.sh b/scripts/build.sh index 5d7765e6b2..33d7bb7c51 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -10,8 +10,10 @@ else BUILD_TYPE="$1" fi -if [[ $(git tag --points-at HEAD 2> /dev/null) == v* ]]; then - touch "${ROOTDIR}/prerelease.txt" +# Intentionally not using prerelease_suffix.sh here. We do not want lingering prerelease.txt in +# dev builds, accidentally overriding version when someone runs the build manually. +if [[ $(git tag --points-at HEAD 2> /dev/null) =~ ^v[0-9.]+$ ]]; then + echo -n > prerelease.txt fi mkdir -p "$BUILDDIR" diff --git a/scripts/ci/build.sh b/scripts/ci/build.sh index fae017d326..b7f7e92f57 100755 --- a/scripts/ci/build.sh +++ b/scripts/ci/build.sh @@ -9,14 +9,7 @@ prerelease_source="${1:-ci}" cd "${ROOTDIR}" -if [[ -n $CIRCLE_TAG || -n $FORCE_RELEASE ]]; then - echo -n > prerelease.txt -else - # Use last commit date rather than build date to avoid ending up with builds for - # different platforms having different version strings (and therefore producing different bytecode) - # if the CI is triggered just before midnight. - TZ=UTC git show --quiet --date="format-local:%Y.%-m.%-d" --format="${prerelease_source}.%cd" > prerelease.txt -fi +"${ROOTDIR}/scripts/prerelease_suffix.sh" "$prerelease_source" "$CIRCLE_TAG" > prerelease.txt mkdir -p build cd build diff --git a/scripts/ci/build_emscripten.sh b/scripts/ci/build_emscripten.sh index 9da9f9bc59..50c851af84 100755 --- a/scripts/ci/build_emscripten.sh +++ b/scripts/ci/build_emscripten.sh @@ -45,14 +45,7 @@ function build() { cd "${ROOT_DIR}" - if [[ -n $CIRCLE_TAG || -n $FORCE_RELEASE || $(git tag --points-at HEAD 2> /dev/null) == v* ]]; then - echo -n > prerelease.txt - else - # Use last commit date rather than build date to avoid ending up with builds for - # different platforms having different version strings (and therefore producing different bytecode) - # if the CI is triggered just before midnight. - TZ=UTC git show --quiet --date="format-local:%Y.%-m.%-d" --format="${prerelease_source}.%cd" > prerelease.txt - fi + "${SCRIPT_DIR}/prerelease_suffix.sh" "$prerelease_source" "$(git tag --points-at HEAD 2> /dev/null)" > prerelease.txt # Disable warnings for unqualified `move()` calls, introduced and enabled by # default in clang-16 which is what the emscripten docker image uses. diff --git a/scripts/ci/build_win.sh b/scripts/ci/build_win.sh index 647cb02d95..64d63c58f4 100755 --- a/scripts/ci/build_win.sh +++ b/scripts/ci/build_win.sh @@ -8,20 +8,7 @@ FORCE_RELEASE="${FORCE_RELEASE:-}" CIRCLE_TAG="${CIRCLE_TAG:-}" cd "$ROOTDIR" -if [[ $FORCE_RELEASE != "" || $CIRCLE_TAG != "" ]]; then - echo -n > prerelease.txt -else - # Use last commit date rather than build date to avoid ending up with builds for - # different platforms having different version strings (and therefore producing different bytecode) - # if the CI is triggered just before midnight. - # NOTE: The -local suffix makes git not use the timezone from the commit but instead convert to - # local one, which we explicitly set to UTC. - # NOTE: git --date is supposed to support the %-m/%-d format too, but it does not seem to - # work on Windows. Without it we get leading zeros for month and day. - last_commit_date=$(TZ=UTC git show --quiet --date="format-local:%Y-%m-%d" --format="%cd") - last_commit_date_stripped=$(date --date="$last_commit_date" "+%Y.%-m.%-d") - echo -n "${prerelease_source}.${last_commit_date_stripped}" > prerelease.txt -fi +"${ROOTDIR}/scripts/prerelease_suffix.sh" "$prerelease_source" "$CIRCLE_TAG" > prerelease.txt mkdir -p build/ cd build/ diff --git a/scripts/prerelease_suffix.sh b/scripts/prerelease_suffix.sh new file mode 100755 index 0000000000..dc70f706e6 --- /dev/null +++ b/scripts/prerelease_suffix.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -euo pipefail + +(( $# <= 2 )) || { >&2 echo "Usage: $0 [PRERELEASE_SOURCE] [GIT_TAG]"; exit 1; } +prerelease_source="${1:-nightly}" +git_tag="${2:-}" +FORCE_RELEASE="${FORCE_RELEASE:-}" + +GNU_DATE="date" +if [[ "$OSTYPE" == "darwin"* ]]; then + GNU_DATE=gdate +fi + +if [[ $FORCE_RELEASE != "" || $git_tag == v* ]]; then + echo -n +else + # Use last commit date rather than build date to avoid ending up with builds for + # different platforms having different version strings (and therefore producing different bytecode) + # if the CI is triggered just before midnight. + # NOTE: The -local suffix makes git not use the timezone from the commit but instead convert to + # local one, which we explicitly set to UTC. + # NOTE: git --date is supposed to support the %-m/%-d format too, but it does not seem to + # work on Windows. Without it we get leading zeros for month and day. + last_commit_date=$(TZ=UTC git show --quiet --date="format-local:%Y-%m-%d" --format="%cd") + last_commit_date_stripped=$("$GNU_DATE" --date "$last_commit_date" "+%Y.%-m.%-d") + echo -n "${prerelease_source}.${last_commit_date_stripped}" +fi From 8439e2abdb471bb7d19e98c06226cd25af002cf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 10 Oct 2025 22:50:47 +0200 Subject: [PATCH 054/243] Handle tagged prereleases separately from releases and other prereleases --- .circleci/config.yml | 1 + scripts/create_source_tarball.sh | 20 ++++++++++++++------ scripts/prerelease_suffix.sh | 4 +++- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e5f4d3cb3b..5d3db526ad 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1001,6 +1001,7 @@ jobs: <<: *base_ubuntu2404_xlarge steps: - build + - run: cat prerelease.txt # x64 ASAN build, for testing for memory related bugs b_ubu_asan: &b_ubu_asan diff --git a/scripts/create_source_tarball.sh b/scripts/create_source_tarball.sh index c175b5045f..0e135ad824 100755 --- a/scripts/create_source_tarball.sh +++ b/scripts/create_source_tarball.sh @@ -8,14 +8,22 @@ source "${REPO_ROOT}/scripts/common.sh" cd "$REPO_ROOT" version=$(scripts/get_version.sh) commit_hash=$(git rev-parse --short=8 HEAD) -commit_date=$(TZ=UTC git show --quiet --date="format-local:%Y.%-m.%-d" --format="%cd") -# File exists and has zero size -> not a prerelease -if [[ -e prerelease.txt && ! -s prerelease.txt ]]; then - version_string="$version" -elif [[ -e prerelease.txt ]]; then - version_string="${version}-$(cat prerelease.txt)-${commit_hash}" +if [[ -e prerelease.txt ]]; then + prerelease_suffix=$(cat prerelease.txt) + if [[ $prerelease_suffix == "" ]]; then + # File exists and has zero size -> not a prerelease + version_string="$version" + elif [[ $prerelease_suffix == pre.* ]]; then + # Tagged prerelease -> unambiguous, so commit hash not needed + version_string="${version}-${prerelease_suffix}" + else + # Nightly/develop/other prerelease -> include commit hash + version_string="${version}-${prerelease_suffix}-${commit_hash}" + fi else + # Nightly/develop/other prerelease -> include commit hash + default prerelease suffix + commit_date=$(TZ=UTC git show --quiet --date="format-local:%Y.%-m.%-d" --format="%cd") version_string="${version}-nightly-${commit_date}-${commit_hash}" fi diff --git a/scripts/prerelease_suffix.sh b/scripts/prerelease_suffix.sh index dc70f706e6..2913b12fb4 100755 --- a/scripts/prerelease_suffix.sh +++ b/scripts/prerelease_suffix.sh @@ -11,8 +11,10 @@ if [[ "$OSTYPE" == "darwin"* ]]; then GNU_DATE=gdate fi -if [[ $FORCE_RELEASE != "" || $git_tag == v* ]]; then +if [[ $FORCE_RELEASE != "" || $git_tag =~ ^v[0-9.]+$ ]]; then echo -n +elif [[ $git_tag =~ ^v[0-9.]+-pre. ]]; then + echo -n "pre.${git_tag#*-pre.}" else # Use last commit date rather than build date to avoid ending up with builds for # different platforms having different version strings (and therefore producing different bytecode) From 5667f96aee8b501abf9223ec1963540b9bde6066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 16 Oct 2025 17:05:52 +0200 Subject: [PATCH 055/243] bytecode_reports_for_modified_binaries.sh: Do not try to iterate over platform binaries when nothing was modified --- scripts/solc-bin/bytecode_reports_for_modified_binaries.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/solc-bin/bytecode_reports_for_modified_binaries.sh b/scripts/solc-bin/bytecode_reports_for_modified_binaries.sh index 50a7f53dd8..39b45a6e64 100755 --- a/scripts/solc-bin/bytecode_reports_for_modified_binaries.sh +++ b/scripts/solc-bin/bytecode_reports_for_modified_binaries.sh @@ -124,7 +124,12 @@ modified_release_versions=$( uniq ) echo "Release versions modified in the commit range:" -echo "$modified_release_versions" +if [[ $modified_release_versions != "" ]]; then + echo "$modified_release_versions" +else + echo "No modified binaries found." + exit 0 +fi # NOTE: We want perform the check when the soljson-* files in bin/ and wasm/ are modified too # because in that case the symlinks in emscripten-wasm32/ and emscripten-asmjs/ might remain From 4a772b6492f258bc59a8686299234196bd66d65e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 16 Oct 2025 17:39:41 +0200 Subject: [PATCH 056/243] create_source_tarball.sh: Copy prerelease.txt also when it's non-empty --- scripts/create_source_tarball.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/create_source_tarball.sh b/scripts/create_source_tarball.sh index 0e135ad824..9ccbec2223 100755 --- a/scripts/create_source_tarball.sh +++ b/scripts/create_source_tarball.sh @@ -45,7 +45,7 @@ SOLDIR="$SOLDIR" git submodule foreach 'git checkout-index --all --prefix="${SOL # Include the commit hash and prerelease suffix in the tarball echo "$commit_hash" > "${SOLDIR}/commit_hash.txt" -[[ -e prerelease.txt && ! -s prerelease.txt ]] && cp prerelease.txt "${SOLDIR}/" +[[ -e prerelease.txt ]] && cp prerelease.txt "${SOLDIR}/" mkdir -p "$REPO_ROOT/upload" tar \ From 439ae46cb42ec1883f188949b48ee4135959c1ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 16 Oct 2025 12:39:41 +0200 Subject: [PATCH 057/243] Build the source tarball in CI --- .circleci/config.yml | 12 ++++++++++++ ReleaseChecklist.md | 10 ++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5d3db526ad..6b596f2b17 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1842,6 +1842,17 @@ jobs: - matrix_notify_failure_unless_pr - matrix_notify_release_unless_pr + c_source_tarball: + <<: *base_ubuntu2404_small + steps: + - checkout + - setup_prerelease + - run: scripts/create_source_tarball.sh + - store_artifacts: + path: upload/ + - matrix_notify_failure_unless_pr + - matrix_notify_release_unless_pr + b_alpine_docker: <<: *base_cimg_small steps: @@ -2017,6 +2028,7 @@ workflows: - b_osx - b_win - b_ems + - c_source_tarball: *requires_nothing nightly: diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 727675fce5..fe751e896f 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -67,10 +67,7 @@ At least a day before the release: - [ ] Wait for the CI runs on the tag itself. ### Upload Release Artifacts and Publish Binaries -- [ ] Switch to the tag that archives have to be created for. -- [ ] Create the `prerelease.txt` file: `echo -n > prerelease.txt`. -- [ ] Run `scripts/create_source_tarball.sh` to create the source tarball. This will create the tarball in a directory called `upload`. -- [ ] Take the tarball from the upload directory (its name should be `solidity_x.x.x.tar.gz`, otherwise `prerelease.txt` was missing in the step before) and upload it to the release page. +- [ ] Take the source tarball (`solidity_x.x.x.tar.gz`) from `c_source_tarball` run of the tagged commit on Circle CI and upload it to the release page. - [ ] Take the `github-binaries.tar` tarball from `c_release_binaries` run of the tagged commit on Circle CI and add all binaries from it to the release page. Make sure it contains four binaries: `solc-windows.exe`, `solc-macos`, `solc-static-linux` and `soljson.js`. - [ ] Take the `solc-bin-binaries.tar` tarball from `c_release_binaries` run of the tagged commit on Circle CI and add all binaries from it to solc-bin. @@ -135,10 +132,7 @@ At least a day before the release: - Check the `Set as a pre-release` box. - Click the `Publish release` button on the release page, creating the tag. - [ ] Wait for the CI runs on the tag itself. -- [ ] Switch to the tag that archives have to be created for. -- [ ] Create the `prerelease.txt` file: `scripts/prerelease_suffix.sh pre "$(git describe --tags --exact-match)" > prerelease.txt`. -- [ ] Run `scripts/create_source_tarball.sh` to create the source tarball. This will create the tarball in a directory called `upload`. -- [ ] Take the tarball from the upload directory (its name should be `solidity_x.x.x-pre.N.tar.gz`, if `prerelease.txt` was created correctly) and upload it to the release page. +- [ ] Take the source tarball (`solidity_x.x.x-pre.N.tar.gz`) from `c_source_tarball` run of the tagged commit on Circle CI and upload it to the release page. - [ ] Take the `github-binaries.tar` tarball from `c_release_binaries` run of the tagged commit on Circle CI and add all binaries from it to the release page. Make sure it contains four binaries: `solc-windows.exe`, `solc-macos`, `solc-static-linux` and `soljson.js`. - [ ] Take the `solc-bin-binaries.tar` tarball from `c_release_binaries` run of the tagged commit on Circle CI and add all binaries from it to solc-bin. From d74f97be00e32720c1e37fb74b93652991b58b40 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Thu, 16 Oct 2025 16:51:21 +0200 Subject: [PATCH 058/243] Bump emscripten docker image --- scripts/build_emscripten.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build_emscripten.sh b/scripts/build_emscripten.sh index 93556a557e..a723752ec8 100755 --- a/scripts/build_emscripten.sh +++ b/scripts/build_emscripten.sh @@ -33,9 +33,9 @@ if (( $# != 0 )); then params="$(printf "%q " "${@}")" fi -# solbuildpackpusher/solidity-buildpack-deps:emscripten-20` +# solbuildpackpusher/solidity-buildpack-deps:emscripten-21 # NOTE: Without `safe.directory` git would assume it's not safe to operate on /root/project since it's owned by a different user. # See https://github.blog/2022-04-12-git-security-vulnerability-announced/ docker run -v "$(pwd):/root/project" -w /root/project \ - solbuildpackpusher/solidity-buildpack-deps@sha256:98f963ed799a0d206ef8e7b5475f847e0dea53b7fdea9618bbc6106a62730bd2 \ + solbuildpackpusher/solidity-buildpack-deps@sha256:fc53d68a4680ffa7d5f70164e13a903478964f15bcc07434d74833a05f4fbc19 \ /bin/bash -c "git config --global --add safe.directory /root/project && ./scripts/ci/build_emscripten.sh ${params}" From 8615ed7bef1231b465f8a176e9b50704fedb3c12 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Wed, 15 Oct 2025 12:51:50 +0200 Subject: [PATCH 059/243] Migrate from dockerhub to ghcr.io --- .circleci/config.yml | 2 +- .github/workflows/buildpack-deps.yml | 12 ++++++++---- scripts/Dockerfile | 8 ++++---- scripts/Dockerfile_alpine | 3 ++- scripts/docker_deploy_manual.sh | 8 ++++++-- 5 files changed, 21 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6b596f2b17..0810052256 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1866,7 +1866,7 @@ jobs: command: scripts/docker_deploy_manual.sh develop "file://$PWD" --no-push - run: name: Smoke test - command: docker run --pull=never ethereum/solc:build-alpine --version + command: docker run --pull=never ghcr.io/argotorg/solc:build-alpine --version - matrix_notify_failure_unless_pr workflows: diff --git a/.github/workflows/buildpack-deps.yml b/.github/workflows/buildpack-deps.yml index c4c84dae16..d4e7c5a7e8 100644 --- a/.github/workflows/buildpack-deps.yml +++ b/.github/workflows/buildpack-deps.yml @@ -16,8 +16,7 @@ jobs: buildpack-deps: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} - DOCKER_REPOSITORY: solbuildpackpusher/solidity-buildpack-deps + DOCKER_REPOSITORY: ghcr.io/argotorg/solidity-buildpack-deps IMAGE_NAME: buildpack-deps runs-on: ubuntu-latest @@ -32,11 +31,16 @@ jobs: with: fetch-depth: 0 + - name: Log in to GitHub Container Registry + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Upgrade ${{ env.IMAGE_NAME }}-${{ matrix.image_variant }} run: | - echo ${DOCKERHUB_TOKEN} | docker login -u solbuildpackpusher --password-stdin scripts/ci/docker_upgrade.sh ${{ env.IMAGE_NAME }} ${{ matrix.image_variant }} ${{ env.DOCKER_REPOSITORY }} - docker logout - name: comment PR if: "env.DOCKER_IMAGE" diff --git a/scripts/Dockerfile b/scripts/Dockerfile index 6df9337ec8..fa9f65b6f4 100644 --- a/scripts/Dockerfile +++ b/scripts/Dockerfile @@ -1,6 +1,4 @@ FROM alpine AS build -MAINTAINER chriseth -#Official solidity docker image #Establish working directory as solidity WORKDIR /solidity @@ -8,8 +6,8 @@ WORKDIR /solidity # Build dependencies RUN apk update && apk add boost-dev boost-static build-base cmake git clang -#Copy working directory on travis to the image -COPY / $WORKDIR +#Copy working directory to the image +COPY . /solidity # Number of parallel jobs during build # or 0 for auto-computing (max(1, CPU_core_count * 2/3), a greedy value) @@ -34,5 +32,7 @@ RUN make solc \ RUN strip solc/solc FROM scratch +LABEL org.opencontainers.image.source=https://github.com/argotorg/solidity +LABEL maintainer="Solidity Team " COPY --from=build /solidity/solc/solc /usr/bin/solc ENTRYPOINT ["/usr/bin/solc"] diff --git a/scripts/Dockerfile_alpine b/scripts/Dockerfile_alpine index 21bba45667..8db45d1024 100644 --- a/scripts/Dockerfile_alpine +++ b/scripts/Dockerfile_alpine @@ -1,5 +1,6 @@ FROM alpine -MAINTAINER chriseth +LABEL org.opencontainers.image.source=https://github.com/argotorg/solidity +LABEL maintainer="Solidity Team " COPY upload/solc-static-linux /usr/local/bin/solc ENTRYPOINT ["/usr/local/bin/solc"] diff --git a/scripts/docker_deploy_manual.sh b/scripts/docker_deploy_manual.sh index 196a3b36c3..c17119ce08 100755 --- a/scripts/docker_deploy_manual.sh +++ b/scripts/docker_deploy_manual.sh @@ -6,7 +6,7 @@ REPO_ROOT="$(dirname "$0")/.." # shellcheck source=scripts/common.sh source "${REPO_ROOT}/scripts/common.sh" -image="ethereum/solc" +image="ghcr.io/argotorg/solc" if (( $# < 1 || $# > 3 )); then fail "Usage: $0 [repo URL] [--no-push]" @@ -22,7 +22,11 @@ else publish=true fi -#docker login +# NOTE: Login to GHCR before running this script with a PAT: +# echo $GHCR_TOKEN | docker login ghcr.io -u USERNAME --password-stdin +# +# Create a classic PAT with write:packages scope only visiting the following url: +# https://github.com/settings/tokens/new?scopes=write:packages DIR=$(mktemp -d) ( From 93871f56c09ee80f013c3bbff67c2c3229292947 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Thu, 16 Oct 2025 13:52:51 +0200 Subject: [PATCH 060/243] Change docker registry on CI --- .circleci/config.yml | 20 ++++++++++---------- scripts/build_emscripten.sh | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0810052256..76f349a42d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,25 +9,25 @@ version: 2.1 parameters: ubuntu-2004-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-26 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:1f387a77be889f65a2a25986a5c5eccc88cec23fabe6aeaf351790751145c81e" + # ghcr.io/argotorg/solidity-buildpack-deps:ubuntu2004-26 + default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:1f387a77be889f65a2a25986a5c5eccc88cec23fabe6aeaf351790751145c81e" ubuntu-2404-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu2404-6 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:c0412c53e59ce0c96bde4c08e7332ea12e4cadba9bbac829621947897fa21272" + # ghcr.io/argotorg/solidity-buildpack-deps:ubuntu2404-6 + default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:c0412c53e59ce0c96bde4c08e7332ea12e4cadba9bbac829621947897fa21272" ubuntu-2404-clang-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu2404.clang-7 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:7466ed23590cf14e60da884894723bcdab064742f017dcfcce015999b4f9143b" + # ghcr.io/argotorg/solidity-buildpack-deps:ubuntu2404.clang-7 + default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:7466ed23590cf14e60da884894723bcdab064742f017dcfcce015999b4f9143b" ubuntu-clang-ossfuzz-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu.clang.ossfuzz-12 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:c7088af5082bf764244a6251ecf3dd29d280d2cd2cd071f011f7f32e0326f426" + # ghcr.io/argotorg/solidity-buildpack-deps:ubuntu.clang.ossfuzz-12 + default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:c7088af5082bf764244a6251ecf3dd29d280d2cd2cd071f011f7f32e0326f426" emscripten-docker-image: type: string # NOTE: Please remember to update the `scripts/build_emscripten.sh` whenever the hash of this image changes. - # solbuildpackpusher/solidity-buildpack-deps:emscripten-21 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:fc53d68a4680ffa7d5f70164e13a903478964f15bcc07434d74833a05f4fbc19" + # ghcr.io/argotorg/solidity-buildpack-deps:emscripten-21 + default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:fc53d68a4680ffa7d5f70164e13a903478964f15bcc07434d74833a05f4fbc19" evm-version: type: string default: prague diff --git a/scripts/build_emscripten.sh b/scripts/build_emscripten.sh index a723752ec8..aae4b5c1f3 100755 --- a/scripts/build_emscripten.sh +++ b/scripts/build_emscripten.sh @@ -33,9 +33,9 @@ if (( $# != 0 )); then params="$(printf "%q " "${@}")" fi -# solbuildpackpusher/solidity-buildpack-deps:emscripten-21 +# ghcr.io/argotorg/solidity-buildpack-deps:emscripten-21 # NOTE: Without `safe.directory` git would assume it's not safe to operate on /root/project since it's owned by a different user. # See https://github.blog/2022-04-12-git-security-vulnerability-announced/ docker run -v "$(pwd):/root/project" -w /root/project \ - solbuildpackpusher/solidity-buildpack-deps@sha256:fc53d68a4680ffa7d5f70164e13a903478964f15bcc07434d74833a05f4fbc19 \ + ghcr.io/argotorg/solidity-buildpack-deps@sha256:fc53d68a4680ffa7d5f70164e13a903478964f15bcc07434d74833a05f4fbc19 \ /bin/bash -c "git config --global --add safe.directory /root/project && ./scripts/ci/build_emscripten.sh ${params}" From 10c874957b30867417f9113985a29a9c1b00d8fe Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Mon, 20 Oct 2025 14:06:54 +0200 Subject: [PATCH 061/243] Update the Docker registry in the Solidity documentation --- docs/installing-solidity.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index c9286952ea..6f3dfbb7fb 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -71,7 +71,7 @@ Please refer to the solc-js repository for instructions. Docker ====== -Docker images of Solidity builds are available using the ``solc`` image from the ``ethereum`` organization. +Docker images of Solidity builds are available using the `solc `_ image from the argotorg organization on ghcr.io. Use the ``stable`` tag for the latest released version, and ``nightly`` for potentially unstable changes in the ``develop`` branch. The Docker image runs the compiler executable so that you can pass all compiler arguments to it. @@ -80,11 +80,11 @@ and runs it in a new container, passing the ``--help`` argument. .. code-block:: bash - docker run ethereum/solc:stable --help + docker run ghcr.io/argotorg/solc:stable --help .. note:: - Specific compiler versions are supported as the Docker image tag such as ``ethereum/solc:0.8.23``. + Specific compiler versions are supported as the Docker image tag such as ``ghcr.io/argotorg/solc:0.8.23``. We will be passing the ``stable`` tag here instead of specific version tag to ensure that users get the latest version by default and avoid the issue of an out-of-date version. @@ -95,7 +95,7 @@ local folder for input and output, and specify the contract to compile. For exam docker run \ --volume "/tmp/some/local/path/:/sources/" \ - ethereum/solc:stable \ + ghcr.io/argotorg/solc:stable \ /sources/Contract.sol \ --abi \ --bin \ @@ -108,7 +108,7 @@ self-contained (i.e. it does not refer to any external files that would have to .. code-block:: bash - docker run ethereum/solc:stable --standard-json < input.json > output.json + docker run ghcr.io/argotorg/solc:stable --standard-json < input.json > output.json Linux Packages ============== From 2086874cc85224756f2724d92fa1c7644f45fded Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Wed, 22 Oct 2025 10:04:49 +0200 Subject: [PATCH 062/243] EVM instruction interpreter: record linker symbols --- .../EVMInstructionInterpreter.cpp | 21 ++++++++++++++----- .../EVMInstructionInterpreter.h | 4 ++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index c3ce0ed235..cec5d55013 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -534,7 +534,8 @@ u256 EVMInstructionInterpreter::evalBuiltin( return u256(keccak256(arg)) & 0xfff; } } - else if (fun == "datacopy") + + if (fun == "datacopy") { // This is identical to codecopy. if ( @@ -550,11 +551,21 @@ u256 EVMInstructionInterpreter::evalBuiltin( ); return 0; } - else if (fun == "memoryguard") + + if (fun == "memoryguard") return _evaluatedArguments.at(0); - else - yulAssert(false, "Unknown builtin: " + fun); - return 0; + + if (fun == "linkersymbol") + { + yulAssert(_arguments.size() == 1); + yulAssert(std::holds_alternative(_arguments[0])); + std::string const placeholder = formatLiteral(std::get(_arguments[0])); + h256 const identifier(keccak256(placeholder)); + m_linkerSymbols.emplace(identifier, placeholder); + return 0; + } + + yulAssert(false, "Unknown builtin: " + fun); } diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.h b/test/tools/yulInterpreter/EVMInstructionInterpreter.h index d3d2211129..dac72ce6ae 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.h +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.h @@ -168,6 +168,10 @@ class EVMInstructionInterpreter InterpreterState& m_state; /// Flag to disable trace of instructions that write to memory. bool m_disableMemoryWriteInstructions; + /// mapping from linker identifier (hash of literal) to original string representation, populated by linkersymbol + /// calls + std::map m_linkerSymbols; + public: /// Maximum length for range-based memory access operations. static constexpr unsigned s_maxRangeSize = 0xffff; From 343373b06d40805f6a14f4bbc5fe2cfab959c08e Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 20 Oct 2025 14:15:48 +0200 Subject: [PATCH 063/243] Make disjoint set datastructure templatized over value type with explicit instantiation --- libsolutil/DisjointSet.cpp | 26 ++++++++++++++++++-------- libsolutil/DisjointSet.h | 3 ++- test/libsolutil/DisjointSet.cpp | 12 ++++++------ 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/libsolutil/DisjointSet.cpp b/libsolutil/DisjointSet.cpp index 43d3bfa188..ef15ea4b56 100644 --- a/libsolutil/DisjointSet.cpp +++ b/libsolutil/DisjointSet.cpp @@ -24,7 +24,8 @@ using namespace solidity::util; -ContiguousDisjointSet::ContiguousDisjointSet(size_t const _numNodes): +template +ContiguousDisjointSet::ContiguousDisjointSet(size_t const _numNodes): m_parents(_numNodes), m_neighbors(_numNodes), m_sizes(_numNodes, static_cast(1)), @@ -35,9 +36,11 @@ ContiguousDisjointSet::ContiguousDisjointSet(size_t const _numNodes): std::iota(m_neighbors.begin(), m_neighbors.end(), 0); } -size_t ContiguousDisjointSet::numSets() const { return m_numSets; } +template +size_t ContiguousDisjointSet::numSets() const { return m_numSets; } -ContiguousDisjointSet::value_type ContiguousDisjointSet::find(value_type const _element) const +template +typename ContiguousDisjointSet::value_type ContiguousDisjointSet::find(value_type const _element) const { solAssert(_element < m_parents.size()); // path halving @@ -50,7 +53,8 @@ ContiguousDisjointSet::value_type ContiguousDisjointSet::find(value_type const _ return rootElement; } -void ContiguousDisjointSet::merge(value_type const _x, value_type const _y, bool const _mergeBySize) +template +void ContiguousDisjointSet::merge(value_type const _x, value_type const _y, bool const _mergeBySize) { auto xRoot = find(_x); auto yRoot = find(_y); @@ -69,17 +73,20 @@ void ContiguousDisjointSet::merge(value_type const _x, value_type const _y, bool --m_numSets; } -bool ContiguousDisjointSet::sameSubset(value_type const _x, value_type const _y) const +template +bool ContiguousDisjointSet::sameSubset(value_type const _x, value_type const _y) const { return find(_x) == find(_y); } -ContiguousDisjointSet::size_type ContiguousDisjointSet::sizeOfSubset(value_type const _x) const +template +typename ContiguousDisjointSet::size_type ContiguousDisjointSet::sizeOfSubset(value_type const _x) const { return m_sizes[find(_x)]; } -std::set ContiguousDisjointSet::subset(value_type const _x) const +template +std::set::value_type> ContiguousDisjointSet::subset(value_type const _x) const { solAssert(_x < m_parents.size()); std::set result{_x}; @@ -92,7 +99,8 @@ std::set ContiguousDisjointSet::subset(value_ return result; } -std::vector> ContiguousDisjointSet::subsets() const +template +std::vector::value_type>> ContiguousDisjointSet::subsets() const { std::vector> result; std::vector visited(m_parents.size(), false); @@ -107,3 +115,5 @@ std::vector> ContiguousDisjointSet:: } return result; } + +template class solidity::util::ContiguousDisjointSet; diff --git a/libsolutil/DisjointSet.h b/libsolutil/DisjointSet.h index 02792cbe69..0d5b048cb0 100644 --- a/libsolutil/DisjointSet.h +++ b/libsolutil/DisjointSet.h @@ -31,11 +31,12 @@ namespace solidity::util /// [1] https://en.wikipedia.org/wiki/Disjoint-set_data_structure /// [2] Tarjan, Robert E., and Jan Van Leeuwen. "Worst-case analysis of set union algorithms." /// Journal of the ACM (JACM) 31.2 (1984): 245-281. +template class ContiguousDisjointSet { public: using size_type = size_t; - using value_type = size_t; + using value_type = ValueType; /// Constructs a new disjoint set datastructure with `_numNodes` elements and each element in its own individual set explicit ContiguousDisjointSet(size_t _numNodes); diff --git a/test/libsolutil/DisjointSet.cpp b/test/libsolutil/DisjointSet.cpp index 338138b6ae..8fcc994bdd 100644 --- a/test/libsolutil/DisjointSet.cpp +++ b/test/libsolutil/DisjointSet.cpp @@ -29,8 +29,8 @@ BOOST_AUTO_TEST_SUITE(DisjointSetTest) BOOST_AUTO_TEST_CASE(full_union) { - ContiguousDisjointSet ds(10); - for (size_t i = 1; i < 10; ++i) + ContiguousDisjointSet ds(10); + for (std::uint32_t i = 1; i < 10; ++i) { BOOST_CHECK(!ds.sameSubset(0, i)); ds.merge(0, i); @@ -42,16 +42,16 @@ BOOST_AUTO_TEST_CASE(full_union) std::set fullSet{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; BOOST_CHECK_EQUAL_COLLECTIONS(subsets[0].begin(), subsets[0].end(), fullSet.begin(), fullSet.end()); - for (size_t i = 1; i < 10; ++i) BOOST_CHECK_EQUAL(ds.find(0), ds.find(i)); + for (std::uint32_t i = 1; i < 10; ++i) BOOST_CHECK_EQUAL(ds.find(0), ds.find(i)); } BOOST_AUTO_TEST_CASE(pairs) { - ContiguousDisjointSet ds(10); + ContiguousDisjointSet ds(10); BOOST_CHECK_EQUAL(ds.numSets(), 10); BOOST_CHECK_EQUAL(ds.subsets().size(), 10); - auto const checkPair = [&](size_t expectedNumSubsets, size_t x, size_t y) + auto const checkPair = [&](size_t expectedNumSubsets, std::uint32_t x, std::uint32_t y) { BOOST_CHECK_EQUAL(ds.numSets(), expectedNumSubsets); BOOST_CHECK_EQUAL(ds.subsets().size(), expectedNumSubsets); @@ -84,7 +84,7 @@ BOOST_AUTO_TEST_CASE(pairs) BOOST_AUTO_TEST_CASE(merge_with_fixed_representative) { - ContiguousDisjointSet ds(10); + ContiguousDisjointSet ds(10); ds.merge(5, 3, false); BOOST_CHECK_EQUAL(ds.find(5), 5); ds.merge(1, 2); From 1ea88b839979075619ff2721230dbf8156d8c105 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 20 Oct 2025 14:21:46 +0200 Subject: [PATCH 064/243] SSACFG: Use tagged union for ValueId --- libyul/backends/evm/ssa/ControlFlow.cpp | 3 +- libyul/backends/evm/ssa/ControlFlow.h | 30 ++-- libyul/backends/evm/ssa/LivenessAnalysis.cpp | 46 +++--- libyul/backends/evm/ssa/LivenessAnalysis.h | 5 +- libyul/backends/evm/ssa/SSACFG.cpp | 77 +++++---- libyul/backends/evm/ssa/SSACFG.h | 143 ++++++++++------ libyul/backends/evm/ssa/SSACFGBuilder.cpp | 54 +++--- .../evm/ssa/SSACFGLoopNestingForest.cpp | 18 +- .../evm/ssa/SSACFGLoopNestingForest.h | 20 ++- .../evm/ssa/SSACFGTopologicalSort.cpp | 6 +- .../backends/evm/ssa/SSACFGTopologicalSort.h | 26 +-- .../libyul/yulSSAControlFlowGraph/complex.yul | 102 ++++++------ .../yulSSAControlFlowGraph/complex2.yul | 122 +++++++------- .../default_initialized_variable.yul | 18 +- .../yulSSAControlFlowGraph/function.yul | 24 +-- test/libyul/yulSSAControlFlowGraph/if.yul | 20 +-- .../yulSSAControlFlowGraph/if_const.yul | 10 +- .../yulSSAControlFlowGraph/nested_for.yul | 154 +++++++++--------- .../nested_function.yul | 66 ++++---- test/libyul/yulSSAControlFlowGraph/switch.yul | 32 ++-- .../yulSSAControlFlowGraph/switch_const.yul | 10 +- 21 files changed, 523 insertions(+), 463 deletions(-) diff --git a/libyul/backends/evm/ssa/ControlFlow.cpp b/libyul/backends/evm/ssa/ControlFlow.cpp index fb560d6dd6..16026ab30d 100644 --- a/libyul/backends/evm/ssa/ControlFlow.cpp +++ b/libyul/backends/evm/ssa/ControlFlow.cpp @@ -24,8 +24,7 @@ using namespace solidity::yul::ssa; ControlFlowLiveness::ControlFlowLiveness(ControlFlow const& _controlFlow): controlFlow(_controlFlow), - mainLiveness(std::make_unique(*_controlFlow.mainGraph)), - functionLiveness(_controlFlow.functionGraphs | ranges::views::transform([](auto const& _cfg) { return std::make_unique(*_cfg); }) | ranges::to) + cfgLiveness(_controlFlow.functionGraphs | ranges::views::transform([](auto const& _cfg) { return std::make_unique(*_cfg); }) | ranges::to) { } std::string ControlFlowLiveness::toDot() const diff --git a/libyul/backends/evm/ssa/ControlFlow.h b/libyul/backends/evm/ssa/ControlFlow.h index 314545643e..ea07d4d4e5 100644 --- a/libyul/backends/evm/ssa/ControlFlow.h +++ b/libyul/backends/evm/ssa/ControlFlow.h @@ -24,6 +24,8 @@ #include #include +#include + namespace solidity::yul::ssa { @@ -33,44 +35,52 @@ struct ControlFlowLiveness{ explicit ControlFlowLiveness(ControlFlow const& _controlFlow); std::reference_wrapper controlFlow; - std::unique_ptr mainLiveness; - std::vector> functionLiveness; + std::vector> cfgLiveness; std::string toDot() const; }; struct ControlFlow { - std::unique_ptr mainGraph{std::make_unique()}; - std::vector> functionGraphs{}; - std::vector> functionGraphMapping{}; + using FunctionGraphID = std::uint32_t; - SSACFG const* functionGraph(Scope::Function const* _function) + static FunctionGraphID constexpr mainGraphID() noexcept { return 0; } + + SSACFG const* mainGraph() const { return functionGraph(mainGraphID()); } + + SSACFG const* functionGraph(Scope::Function const* _function) const { - auto it = std::find_if(functionGraphMapping.begin(), functionGraphMapping.end(), [_function](auto const& tup) { return _function == std::get<0>(tup); }); + auto it = ranges::find_if(functionGraphMapping, [_function](auto const& tup) { return _function == std::get<0>(tup); }); if (it != functionGraphMapping.end()) return std::get<1>(*it); return nullptr; } + SSACFG const* functionGraph(FunctionGraphID const _id) const + { + return functionGraphs.at(_id).get(); + } + std::string toDot(ControlFlowLiveness const* _liveness=nullptr) const { if (_liveness) yulAssert(&_liveness->controlFlow.get() == this); std::ostringstream output; output << "digraph SSACFG {\nnodesep=0.7;\ngraph[fontname=\"DejaVu Sans\"]\nnode[shape=box,fontname=\"DejaVu Sans\"];\n\n"; - output << mainGraph->toDot(false, std::nullopt, _liveness ? _liveness->mainLiveness.get() : nullptr); for (size_t index=0; index < functionGraphs.size(); ++index) output << functionGraphs[index]->toDot( false, - index+1, - _liveness ? _liveness->functionLiveness[index].get() : nullptr + index, + _liveness ? _liveness->cfgLiveness[index].get() : nullptr ); output << "}\n"; return output.str(); } + + std::vector> functionGraphs{}; + std::vector> functionGraphMapping{}; }; } diff --git a/libyul/backends/evm/ssa/LivenessAnalysis.cpp b/libyul/backends/evm/ssa/LivenessAnalysis.cpp index 42d2cf39c3..d97ff00971 100644 --- a/libyul/backends/evm/ssa/LivenessAnalysis.cpp +++ b/libyul/backends/evm/ssa/LivenessAnalysis.cpp @@ -31,18 +31,11 @@ using namespace solidity::yul::ssa; namespace { -constexpr auto excludingLiteralsFilter(SSACFG const& _cfg) +constexpr auto excludingLiteralsFilter() { - return [&_cfg](LivenessAnalysis::LivenessData::Value const& _valueId) -> bool + return [](LivenessAnalysis::LivenessData::Value const& _valueId) -> bool { - return !std::holds_alternative(_cfg.valueInfo(_valueId)); - }; -} -constexpr auto unreachableFilter(SSACFG const& _cfg) -{ - return [&_cfg](LivenessAnalysis::LivenessData::Value const& _valueId) -> bool - { - return std::holds_alternative(_cfg.valueInfo(_valueId)); + return !_valueId.isLiteral(); }; } } @@ -149,18 +142,18 @@ LivenessAnalysis::LivenessData LivenessAnalysis::blockExitValues(SSACFG::BlockId [](SSACFG::BasicBlock::MainExit const&) {}, [&](SSACFG::BasicBlock::FunctionReturn const& _functionReturn) { - for (auto const& valueId: _functionReturn.returnValues | ranges::views::filter(excludingLiteralsFilter(m_cfg))) + for (auto const& valueId: _functionReturn.returnValues | ranges::views::filter(excludingLiteralsFilter())) result.insert(valueId); }, [&](SSACFG::BasicBlock::JumpTable const& _jt) { - if (excludingLiteralsFilter(m_cfg)(_jt.value)) + if (excludingLiteralsFilter()(_jt.value)) result.insert(_jt.value); }, [](SSACFG::BasicBlock::Jump const&) {}, [&](SSACFG::BasicBlock::ConditionalJump const& _conditionalJump) { - if (excludingLiteralsFilter(m_cfg)(_conditionalJump.condition)) + if (excludingLiteralsFilter()(_conditionalJump.condition)) result.insert(_conditionalJump.condition); }, [](SSACFG::BasicBlock::Terminated const&) {}}; @@ -218,12 +211,11 @@ void LivenessAnalysis::runDagDfs() { for (auto const& phi: m_cfg.block(_successor).phis) { - auto const& info = m_cfg.valueInfo(phi); - yulAssert(std::holds_alternative(info), "value info of phi wasn't PhiValue"); - auto const argIndex = m_cfg.phiArgumentIndex(blockId, _successor); - yulAssert(argIndex < std::get(info).arguments.size()); - auto const arg = std::get(info).arguments.at(argIndex); - if (!std::holds_alternative(m_cfg.valueInfo(arg))) + auto const& info = m_cfg.phiInfo(phi); + auto const& argIndex = m_cfg.phiArgumentIndex(blockId, _successor); + yulAssert(argIndex < info.arguments.size()); + auto const& arg = info.arguments.at(argIndex); + if (!arg.isLiteral()) live.insert(arg); } }); @@ -242,11 +234,11 @@ void LivenessAnalysis::runDagDfs() }); if (std::holds_alternative(block.exit)) - for (auto const& returnValue: std::get(block.exit).returnValues | ranges::views::filter(excludingLiteralsFilter(m_cfg))) + for (auto const& returnValue: std::get(block.exit).returnValues | ranges::views::filter(excludingLiteralsFilter())) live.insert(returnValue); // clean out unreachables - live.eraseIf([&](auto const& _entry) { return unreachableFilter(m_cfg)(_entry.first); }); + live.eraseIf([&](auto const& _entry) { return _entry.first.isUnreachable(); }); // LiveOut(B) <- live m_liveOuts[blockId.value] = live; @@ -259,9 +251,9 @@ void LivenessAnalysis::runDagDfs() for (auto const& op: block.operations | ranges::views::reverse) { // remove variables defined at p from live - live.eraseAll(op.outputs | ranges::views::filter(excludingLiteralsFilter(m_cfg)) | ranges::to); + live.eraseAll(op.outputs | ranges::views::filter(excludingLiteralsFilter()) | ranges::to); // add uses at p to live - live.insertAll(op.inputs | ranges::views::filter(excludingLiteralsFilter(m_cfg)) | ranges::to); + live.insertAll(op.inputs | ranges::views::filter(excludingLiteralsFilter()) | ranges::to); } } @@ -272,7 +264,7 @@ void LivenessAnalysis::runDagDfs() } } -void LivenessAnalysis::runLoopTreeDfs(size_t const _loopHeader) +void LivenessAnalysis::runLoopTreeDfs(SSACFG::BlockId::ValueType const _loopHeader) { // SSA Book, Algorithm 9.3 if (m_loopNestingForest.loopNodes().contains(_loopHeader)) @@ -286,7 +278,7 @@ void LivenessAnalysis::runLoopTreeDfs(size_t const _loopHeader) // must be live out of header if live in of children m_liveOuts[_loopHeader].maxUnion(liveLoop); // for each blockId \in children(loopHeader) - for (size_t blockIdValue = 0; blockIdValue < m_cfg.numBlocks(); ++blockIdValue) + for (SSACFG::BlockId::ValueType blockIdValue = 0u; blockIdValue < m_cfg.numBlocks(); ++blockIdValue) if (m_loopNestingForest.loopParents()[blockIdValue] == _loopHeader) { // propagate loop liveness information down to the loop header's children @@ -313,9 +305,9 @@ void LivenessAnalysis::fillOperationsLiveOut() for (auto const& op: operations | ranges::views::reverse) { *rit = live; - for (auto const& output: op.outputs | ranges::views::filter(excludingLiteralsFilter(m_cfg))) + for (auto const& output: op.outputs | ranges::views::filter(excludingLiteralsFilter())) live.erase(output); - for (auto const& input: op.inputs | ranges::views::filter(excludingLiteralsFilter(m_cfg))) + for (auto const& input: op.inputs | ranges::views::filter(excludingLiteralsFilter())) live.insert(input); ++rit; } diff --git a/libyul/backends/evm/ssa/LivenessAnalysis.h b/libyul/backends/evm/ssa/LivenessAnalysis.h index 6462af86d8..b34a790bf1 100644 --- a/libyul/backends/evm/ssa/LivenessAnalysis.h +++ b/libyul/backends/evm/ssa/LivenessAnalysis.h @@ -42,8 +42,7 @@ class LivenessAnalysis LivenessData() = default; template Sentinel> - LivenessData(Iter begin, Sentinel end): m_liveCounts(begin, end) { - } + LivenessData(Iter begin, Sentinel end): m_liveCounts(begin, end) {} explicit LivenessData(LiveCounts&& _liveCounts): m_liveCounts(std::move(_liveCounts)) {} bool contains(Value const& _valueId) const; @@ -113,7 +112,7 @@ class LivenessAnalysis private: void runDagDfs(); - void runLoopTreeDfs(std::size_t _loopHeader); + void runLoopTreeDfs(SSACFG::BlockId::ValueType _loopHeader); void fillOperationsLiveOut(); LivenessData blockExitValues(SSACFG::BlockId const& _blockId) const; diff --git a/libyul/backends/evm/ssa/SSACFG.cpp b/libyul/backends/evm/ssa/SSACFG.cpp index 9a98a08ba5..a785c51aa2 100644 --- a/libyul/backends/evm/ssa/SSACFG.cpp +++ b/libyul/backends/evm/ssa/SSACFG.cpp @@ -28,6 +28,8 @@ #include #pragma GCC diagnostic pop +#include + #include using namespace solidity; @@ -43,41 +45,23 @@ class SSACFGPrinter SSACFGPrinter(SSACFG const& _cfg, SSACFG::BlockId _blockId, LivenessAnalysis const* _liveness): m_cfg(_cfg), m_functionIndex(0), m_liveness(_liveness) { + if (_liveness) + m_junkAdmittingBlocks = std::make_unique(_cfg, _liveness->topologicalSort()); printBlock(_blockId); } SSACFGPrinter(SSACFG const& _cfg, size_t _functionIndex, Scope::Function const& _function, LivenessAnalysis const* _liveness): m_cfg(_cfg), m_functionIndex(_functionIndex), m_liveness(_liveness) { + if (_liveness) + m_junkAdmittingBlocks = std::make_unique(_cfg, _liveness->topologicalSort()); printFunction(_function); } friend std::ostream& operator<<(std::ostream& stream, SSACFGPrinter const& printer) { stream << printer.m_result.str(); return stream; } -private: - static std::string varToString(SSACFG const& _cfg, SSACFG::ValueId _var) { - if (_var.value == std::numeric_limits::max()) - return "INVALID"; - auto const& info = _cfg.valueInfo(_var); - return std::visit( - GenericVisitor{ - [&](SSACFG::UnreachableValue const&) -> std::string { - return "[unreachable]"; - }, - [&](SSACFG::PhiValue const&) -> std::string { - return fmt::format("v{}", _var.value); - }, - [&](SSACFG::VariableValue const&) -> std::string { - return fmt::format("v{}", _var.value); - }, - [&](SSACFG::LiteralValue const& _literal) -> std::string { - return formatNumberReadable(_literal.value); - } - }, - info - ); - } +private: static std::string escape(std::string_view const str) { using namespace std::literals; @@ -113,7 +97,7 @@ class SSACFGPrinter static std::string formatPhi(SSACFG const& _cfg, SSACFG::PhiValue const& _phiValue) { - auto const transform = [&](SSACFG::ValueId const& valueId) { return varToString(_cfg, valueId); }; + auto const transform = [&](SSACFG::ValueId const& valueId) { return valueId.str(_cfg); }; std::vector formattedArgs; formattedArgs.reserve(_phiValue.arguments.size()); for (auto const& [arg, entry]: ranges::zip_view(_phiValue.arguments | ranges::views::transform(transform), _cfg.block(_phiValue.block).entries)) @@ -126,7 +110,7 @@ class SSACFGPrinter void writeBlock(SSACFG::BlockId const& _id, SSACFG::BasicBlock const& _block) { - auto const valueToString = [&](SSACFG::ValueId const& valueId) { return varToString(m_cfg, valueId); }; + auto const valueToString = [&](SSACFG::ValueId const& valueId) { return valueId.str(m_cfg); }; bool entryBlock = _id.value == 0 && m_functionIndex == 0; if (entryBlock) { @@ -134,11 +118,15 @@ class SSACFGPrinter m_result << fmt::format("Entry{} -> {};\n", m_functionIndex, formatBlockHandle(_id)); } { + std::string revertPathInfo; + if (m_junkAdmittingBlocks) + revertPathInfo = m_junkAdmittingBlocks->allowsAdditionOfJunk(_id) ? "fillcolor=\"#FF746C\", style=filled, " : ""; if (m_liveness) { m_result << fmt::format( - "{} [label=\"\\\nBlock {}; ({}, max {})\\n", + "{} [{}label=\"\\\nBlock {}; ({}, max {})\\n", formatBlockHandle(_id), + revertPathInfo, _id.value, m_liveness->topologicalSort().preOrderIndexOf(_id.value), m_liveness->topologicalSort().maxSubtreePreOrderIndexOf(_id.value) @@ -151,14 +139,19 @@ class SSACFGPrinter "LiveOut: {}\\l\\n", fmt::join(m_liveness->liveOut(_id) | ranges::views::transform([&](auto const& liveOut) { return valueToString(SSACFG::ValueId{liveOut.first}) + fmt::format("[{}]", liveOut.second); }), ", ") ); + auto const usedVariables = m_liveness->used(_id); + m_result << fmt::format( + "Used: {}\\l\\n", + fmt::join(usedVariables | ranges::views::transform([&](auto const& used) { return valueToString(SSACFG::ValueId{used.first}) + fmt::format("[{}]", used.second); }), ", ") + ); } else - m_result << fmt::format("{} [label=\"\\\nBlock {}\\n", formatBlockHandle(_id), _id.value); + m_result << fmt::format("{} [{}label=\"\\\nBlock {}\\n", formatBlockHandle(_id), revertPathInfo, _id.value); + for (auto const& phi: _block.phis) { - auto const* phiValue = std::get_if(&m_cfg.valueInfo(phi)); - solAssert(phiValue); - m_result << fmt::format("v{} := {}\\l\\\n", phi.value, formatPhi(m_cfg, *phiValue)); + auto const& phiInfo = m_cfg.phiInfo(phi); + m_result << fmt::format("phi{} := {}\\l\\\n", phi.value(), formatPhi(m_cfg, phiInfo)); } for (auto const& operation: _block.operations) { @@ -172,7 +165,7 @@ class SSACFGPrinter [&](SSACFG::LiteralAssignment const&) { yulAssert(operation.inputs.size() == 1); - return varToString(m_cfg, operation.inputs.back()); + return operation.inputs.back().str(m_cfg); } }, operation.kind); if (!operation.outputs.empty()) @@ -210,7 +203,7 @@ class SSACFGPrinter m_result << fmt::format("{} -> {}Exit;\n", formatBlockHandle(_id), formatBlockHandle(_id)); m_result << fmt::format( "{}Exit [label=\"{{ If {} | {{ <0> Zero | <1> NonZero }}}}\" shape=Mrecord];\n", - formatBlockHandle(_id), varToString(m_cfg, _conditionalJump.condition) + formatBlockHandle(_id), _conditionalJump.condition.str(m_cfg) ); m_result << formatEdge(_id, _conditionalJump.zero, "0"); m_result << formatEdge(_id, _conditionalJump.nonZero, "1"); @@ -279,7 +272,7 @@ class SSACFGPrinter void printFunction(Scope::Function const& _fun) { static auto constexpr returnsTransform = [](auto const& functionReturnValue) { return escape(functionReturnValue.get().name.str()); }; - static auto constexpr argsTransform = [](auto const& arg) { return fmt::format("v{}", std::get<1>(arg).value); }; + static auto constexpr argsTransform = [](auto const& arg) { return fmt::format("v{}", std::get<1>(arg).value()); }; m_result << "FunctionEntry_" << escape(_fun.name.str()) << "_" << m_cfg.entry.value << " [label=\""; if (!m_cfg.returns.empty()) m_result << fmt::format("function {0}:\n {1} := {0}({2})", escape(_fun.name.str()), fmt::join(m_cfg.returns | ranges::views::transform(returnsTransform), ", "), fmt::join(m_cfg.arguments | ranges::views::transform(argsTransform), ", ")); @@ -291,12 +284,28 @@ class SSACFGPrinter } SSACFG const& m_cfg; + std::unique_ptr m_junkAdmittingBlocks; size_t m_functionIndex; LivenessAnalysis const* m_liveness; std::stringstream m_result{}; }; } +std::string SSACFG::ValueId::str(SSACFG const& _cfg) const +{ + if (!hasValue()) + return "INVALID"; + switch (kind()) + { + case Kind::Literal: return toCompactHexWithPrefix(_cfg.literalInfo(*this).value); + case Kind::Variable: return fmt::format("v{}", value()); + case Kind::Phi: return fmt::format("phi{}", value()); + case Kind::Unreachable: return "[unreachable]"; + } + unreachable(); +} + + std::string SSACFG::toDot( bool _includeDiGraphDefinition, std::optional _functionIndex, @@ -305,7 +314,7 @@ std::string SSACFG::toDot( { std::ostringstream output; if (_includeDiGraphDefinition) - output << "digraph SSACFG {\nnodesep=0.7;\ngraph[fontname=\"DejaVu Sans\"]\nnode[shape=box,fontname=\"DejaVu Sans\"];\n\n"; + output << "digraph SSACFG {\nnodesep=0.7;\ngraph[fontname=\"DejaVu Sans\", rankdir=LR]\nnode[shape=box,fontname=\"DejaVu Sans\"];\n\n"; if (function) output << SSACFGPrinter(*this, _functionIndex ? *_functionIndex : static_cast(1), *function, _liveness); else diff --git a/libyul/backends/evm/ssa/SSACFG.h b/libyul/backends/evm/ssa/SSACFG.h index 5ea8247ce3..40537cfcd5 100644 --- a/libyul/backends/evm/ssa/SSACFG.h +++ b/libyul/backends/evm/ssa/SSACFG.h @@ -51,15 +51,50 @@ class SSACFG struct BlockId { - size_t value = std::numeric_limits::max(); + using ValueType = std::uint32_t; + ValueType value = std::numeric_limits::max(); + bool hasValue() const { return value != std::numeric_limits::max(); } auto operator<=>(BlockId const&) const = default; }; - struct ValueId + class ValueId { - using ValueType = size_t; - ValueType value = std::numeric_limits::max(); - bool hasValue() const { return value != std::numeric_limits::max(); } + public: + enum class Kind: std::uint8_t + { + Literal, + Variable, + Phi, + Unreachable + }; + using ValueType = std::uint32_t; + + constexpr ValueId() = default; + constexpr ValueId(ValueType const _value, Kind const _kind): m_value(_value), m_kind(_kind) {} + constexpr ValueId(ValueId const&) = default; + constexpr ValueId(ValueId&&) = default; + constexpr ValueId& operator=(ValueId const&) = default; + constexpr ValueId& operator=(ValueId&&) = default; + + static ValueId constexpr makeLiteral(ValueType const& _value) { return ValueId{_value, Kind::Literal}; } + static ValueId constexpr makeVariable(ValueType const& _value) { return ValueId{_value, Kind::Variable}; } + static ValueId constexpr makePhi(ValueType const& _value) { return ValueId{_value, Kind::Phi}; } + static ValueId constexpr makeUnreachable() { return ValueId{0u, Kind::Unreachable}; } + + bool constexpr isLiteral() const noexcept { return m_kind == Kind::Literal; } + bool constexpr isVariable() const noexcept { return m_kind == Kind::Variable; } + bool constexpr isPhi() const noexcept { return m_kind == Kind::Phi; } + bool constexpr isUnreachable() const noexcept { return m_kind == Kind::Unreachable; } + + bool constexpr hasValue() const { return m_value != std::numeric_limits::max(); } + ValueType constexpr value() const noexcept { return m_value; } + Kind constexpr kind() const noexcept { return m_kind; } + std::string str(SSACFG const& _cfg) const; + auto operator<=>(ValueId const&) const = default; + + private: + ValueType m_value{std::numeric_limits::max()}; + Kind m_kind{Kind::Unreachable}; }; struct BuiltinCall @@ -158,7 +193,7 @@ class SSACFG }; BlockId makeBlock(langutil::DebugData::ConstPtr _debugData) { - BlockId blockId { m_blocks.size() }; + BlockId blockId { static_cast(m_blocks.size()) }; m_blocks.emplace_back(BasicBlock{std::move(_debugData), {}, {}, {}, BasicBlock::Terminated{}}); return blockId; } @@ -183,63 +218,47 @@ class SSACFG std::vector arguments; }; struct UnreachableValue {}; - using ValueInfo = std::variant; - bool isLiteralValue(ValueId const _var) const - { - return std::holds_alternative(valueInfo(_var)); - } - bool isPhiValue(ValueId const _var) const - { - return std::holds_alternative(valueInfo(_var)); - } - ValueInfo& valueInfo(ValueId const _var) - { - return m_valueInfos.at(_var.value); - } - ValueInfo const& valueInfo(ValueId const _var) const - { - return valueInfo(_var.value); - } - ValueInfo const& valueInfo(ValueId::ValueType const _var) const - { - return m_valueInfos.at(_var); - } ValueId newPhi(BlockId const _definingBlock) { - ValueId id { m_valueInfos.size() }; - auto block = m_blocks.at(_definingBlock.value); - m_valueInfos.emplace_back(PhiValue{debugDataOf(block), _definingBlock, {}}); - return id; + auto const& block = m_blocks.at(_definingBlock.value); + m_phis.emplace_back(PhiValue{debugDataOf(block), _definingBlock, std::vector{}}); + auto const value = m_phis.size() - 1; + yulAssert(value < std::numeric_limits::max()); + return ValueId::makePhi(static_cast(value)); } ValueId newVariable(BlockId const _definingBlock) { - ValueId id { m_valueInfos.size() }; - auto block = m_blocks.at(_definingBlock.value); - m_valueInfos.emplace_back(VariableValue{debugDataOf(block), _definingBlock}); - return id; + auto const& block = m_blocks.at(_definingBlock.value); + m_variables.emplace_back(VariableValue{debugDataOf(block), _definingBlock}); + auto const value = m_variables.size() - 1; + yulAssert(value < std::numeric_limits::max()); + return ValueId::makeVariable(static_cast(value)); } + ValueId unreachableValue() { if (!m_unreachableValue) - { - m_unreachableValue = ValueId { m_valueInfos.size() }; - m_valueInfos.emplace_back(UnreachableValue{}); - } + m_unreachableValue = ValueId::makeUnreachable(); return *m_unreachableValue; } + ValueId newLiteral(langutil::DebugData::ConstPtr _debugData, u256 _value) { - auto [it, inserted] = m_literals.emplace(_value, ValueId{m_valueInfos.size()}); - if (inserted) - m_valueInfos.emplace_back(LiteralValue{std::move(_debugData), _value}); - else + auto const it = m_literalMapping.find(_value); + if (it != m_literalMapping.end()) { - yulAssert(_value == it->first); - yulAssert(std::holds_alternative(m_valueInfos.at(it->second.value))); - yulAssert(std::get(m_valueInfos.at(it->second.value)).value == _value); + ValueId const& valueId = it->second; + yulAssert(valueId.hasValue() && m_literals[valueId.value()].value == _value); + return valueId; } - yulAssert(it->second.value < m_valueInfos.size()); - return it->second; + + + m_literals.emplace_back(LiteralValue{std::move(_debugData), std::move(_value)}); + auto const value = m_literals.size() - 1; + yulAssert(value < std::numeric_limits::max()); + auto const literalId = ValueId::makeLiteral(static_cast(value)); + m_literalMapping.emplace(_value, literalId); + return literalId; } size_t phiArgumentIndex(BlockId const _source, BlockId const _target) const @@ -255,9 +274,33 @@ class SSACFG std::optional _functionIndex=std::nullopt, LivenessAnalysis const* _liveness=nullptr ) const; + + PhiValue const& phiInfo(ValueId const& _valueId) const + { + yulAssert(_valueId.hasValue() && _valueId.isPhi()); + return m_phis.at(_valueId.value()); + } + PhiValue& phiInfo(ValueId const& _valueId) + { + yulAssert(_valueId.hasValue() && _valueId.isPhi()); + return m_phis.at(_valueId.value()); + } + LiteralValue const& literalInfo(ValueId const& _valueId) const + { + yulAssert(_valueId.hasValue() && _valueId.isLiteral()); + return m_literals.at(_valueId.value()); + } + VariableValue const& variableInfo(ValueId const& _valueId) const + { + yulAssert(_valueId.hasValue() && _valueId.isVariable()); + return m_variables.at(_valueId.value()); + } + private: - std::deque m_valueInfos; - std::map m_literals; + std::vector m_literals; + std::map m_literalMapping; + std::vector m_phis; + std::vector m_variables; std::optional m_unreachableValue; public: langutil::DebugData::ConstPtr debugData; diff --git a/libyul/backends/evm/ssa/SSACFGBuilder.cpp b/libyul/backends/evm/ssa/SSACFGBuilder.cpp index 92b49e7fc1..59e28b1b82 100644 --- a/libyul/backends/evm/ssa/SSACFGBuilder.cpp +++ b/libyul/backends/evm/ssa/SSACFGBuilder.cpp @@ -72,13 +72,16 @@ std::unique_ptr SSACFGBuilder::build( ControlFlowSideEffectsCollector sideEffects(_dialect, _block); auto controlFlow = std::make_unique(); - SSACFGBuilder builder(*controlFlow, *controlFlow->mainGraph, _analysisInfo, sideEffects, _dialect, _keepLiteralAssignments); - builder.m_currentBlock = controlFlow->mainGraph->makeBlock(debugDataOf(_block)); + controlFlow->functionGraphs.emplace_back(std::make_unique()); + controlFlow->functionGraphMapping.emplace_back(nullptr, controlFlow->functionGraphs.back().get()); + SSACFG& mainGraph = *controlFlow->functionGraphs.back(); + SSACFGBuilder builder(*controlFlow, mainGraph, _analysisInfo, sideEffects, _dialect, _keepLiteralAssignments); + builder.m_currentBlock = mainGraph.makeBlock(debugDataOf(_block)); builder.sealBlock(builder.m_currentBlock); builder(_block); if (!builder.blockInfo(builder.m_currentBlock).sealed) builder.sealBlock(builder.m_currentBlock); - controlFlow->mainGraph->block(builder.m_currentBlock).exit = SSACFG::BasicBlock::MainExit{}; + mainGraph.block(builder.m_currentBlock).exit = SSACFG::BasicBlock::MainExit{}; builder.cleanUnreachable(); return controlFlow; } @@ -86,12 +89,11 @@ std::unique_ptr SSACFGBuilder::build( SSACFG::ValueId SSACFGBuilder::tryRemoveTrivialPhi(SSACFG::ValueId _phi) { // TODO: double-check if this is sane - auto const* phiInfo = std::get_if(&m_graph.valueInfo(_phi)); - yulAssert(phiInfo); - yulAssert(blockInfo(phiInfo->block).sealed); + auto const& phiInfo = m_graph.phiInfo(_phi); + yulAssert(blockInfo(phiInfo.block).sealed); SSACFG::ValueId same; - for (SSACFG::ValueId arg: phiInfo->arguments) + for (SSACFG::ValueId arg: phiInfo.arguments) { if (arg == same || arg == _phi) continue; // unique value or self-reference @@ -106,19 +108,19 @@ SSACFG::ValueId SSACFGBuilder::tryRemoveTrivialPhi(SSACFG::ValueId _phi) same = m_graph.unreachableValue(); } - m_graph.block(phiInfo->block).phis.erase(_phi); + m_graph.block(phiInfo.block).phis.erase(_phi); std::vector phiUses; - for (size_t blockIdValue = 0; blockIdValue < m_graph.numBlocks(); ++blockIdValue) + for (SSACFG::BlockId::ValueType blockIdValue = 0; blockIdValue < m_graph.numBlocks(); ++blockIdValue) { auto& block = m_graph.block(SSACFG::BlockId{blockIdValue}); for (auto blockPhi: block.phis) { + yulAssert(blockPhi.hasValue()); yulAssert(blockPhi != _phi, "Phis should be defined in exactly one block, _phi was erased."); - auto* blockPhiInfo = std::get_if(&m_graph.valueInfo(blockPhi)); - yulAssert(blockPhiInfo); + auto& blockPhiInfo = m_graph.phiInfo(blockPhi); bool usedInPhi = false; - for (auto& arg: blockPhiInfo->arguments) + for (auto& arg: blockPhiInfo.arguments) if (arg == _phi) { arg = same; @@ -185,14 +187,15 @@ void SSACFGBuilder::cleanUnreachable() std::vector maybeTrivialPhi; std::erase_if(block.entries, [&](auto const& entry) { return !reachabilityCheck.visited.contains(entry); }); for (auto phi: block.phis) - if (auto* phiInfo = std::get_if(&m_graph.valueInfo(phi))) - { - auto erasedCount = std::erase_if(phiInfo->arguments, [&](SSACFG::ValueId _arg) { - return std::holds_alternative(m_graph.valueInfo(_arg)); - }); - if (erasedCount > 0) - maybeTrivialPhi.push_back(phi); - } + { + yulAssert(phi.hasValue()); + auto& phiInfo = m_graph.phiInfo(phi); + auto const erasedCount = std::erase_if(phiInfo.arguments, [&](SSACFG::ValueId const _arg) { + return _arg.isUnreachable(); + }); + if (erasedCount > 0) + maybeTrivialPhi.push_back(phi); + } // After removing a phi argument, we might end up with a trivial phi that can be removed. for (auto phi: maybeTrivialPhi) @@ -565,7 +568,7 @@ void SSACFGBuilder::assign(std::vector(m_graph.valueInfo(_phi))); - auto& phi = std::get(m_graph.valueInfo(_phi)); - for (auto const& pred: m_graph.block(phi.block).entries) - phi.arguments.emplace_back(readVariable(_variable, pred)); + for (auto const& pred: m_graph.block(m_graph.phiInfo(_phi).block).entries) + { + auto const var = readVariable(_variable, pred); + m_graph.phiInfo(_phi).arguments.emplace_back(var); + } // we call tryRemoveTrivialPhi explicitly to avoid removing trivial phis in unsealed blocks return _phi; } diff --git a/libyul/backends/evm/ssa/SSACFGLoopNestingForest.cpp b/libyul/backends/evm/ssa/SSACFGLoopNestingForest.cpp index bbd171a452..c5c422a467 100644 --- a/libyul/backends/evm/ssa/SSACFGLoopNestingForest.cpp +++ b/libyul/backends/evm/ssa/SSACFGLoopNestingForest.cpp @@ -18,17 +18,19 @@ #include +#include + using namespace solidity::yul::ssa; SSACFGLoopNestingForest::SSACFGLoopNestingForest(ForwardSSACFGTopologicalSort const& _sort): m_sort(_sort), m_cfg(_sort.cfg()), m_vertexPartition(m_cfg.numBlocks()), - m_loopParents(m_cfg.numBlocks(), std::numeric_limits::max()) + m_loopParents(m_cfg.numBlocks(), std::numeric_limits::max()) { auto dfsOrder = m_sort.preOrder(); // we go from innermost to outermost - std::reverse(dfsOrder.begin(), dfsOrder.end()); + ranges::reverse(dfsOrder); for (auto const& blockId: dfsOrder) findLoop(blockId); @@ -36,18 +38,18 @@ SSACFGLoopNestingForest::SSACFGLoopNestingForest(ForwardSSACFGTopologicalSort co // get the root nodes for (auto loopHeader: m_loopNodes) { - while (m_loopParents[loopHeader] != std::numeric_limits::max()) + while (m_loopParents[loopHeader] != std::numeric_limits::max()) loopHeader = m_loopParents[loopHeader]; m_loopRootNodes.insert(loopHeader); } } -void SSACFGLoopNestingForest::findLoop(size_t const _potentialHeader) +void SSACFGLoopNestingForest::findLoop(BlockIdValue const _potentialHeader) { - if (m_sort.backEdgeTargets().count(_potentialHeader) > 0) + if (m_sort.backEdgeTargets().contains(_potentialHeader)) { - std::set loopBody; - std::set workList; + std::set loopBody; + std::set workList; for (auto const pred: m_cfg.block(SSACFG::BlockId{_potentialHeader}).entries) { auto const representative = m_vertexPartition.find(pred.value); @@ -78,7 +80,7 @@ void SSACFGLoopNestingForest::findLoop(size_t const _potentialHeader) collapse(loopBody, _potentialHeader); } } -void SSACFGLoopNestingForest::collapse(std::set const& _loopBody, size_t _loopHeader) +void SSACFGLoopNestingForest::collapse(std::set const& _loopBody, BlockIdValue _loopHeader) { for (auto const z: _loopBody) { diff --git a/libyul/backends/evm/ssa/SSACFGLoopNestingForest.h b/libyul/backends/evm/ssa/SSACFGLoopNestingForest.h index 56cd5ace74..4654cbf135 100644 --- a/libyul/backends/evm/ssa/SSACFGLoopNestingForest.h +++ b/libyul/backends/evm/ssa/SSACFGLoopNestingForest.h @@ -36,26 +36,28 @@ namespace solidity::yul::ssa /// ACM Transactions on Programming Languages and Systems (TOPLAS) 21.2 (1999): 175-188. class SSACFGLoopNestingForest { + using BlockIdValue = SSACFG::BlockId::ValueType; + public: explicit SSACFGLoopNestingForest(ForwardSSACFGTopologicalSort const& _sort); /// blocks which are not contained in a loop get assigned the loop parent numeric_limit::max() - std::vector const& loopParents() const { return m_loopParents; } + std::vector const& loopParents() const { return m_loopParents; } /// all loop nodes (entry blocks for loops), also nested ones - std::set const& loopNodes() const { return m_loopNodes; } + std::set const& loopNodes() const { return m_loopNodes; } /// root loop nodes in the forest for outer-most loops - std::set const& loopRootNodes() const { return m_loopRootNodes; } + std::set const& loopRootNodes() const { return m_loopRootNodes; } private: - void findLoop(size_t _potentialHeader); - void collapse(std::set const& _loopBody, size_t _loopHeader); + void findLoop(BlockIdValue _potentialHeader); + void collapse(std::set const& _loopBody, BlockIdValue _loopHeader); ForwardSSACFGTopologicalSort const& m_sort; SSACFG const& m_cfg; - util::ContiguousDisjointSet m_vertexPartition; - std::vector m_loopParents; - std::set m_loopNodes; - std::set m_loopRootNodes; + util::ContiguousDisjointSet m_vertexPartition; + std::vector m_loopParents; + std::set m_loopNodes; + std::set m_loopRootNodes; }; } diff --git a/libyul/backends/evm/ssa/SSACFGTopologicalSort.cpp b/libyul/backends/evm/ssa/SSACFGTopologicalSort.cpp index 222a9774a8..3a1ca43c57 100644 --- a/libyul/backends/evm/ssa/SSACFGTopologicalSort.cpp +++ b/libyul/backends/evm/ssa/SSACFGTopologicalSort.cpp @@ -35,10 +35,10 @@ ForwardSSACFGTopologicalSort::ForwardSSACFGTopologicalSort(SSACFG const& _cfg): m_backEdgeTargets.insert(v2); } -void ForwardSSACFGTopologicalSort::dfs(size_t const _vertex) { +void ForwardSSACFGTopologicalSort::dfs(SSACFG::BlockId::ValueType const _vertex) { yulAssert(!m_explored[_vertex]); m_explored[_vertex] = true; - m_blockWisePreOrder[_vertex] = m_preOrder.size(); + m_blockWisePreOrder[_vertex] = static_cast(m_preOrder.size()); m_blockWiseMaxSubtreePreOrder[_vertex] = m_blockWisePreOrder[_vertex]; m_preOrder.push_back(_vertex); @@ -55,7 +55,7 @@ void ForwardSSACFGTopologicalSort::dfs(size_t const _vertex) { m_postOrder.push_back(_vertex); } -bool ForwardSSACFGTopologicalSort::ancestor(size_t const _block1, size_t const _block2) const { +bool ForwardSSACFGTopologicalSort::ancestor(SSACFG::BlockId::ValueType const _block1, SSACFG::BlockId::ValueType const _block2) const { yulAssert(_block1 < m_blockWisePreOrder.size()); yulAssert(_block2 < m_blockWisePreOrder.size()); diff --git a/libyul/backends/evm/ssa/SSACFGTopologicalSort.h b/libyul/backends/evm/ssa/SSACFGTopologicalSort.h index 5eb12545d7..39c2eebc35 100644 --- a/libyul/backends/evm/ssa/SSACFGTopologicalSort.h +++ b/libyul/backends/evm/ssa/SSACFGTopologicalSort.h @@ -33,26 +33,26 @@ class ForwardSSACFGTopologicalSort public: explicit ForwardSSACFGTopologicalSort(SSACFG const& _cfg); - std::vector const& preOrder() const { return m_preOrder; } - std::vector const& postOrder() const { return m_postOrder; } - std::set const& backEdgeTargets() const { return m_backEdgeTargets; } + std::vector const& preOrder() const { return m_preOrder; } + std::vector const& postOrder() const { return m_postOrder; } + std::set const& backEdgeTargets() const { return m_backEdgeTargets; } SSACFG const& cfg() const { return m_cfg; } bool backEdge(SSACFG::BlockId const& _block1, SSACFG::BlockId const& _block2) const; - size_t preOrderIndexOf(size_t _block) const { return m_blockWisePreOrder[_block]; } - size_t maxSubtreePreOrderIndexOf(size_t _block) const { return m_blockWiseMaxSubtreePreOrder[_block]; } + SSACFG::BlockId::ValueType preOrderIndexOf(SSACFG::BlockId::ValueType _block) const { return m_blockWisePreOrder[_block]; } + SSACFG::BlockId::ValueType maxSubtreePreOrderIndexOf(SSACFG::BlockId::ValueType _block) const { return m_blockWiseMaxSubtreePreOrder[_block]; } private: - void dfs(size_t _vertex); + void dfs(SSACFG::BlockId::ValueType _vertex); /// Checks if block1 is an ancestor of block2, ie there's a path from block1 to block2 in the dfs tree - bool ancestor(size_t _block1, size_t _block2) const; + bool ancestor(SSACFG::BlockId::ValueType _block1, SSACFG::BlockId::ValueType _block2) const; SSACFG const& m_cfg; std::vector m_explored{}; - std::vector m_postOrder{}; - std::vector m_preOrder{}; - std::vector m_blockWisePreOrder{}; - std::vector m_blockWiseMaxSubtreePreOrder{}; - std::vector> m_potentialBackEdges{}; - std::set m_backEdgeTargets{}; + std::vector m_postOrder{}; + std::vector m_preOrder{}; + std::vector m_blockWisePreOrder{}; + std::vector m_blockWiseMaxSubtreePreOrder{}; + std::vector> m_potentialBackEdges{}; + std::set m_backEdgeTargets{}; }; } diff --git a/test/libyul/yulSSAControlFlowGraph/complex.yul b/test/libyul/yulSSAControlFlowGraph/complex.yul index e4f29d07e4..24ab7af125 100644 --- a/test/libyul/yulSSAControlFlowGraph/complex.yul +++ b/test/libyul/yulSSAControlFlowGraph/complex.yul @@ -51,10 +51,10 @@ // // Entry0 [label="Entry"]; // Entry0 -> Block0_0; -// Block0_0 [label="\ +// Block0_0 [fillcolor="#FF746C", style=filled, label="\ // Block 0; (0, max 0)\nLiveIn: \l\ -// LiveOut: \l\nv2 := f(2, 1)\l\ -// pop(v2)\l\ +// LiveOut: \l\nUsed: \l\nv0 := f(0x02, 0x01)\l\ +// pop(v0)\l\ // "]; // Block0_0Exit [label="MainExit"]; // Block0_0 -> Block0_0Exit; @@ -63,135 +63,135 @@ // FunctionEntry_f_0 -> Block1_0; // Block1_0 [label="\ // Block 0; (0, max 17)\nLiveIn: v1[1], v0[1]\l\ -// LiveOut: v4[1], v1[1], v0[1]\l\nv4 := 42\l\ +// LiveOut: v2[1], v1[1], v0[1]\l\nUsed: \l\nv2 := 0x2a\l\ // "]; // Block1_0 -> Block1_0Exit [arrowhead=none]; // Block1_0Exit [label="Jump" shape=oval]; // Block1_0Exit -> Block1_1 [style="solid"]; // Block1_1 [label="\ -// Block 1; (1, max 17)\nLiveIn: v6[4], v1[1], v0[1]\l\ -// LiveOut: v6[2], v1[1], v0[1]\l\nv6 := φ(\l\ -// Block 0 => v4,\l\ -// Block 21 => v44\l\ +// Block 1; (1, max 17)\nLiveIn: phi1[4], v1[1], v0[1]\l\ +// LiveOut: phi1[2], v1[1], v0[1]\l\nUsed: phi1[2]\l\nphi1 := φ(\l\ +// Block 0 => v2,\l\ +// Block 21 => v10\l\ // )\l\ -// v7 := lt(v0, v6)\l\ +// v3 := lt(v0, phi1)\l\ // "]; // Block1_1 -> Block1_1Exit; -// Block1_1Exit [label="{ If v7 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1_1Exit [label="{ If v3 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_1Exit:0 -> Block1_4 [style="solid"]; // Block1_1Exit:1 -> Block1_2 [style="solid"]; // Block1_2 [label="\ -// Block 2; (2, max 17)\nLiveIn: v6[2], v1[1], v0[1]\l\ -// LiveOut: v6[1], v1[1], v8[3], v0[1]\l\nv8 := mload(v6)\l\ -// v9 := eq(0, v8)\l\ +// Block 2; (2, max 17)\nLiveIn: phi1[2], v1[1], v0[1]\l\ +// LiveOut: phi1[1], v1[1], v4[3], v0[1]\l\nUsed: phi1[1]\l\nv4 := mload(phi1)\l\ +// v5 := eq(0x00, v4)\l\ // "]; // Block1_2 -> Block1_2Exit; -// Block1_2Exit [label="{ If v9 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1_2Exit [label="{ If v5 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_2Exit:0 -> Block1_7 [style="solid"]; // Block1_2Exit:1 -> Block1_6 [style="solid"]; // Block1_4 [label="\ // Block 4; (4, max 4)\nLiveIn: \l\ -// LiveOut: \l\nsstore(3084, 12)\l\ +// LiveOut: \l\nUsed: \l\nsstore(0x0c0c, 0x0c)\l\ // "]; -// Block1_4Exit [label="FunctionReturn[0]"]; +// Block1_4Exit [label="FunctionReturn[0x00]"]; // Block1_4 -> Block1_4Exit; // Block1_6 [label="\ // Block 6; (3, max 4)\nLiveIn: \l\ -// LiveOut: \l\nsstore(514, 2)\l\ +// LiveOut: \l\nUsed: \l\nsstore(0x0202, 0x02)\l\ // "]; // Block1_6 -> Block1_6Exit [arrowhead=none]; // Block1_6Exit [label="Jump" shape=oval]; // Block1_6Exit -> Block1_4 [style="solid"]; // Block1_7 [label="\ -// Block 7; (5, max 17)\nLiveIn: v6[1], v1[1], v8[3], v0[1]\l\ -// LiveOut: v6[1], v1[1], v8[2], v0[1]\l\nv14 := eq(1, v8)\l\ +// Block 7; (5, max 17)\nLiveIn: phi1[1], v1[1], v4[3], v0[1]\l\ +// LiveOut: phi1[1], v1[1], v4[2], v0[1]\l\nUsed: v4[1]\l\nv6 := eq(0x01, v4)\l\ // "]; // Block1_7 -> Block1_7Exit; -// Block1_7Exit [label="{ If v14 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1_7Exit [label="{ If v6 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_7Exit:0 -> Block1_10 [style="solid"]; // Block1_7Exit:1 -> Block1_9 [style="solid"]; // Block1_9 [label="\ // Block 9; (6, max 6)\nLiveIn: \l\ -// LiveOut: \l\nsstore(1028, 4)\l\ +// LiveOut: \l\nUsed: \l\nsstore(0x0404, 0x04)\l\ // "]; -// Block1_9Exit [label="FunctionReturn[0]"]; +// Block1_9Exit [label="FunctionReturn[0x00]"]; // Block1_9 -> Block1_9Exit; // Block1_10 [label="\ -// Block 10; (7, max 17)\nLiveIn: v6[1], v1[1], v8[2], v0[1]\l\ -// LiveOut: v6[1], v1[1], v8[1], v0[1]\l\nv21 := eq(2, v8)\l\ +// Block 10; (7, max 17)\nLiveIn: phi1[1], v1[1], v4[2], v0[1]\l\ +// LiveOut: phi1[1], v1[1], v4[1], v0[1]\l\nUsed: v4[1]\l\nv7 := eq(0x02, v4)\l\ // "]; // Block1_10 -> Block1_10Exit; -// Block1_10Exit [label="{ If v21 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1_10Exit [label="{ If v7 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_10Exit:0 -> Block1_13 [style="solid"]; // Block1_10Exit:1 -> Block1_12 [style="solid"]; -// Block1_12 [label="\ +// Block1_12 [fillcolor="#FF746C", style=filled, label="\ // Block 12; (8, max 8)\nLiveIn: \l\ -// LiveOut: \l\nsstore(1542, 6)\l\ -// revert(0, 0)\l\ +// LiveOut: \l\nUsed: \l\nsstore(0x0606, 0x06)\l\ +// revert(0x00, 0x00)\l\ // "]; // Block1_12Exit [label="Terminated"]; // Block1_12 -> Block1_12Exit; // Block1_13 [label="\ -// Block 13; (9, max 17)\nLiveIn: v6[1], v1[1], v8[1], v0[1]\l\ -// LiveOut: v6[1], v1[1], v0[1]\l\nv26 := eq(3, v8)\l\ +// Block 13; (9, max 17)\nLiveIn: phi1[1], v1[1], v4[1], v0[1]\l\ +// LiveOut: phi1[1], v1[1], v0[1]\l\nUsed: v4[1]\l\nv8 := eq(0x03, v4)\l\ // "]; // Block1_13 -> Block1_13Exit; -// Block1_13Exit [label="{ If v26 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1_13Exit [label="{ If v8 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_13Exit:0 -> Block1_16 [style="solid"]; // Block1_13Exit:1 -> Block1_15 [style="solid"]; // Block1_15 [label="\ -// Block 15; (10, max 14)\nLiveIn: v6[1], v1[1], v0[1]\l\ -// LiveOut: v6[1], v1[1], v0[1]\l\nsstore(2056, 8)\l\ +// Block 15; (10, max 14)\nLiveIn: phi1[1], v1[1], v0[1]\l\ +// LiveOut: phi1[1], v1[1], v0[1]\l\nUsed: \l\nsstore(0x0808, 0x08)\l\ // "]; // Block1_15 -> Block1_15Exit [arrowhead=none]; // Block1_15Exit [label="Jump" shape=oval]; // Block1_15Exit -> Block1_5 [style="solid"]; // Block1_16 [label="\ -// Block 16; (15, max 17)\nLiveIn: v6[1], v1[1], v0[1]\l\ -// LiveOut: v6[1], v1[1], v0[1]\l\nv30 := mload(v1)\l\ +// Block 16; (15, max 17)\nLiveIn: phi1[1], v1[1], v0[1]\l\ +// LiveOut: phi1[1], v1[1], v0[1]\l\nUsed: \l\nv9 := mload(v1)\l\ // "]; // Block1_16 -> Block1_16Exit; -// Block1_16Exit [label="{ If v30 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1_16Exit [label="{ If v9 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_16Exit:0 -> Block1_18 [style="solid"]; // Block1_16Exit:1 -> Block1_17 [style="solid"]; // Block1_5 [label="\ -// Block 5; (11, max 14)\nLiveIn: v6[1], v1[1], v0[1]\l\ -// LiveOut: v6[1], v1[1], v0[1]\l\nsstore(2827, 11)\l\ +// Block 5; (11, max 14)\nLiveIn: phi1[1], v1[1], v0[1]\l\ +// LiveOut: phi1[1], v1[1], v0[1]\l\nUsed: \l\nsstore(0x0b0b, 0x0b)\l\ // "]; // Block1_5 -> Block1_5Exit [arrowhead=none]; // Block1_5Exit [label="Jump" shape=oval]; // Block1_5Exit -> Block1_3 [style="solid"]; -// Block1_17 [label="\ +// Block1_17 [fillcolor="#FF746C", style=filled, label="\ // Block 17; (16, max 16)\nLiveIn: \l\ -// LiveOut: \l\nreturn(0, 0)\l\ +// LiveOut: \l\nUsed: \l\nreturn(0x00, 0x00)\l\ // "]; // Block1_17Exit [label="Terminated"]; // Block1_17 -> Block1_17Exit; // Block1_18 [label="\ -// Block 18; (17, max 17)\nLiveIn: v6[1], v1[1], v0[1]\l\ -// LiveOut: v6[1], v1[1], v0[1]\l\nsstore(2570, 10)\l\ +// Block 18; (17, max 17)\nLiveIn: phi1[1], v1[1], v0[1]\l\ +// LiveOut: phi1[1], v1[1], v0[1]\l\nUsed: \l\nsstore(0x0a0a, 0x0a)\l\ // "]; // Block1_18 -> Block1_18Exit [arrowhead=none]; // Block1_18Exit [label="Jump" shape=oval]; // Block1_18Exit -> Block1_5 [style="solid"]; // Block1_3 [label="\ -// Block 3; (12, max 14)\nLiveIn: v6[1], v1[1], v0[1]\l\ -// LiveOut: v44[1], v1[1], v0[1]\l\nv44 := add(1, v6)\l\ -// v45 := calldataload(v44)\l\ +// Block 3; (12, max 14)\nLiveIn: phi1[1], v1[1], v0[1]\l\ +// LiveOut: v10[1], v1[1], v0[1]\l\nUsed: phi1[1]\l\nv10 := add(0x01, phi1)\l\ +// v11 := calldataload(v10)\l\ // "]; // Block1_3 -> Block1_3Exit; -// Block1_3Exit [label="{ If v45 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1_3Exit [label="{ If v11 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_3Exit:0 -> Block1_21 [style="solid"]; // Block1_3Exit:1 -> Block1_20 [style="solid"]; // Block1_20 [label="\ -// Block 20; (13, max 13)\nLiveIn: v44[1]\l\ -// LiveOut: \l\nsstore(v44, 0)\l\ +// Block 20; (13, max 13)\nLiveIn: v10[1]\l\ +// LiveOut: \l\nUsed: v10[1]\l\nsstore(v10, 0x00)\l\ // "]; -// Block1_20Exit [label="FunctionReturn[0]"]; +// Block1_20Exit [label="FunctionReturn[0x00]"]; // Block1_20 -> Block1_20Exit; // Block1_21 [label="\ -// Block 21; (14, max 14)\nLiveIn: v44[1], v1[1], v0[1]\l\ -// LiveOut: v44[1], v1[1], v0[1]\l\nsstore(65535, 255)\l\ +// Block 21; (14, max 14)\nLiveIn: v10[1], v1[1], v0[1]\l\ +// LiveOut: v10[1], v1[1], v0[1]\l\nUsed: \l\nsstore(0xffff, 0xff)\l\ // "]; // Block1_21 -> Block1_21Exit [arrowhead=none]; // Block1_21Exit [label="Jump" shape=oval]; diff --git a/test/libyul/yulSSAControlFlowGraph/complex2.yul b/test/libyul/yulSSAControlFlowGraph/complex2.yul index 6e94670baf..9910d8356b 100644 --- a/test/libyul/yulSSAControlFlowGraph/complex2.yul +++ b/test/libyul/yulSSAControlFlowGraph/complex2.yul @@ -58,19 +58,19 @@ // // Entry0 [label="Entry"]; // Entry0 -> Block0_0; -// Block0_0 [label="\ +// Block0_0 [fillcolor="#FF746C", style=filled, label="\ // Block 0; (0, max 0)\nLiveIn: \l\ -// LiveOut: \l\nsstore(1, 1)\l\ -// v2 := f(2, 1)\l\ -// pop(v2)\l\ -// v4 := sload(0)\l\ -// v6 := add(v4, 5)\l\ -// v8 := sload(4)\l\ -// v9 := f(v8, v6)\l\ -// sstore(v9, v6)\l\ -// v10 := sload(5)\l\ -// v11 := f(v10, v9)\l\ -// sstore(v11, 1)\l\ +// LiveOut: \l\nUsed: \l\nsstore(0x01, 0x01)\l\ +// v0 := f(0x02, 0x01)\l\ +// pop(v0)\l\ +// v1 := sload(0x00)\l\ +// v2 := add(v1, 0x05)\l\ +// v3 := sload(0x04)\l\ +// v4 := f(v3, v2)\l\ +// sstore(v4, v2)\l\ +// v5 := sload(0x05)\l\ +// v6 := f(v5, v4)\l\ +// sstore(v6, 0x01)\l\ // "]; // Block0_0Exit [label="MainExit"]; // Block0_0 -> Block0_0Exit; @@ -79,136 +79,136 @@ // FunctionEntry_f_0 -> Block1_0; // Block1_0 [label="\ // Block 0; (0, max 17)\nLiveIn: v1[1], v0[1]\l\ -// LiveOut: v4[1], v1[1], v0[1]\l\nv4 := 42\l\ +// LiveOut: v2[1], v1[1], v0[1]\l\nUsed: \l\nv2 := 0x2a\l\ // "]; // Block1_0 -> Block1_0Exit [arrowhead=none]; // Block1_0Exit [label="Jump" shape=oval]; // Block1_0Exit -> Block1_1 [style="solid"]; // Block1_1 [label="\ -// Block 1; (1, max 17)\nLiveIn: v6[4], v1[1], v0[1]\l\ -// LiveOut: v6[2], v1[1], v0[1]\l\nv6 := φ(\l\ -// Block 0 => v4,\l\ -// Block 21 => v44\l\ +// Block 1; (1, max 17)\nLiveIn: phi1[4], v1[1], v0[1]\l\ +// LiveOut: phi1[2], v1[1], v0[1]\l\nUsed: phi1[2]\l\nphi1 := φ(\l\ +// Block 0 => v2,\l\ +// Block 21 => v10\l\ // )\l\ -// v7 := lt(v0, v6)\l\ +// v3 := lt(v0, phi1)\l\ // "]; // Block1_1 -> Block1_1Exit; -// Block1_1Exit [label="{ If v7 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1_1Exit [label="{ If v3 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_1Exit:0 -> Block1_4 [style="solid"]; // Block1_1Exit:1 -> Block1_2 [style="solid"]; // Block1_2 [label="\ -// Block 2; (2, max 17)\nLiveIn: v6[2], v1[1], v0[1]\l\ -// LiveOut: v6[1], v1[1], v8[3], v0[1]\l\nv8 := mload(v6)\l\ -// v9 := eq(0, v8)\l\ +// Block 2; (2, max 17)\nLiveIn: phi1[2], v1[1], v0[1]\l\ +// LiveOut: phi1[1], v1[1], v4[3], v0[1]\l\nUsed: phi1[1]\l\nv4 := mload(phi1)\l\ +// v5 := eq(0x00, v4)\l\ // "]; // Block1_2 -> Block1_2Exit; -// Block1_2Exit [label="{ If v9 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1_2Exit [label="{ If v5 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_2Exit:0 -> Block1_7 [style="solid"]; // Block1_2Exit:1 -> Block1_6 [style="solid"]; // Block1_4 [label="\ // Block 4; (4, max 4)\nLiveIn: \l\ -// LiveOut: v78[1]\l\nsstore(3084, 12)\l\ -// v78 := 27\l\ +// LiveOut: v12[1]\l\nUsed: \l\nsstore(0x0c0c, 0x0c)\l\ +// v12 := 0x1b\l\ // "]; -// Block1_4Exit [label="FunctionReturn[v78]"]; +// Block1_4Exit [label="FunctionReturn[v12]"]; // Block1_4 -> Block1_4Exit; // Block1_6 [label="\ // Block 6; (3, max 4)\nLiveIn: \l\ -// LiveOut: \l\nsstore(514, 2)\l\ +// LiveOut: \l\nUsed: \l\nsstore(0x0202, 0x02)\l\ // "]; // Block1_6 -> Block1_6Exit [arrowhead=none]; // Block1_6Exit [label="Jump" shape=oval]; // Block1_6Exit -> Block1_4 [style="solid"]; // Block1_7 [label="\ -// Block 7; (5, max 17)\nLiveIn: v6[1], v1[1], v8[3], v0[1]\l\ -// LiveOut: v6[1], v1[1], v8[2], v0[1]\l\nv14 := eq(1, v8)\l\ +// Block 7; (5, max 17)\nLiveIn: phi1[1], v1[1], v4[3], v0[1]\l\ +// LiveOut: phi1[1], v1[1], v4[2], v0[1]\l\nUsed: v4[1]\l\nv6 := eq(0x01, v4)\l\ // "]; // Block1_7 -> Block1_7Exit; -// Block1_7Exit [label="{ If v14 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1_7Exit [label="{ If v6 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_7Exit:0 -> Block1_10 [style="solid"]; // Block1_7Exit:1 -> Block1_9 [style="solid"]; // Block1_9 [label="\ // Block 9; (6, max 6)\nLiveIn: \l\ -// LiveOut: \l\nsstore(1028, 4)\l\ +// LiveOut: \l\nUsed: \l\nsstore(0x0404, 0x04)\l\ // "]; -// Block1_9Exit [label="FunctionReturn[0]"]; +// Block1_9Exit [label="FunctionReturn[0x00]"]; // Block1_9 -> Block1_9Exit; // Block1_10 [label="\ -// Block 10; (7, max 17)\nLiveIn: v6[1], v1[1], v8[2], v0[1]\l\ -// LiveOut: v6[1], v1[1], v8[1], v0[1]\l\nv21 := eq(2, v8)\l\ +// Block 10; (7, max 17)\nLiveIn: phi1[1], v1[1], v4[2], v0[1]\l\ +// LiveOut: phi1[1], v1[1], v4[1], v0[1]\l\nUsed: v4[1]\l\nv7 := eq(0x02, v4)\l\ // "]; // Block1_10 -> Block1_10Exit; -// Block1_10Exit [label="{ If v21 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1_10Exit [label="{ If v7 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_10Exit:0 -> Block1_13 [style="solid"]; // Block1_10Exit:1 -> Block1_12 [style="solid"]; -// Block1_12 [label="\ +// Block1_12 [fillcolor="#FF746C", style=filled, label="\ // Block 12; (8, max 8)\nLiveIn: \l\ -// LiveOut: \l\nsstore(1542, 6)\l\ -// revert(0, 0)\l\ +// LiveOut: \l\nUsed: \l\nsstore(0x0606, 0x06)\l\ +// revert(0x00, 0x00)\l\ // "]; // Block1_12Exit [label="Terminated"]; // Block1_12 -> Block1_12Exit; // Block1_13 [label="\ -// Block 13; (9, max 17)\nLiveIn: v6[1], v1[1], v8[1], v0[1]\l\ -// LiveOut: v6[1], v1[1], v0[1]\l\nv26 := eq(3, v8)\l\ +// Block 13; (9, max 17)\nLiveIn: phi1[1], v1[1], v4[1], v0[1]\l\ +// LiveOut: phi1[1], v1[1], v0[1]\l\nUsed: v4[1]\l\nv8 := eq(0x03, v4)\l\ // "]; // Block1_13 -> Block1_13Exit; -// Block1_13Exit [label="{ If v26 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1_13Exit [label="{ If v8 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_13Exit:0 -> Block1_16 [style="solid"]; // Block1_13Exit:1 -> Block1_15 [style="solid"]; // Block1_15 [label="\ -// Block 15; (10, max 14)\nLiveIn: v6[1], v1[1], v0[1]\l\ -// LiveOut: v6[1], v1[1], v0[1]\l\nsstore(2056, 8)\l\ +// Block 15; (10, max 14)\nLiveIn: phi1[1], v1[1], v0[1]\l\ +// LiveOut: phi1[1], v1[1], v0[1]\l\nUsed: \l\nsstore(0x0808, 0x08)\l\ // "]; // Block1_15 -> Block1_15Exit [arrowhead=none]; // Block1_15Exit [label="Jump" shape=oval]; // Block1_15Exit -> Block1_5 [style="solid"]; // Block1_16 [label="\ -// Block 16; (15, max 17)\nLiveIn: v6[1], v1[1], v0[1]\l\ -// LiveOut: v6[1], v1[1], v0[1]\l\nv30 := mload(v1)\l\ +// Block 16; (15, max 17)\nLiveIn: phi1[1], v1[1], v0[1]\l\ +// LiveOut: phi1[1], v1[1], v0[1]\l\nUsed: \l\nv9 := mload(v1)\l\ // "]; // Block1_16 -> Block1_16Exit; -// Block1_16Exit [label="{ If v30 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1_16Exit [label="{ If v9 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_16Exit:0 -> Block1_18 [style="solid"]; // Block1_16Exit:1 -> Block1_17 [style="solid"]; // Block1_5 [label="\ -// Block 5; (11, max 14)\nLiveIn: v6[1], v1[1], v0[1]\l\ -// LiveOut: v6[1], v1[1], v0[1]\l\nsstore(2827, 11)\l\ +// Block 5; (11, max 14)\nLiveIn: phi1[1], v1[1], v0[1]\l\ +// LiveOut: phi1[1], v1[1], v0[1]\l\nUsed: \l\nsstore(0x0b0b, 0x0b)\l\ // "]; // Block1_5 -> Block1_5Exit [arrowhead=none]; // Block1_5Exit [label="Jump" shape=oval]; // Block1_5Exit -> Block1_3 [style="solid"]; -// Block1_17 [label="\ +// Block1_17 [fillcolor="#FF746C", style=filled, label="\ // Block 17; (16, max 16)\nLiveIn: \l\ -// LiveOut: \l\nreturn(0, 0)\l\ +// LiveOut: \l\nUsed: \l\nreturn(0x00, 0x00)\l\ // "]; // Block1_17Exit [label="Terminated"]; // Block1_17 -> Block1_17Exit; // Block1_18 [label="\ -// Block 18; (17, max 17)\nLiveIn: v6[1], v1[1], v0[1]\l\ -// LiveOut: v6[1], v1[1], v0[1]\l\nsstore(2570, 10)\l\ +// Block 18; (17, max 17)\nLiveIn: phi1[1], v1[1], v0[1]\l\ +// LiveOut: phi1[1], v1[1], v0[1]\l\nUsed: \l\nsstore(0x0a0a, 0x0a)\l\ // "]; // Block1_18 -> Block1_18Exit [arrowhead=none]; // Block1_18Exit [label="Jump" shape=oval]; // Block1_18Exit -> Block1_5 [style="solid"]; // Block1_3 [label="\ -// Block 3; (12, max 14)\nLiveIn: v6[1], v1[1], v0[1]\l\ -// LiveOut: v44[1], v1[1], v0[1]\l\nv44 := add(1, v6)\l\ -// v45 := calldataload(v44)\l\ +// Block 3; (12, max 14)\nLiveIn: phi1[1], v1[1], v0[1]\l\ +// LiveOut: v10[1], v1[1], v0[1]\l\nUsed: phi1[1]\l\nv10 := add(0x01, phi1)\l\ +// v11 := calldataload(v10)\l\ // "]; // Block1_3 -> Block1_3Exit; -// Block1_3Exit [label="{ If v45 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1_3Exit [label="{ If v11 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_3Exit:0 -> Block1_21 [style="solid"]; // Block1_3Exit:1 -> Block1_20 [style="solid"]; // Block1_20 [label="\ -// Block 20; (13, max 13)\nLiveIn: v44[1]\l\ -// LiveOut: \l\nsstore(v44, 0)\l\ +// Block 20; (13, max 13)\nLiveIn: v10[1]\l\ +// LiveOut: \l\nUsed: v10[1]\l\nsstore(v10, 0x00)\l\ // "]; -// Block1_20Exit [label="FunctionReturn[0]"]; +// Block1_20Exit [label="FunctionReturn[0x00]"]; // Block1_20 -> Block1_20Exit; // Block1_21 [label="\ -// Block 21; (14, max 14)\nLiveIn: v44[1], v1[1], v0[1]\l\ -// LiveOut: v44[1], v1[1], v0[1]\l\nsstore(65535, 255)\l\ +// Block 21; (14, max 14)\nLiveIn: v10[1], v1[1], v0[1]\l\ +// LiveOut: v10[1], v1[1], v0[1]\l\nUsed: \l\nsstore(0xffff, 0xff)\l\ // "]; // Block1_21 -> Block1_21Exit [arrowhead=none]; // Block1_21Exit [label="Jump" shape=oval]; diff --git a/test/libyul/yulSSAControlFlowGraph/default_initialized_variable.yul b/test/libyul/yulSSAControlFlowGraph/default_initialized_variable.yul index 799bd94ca0..14a80dadc7 100644 --- a/test/libyul/yulSSAControlFlowGraph/default_initialized_variable.yul +++ b/test/libyul/yulSSAControlFlowGraph/default_initialized_variable.yul @@ -15,27 +15,27 @@ // Entry0 -> Block0_0; // Block0_0 [label="\ // Block 0; (0, max 2)\nLiveIn: \l\ -// LiveOut: v1[1]\l\nv1 := 0\l\ -// v3 := mload(42)\l\ +// LiveOut: v0[1]\l\nUsed: \l\nv0 := 0x00\l\ +// v1 := mload(0x2a)\l\ // "]; // Block0_0 -> Block0_0Exit; -// Block0_0Exit [label="{ If v3 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0_0Exit [label="{ If v1 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block0_0Exit:0 -> Block0_2 [style="solid"]; // Block0_0Exit:1 -> Block0_1 [style="solid"]; // Block0_1 [label="\ // Block 1; (1, max 2)\nLiveIn: \l\ -// LiveOut: v5[1]\l\nv5 := 5\l\ +// LiveOut: v2[1]\l\nUsed: \l\nv2 := 0x05\l\ // "]; // Block0_1 -> Block0_1Exit [arrowhead=none]; // Block0_1Exit [label="Jump" shape=oval]; // Block0_1Exit -> Block0_2 [style="solid"]; // Block0_2 [label="\ -// Block 2; (2, max 2)\nLiveIn: v6[3]\l\ -// LiveOut: \l\nv6 := φ(\l\ -// Block 0 => v1,\l\ -// Block 1 => v5\l\ +// Block 2; (2, max 2)\nLiveIn: phi0[3]\l\ +// LiveOut: \l\nUsed: phi0[3]\l\nphi0 := φ(\l\ +// Block 0 => v0,\l\ +// Block 1 => v2\l\ // )\l\ -// sstore(v6, v6)\l\ +// sstore(phi0, phi0)\l\ // "]; // Block0_2Exit [label="MainExit"]; // Block0_2 -> Block0_2Exit; diff --git a/test/libyul/yulSSAControlFlowGraph/function.yul b/test/libyul/yulSSAControlFlowGraph/function.yul index e125836762..f5090b8265 100644 --- a/test/libyul/yulSSAControlFlowGraph/function.yul +++ b/test/libyul/yulSSAControlFlowGraph/function.yul @@ -26,9 +26,9 @@ // // Entry0 [label="Entry"]; // Entry0 -> Block0_0; -// Block0_0 [label="\ +// Block0_0 [fillcolor="#FF746C", style=filled, label="\ // Block 0; (0, max 0)\nLiveIn: \l\ -// LiveOut: \l\nv0, v1 := i()\l\ +// LiveOut: \l\nUsed: \l\nv0, v1 := i()\l\ // h(v0)\l\ // "]; // Block0_0Exit [label="Terminated"]; @@ -38,27 +38,27 @@ // FunctionEntry_f_0 -> Block1_0; // Block1_0 [label="\ // Block 0; (0, max 0)\nLiveIn: v0[2], v1[1]\l\ -// LiveOut: v4[1]\l\nv3 := add(v1, v0)\l\ -// v4 := sub(v0, v3)\l\ +// LiveOut: v3[1]\l\nUsed: v0[2], v1[1]\l\nv2 := add(v1, v0)\l\ +// v3 := sub(v0, v2)\l\ // "]; -// Block1_0Exit [label="FunctionReturn[v4]"]; +// Block1_0Exit [label="FunctionReturn[v3]"]; // Block1_0 -> Block1_0Exit; // FunctionEntry_g_0 [label="function g: // g()"]; // FunctionEntry_g_0 -> Block2_0; // Block2_0 [label="\ // Block 0; (0, max 0)\nLiveIn: \l\ -// LiveOut: \l\nsstore(257, 1)\l\ +// LiveOut: \l\nUsed: \l\nsstore(0x0101, 0x01)\l\ // "]; // Block2_0Exit [label="FunctionReturn[]"]; // Block2_0 -> Block2_0Exit; // FunctionEntry_h_0 [label="function h: // h(v0)"]; // FunctionEntry_h_0 -> Block3_0; -// Block3_0 [label="\ +// Block3_0 [fillcolor="#FF746C", style=filled, label="\ // Block 0; (0, max 0)\nLiveIn: v0[1]\l\ -// LiveOut: \l\nv2 := f(0, v0)\l\ -// h(v2)\l\ +// LiveOut: \l\nUsed: v0[1]\l\nv1 := f(0x00, v0)\l\ +// h(v1)\l\ // "]; // Block3_0Exit [label="Terminated"]; // Block3_0 -> Block3_0Exit; @@ -67,9 +67,9 @@ // FunctionEntry_i_0 -> Block4_0; // Block4_0 [label="\ // Block 0; (0, max 0)\nLiveIn: \l\ -// LiveOut: v2[1], v4[1]\l\nv2 := 514\l\ -// v4 := 771\l\ +// LiveOut: v0[1], v1[1]\l\nUsed: \l\nv0 := 0x0202\l\ +// v1 := 0x0303\l\ // "]; -// Block4_0Exit [label="FunctionReturn[v2, v4]"]; +// Block4_0Exit [label="FunctionReturn[v0, v1]"]; // Block4_0 -> Block4_0Exit; // } diff --git a/test/libyul/yulSSAControlFlowGraph/if.yul b/test/libyul/yulSSAControlFlowGraph/if.yul index 16fc8957d3..540947e180 100644 --- a/test/libyul/yulSSAControlFlowGraph/if.yul +++ b/test/libyul/yulSSAControlFlowGraph/if.yul @@ -16,28 +16,28 @@ // Entry0 -> Block0_0; // Block0_0 [label="\ // Block 0; (0, max 2)\nLiveIn: \l\ -// LiveOut: v1[1]\l\nv1 := calldataload(3)\l\ -// v3 := mload(42)\l\ +// LiveOut: v0[1]\l\nUsed: \l\nv0 := calldataload(0x03)\l\ +// v1 := mload(0x2a)\l\ // "]; // Block0_0 -> Block0_0Exit; -// Block0_0Exit [label="{ If v3 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0_0Exit [label="{ If v1 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block0_0Exit:0 -> Block0_2 [style="solid"]; // Block0_0Exit:1 -> Block0_1 [style="solid"]; // Block0_1 [label="\ // Block 1; (1, max 2)\nLiveIn: \l\ -// LiveOut: v5[1]\l\nv5 := calldataload(77)\l\ +// LiveOut: v2[1]\l\nUsed: \l\nv2 := calldataload(0x4d)\l\ // "]; // Block0_1 -> Block0_1Exit [arrowhead=none]; // Block0_1Exit [label="Jump" shape=oval]; // Block0_1Exit -> Block0_2 [style="solid"]; // Block0_2 [label="\ -// Block 2; (2, max 2)\nLiveIn: v6[2]\l\ -// LiveOut: \l\nv6 := φ(\l\ -// Block 0 => v1,\l\ -// Block 1 => v5\l\ +// Block 2; (2, max 2)\nLiveIn: phi0[2]\l\ +// LiveOut: \l\nUsed: phi0[2]\l\nphi0 := φ(\l\ +// Block 0 => v0,\l\ +// Block 1 => v2\l\ // )\l\ -// v7 := calldataload(v6)\l\ -// sstore(0, v7)\l\ +// v3 := calldataload(phi0)\l\ +// sstore(0x00, v3)\l\ // "]; // Block0_2Exit [label="MainExit"]; // Block0_2 -> Block0_2Exit; diff --git a/test/libyul/yulSSAControlFlowGraph/if_const.yul b/test/libyul/yulSSAControlFlowGraph/if_const.yul index 26a3c094c5..d8be1eaf2f 100644 --- a/test/libyul/yulSSAControlFlowGraph/if_const.yul +++ b/test/libyul/yulSSAControlFlowGraph/if_const.yul @@ -19,12 +19,12 @@ // // Entry0 [label="Entry"]; // Entry0 -> Block0_0; -// Block0_0 [label="\ +// Block0_0 [fillcolor="#FF746C", style=filled, label="\ // Block 0; (0, max 0)\nLiveIn: \l\ -// LiveOut: \l\nv1 := calldataload(3)\l\ -// v3 := calldataload(42)\l\ -// v4 := calldataload(v3)\l\ -// sstore(0, v4)\l\ +// LiveOut: \l\nUsed: \l\nv0 := calldataload(0x03)\l\ +// v1 := calldataload(0x2a)\l\ +// v2 := calldataload(v1)\l\ +// sstore(0x00, v2)\l\ // "]; // Block0_0Exit [label="MainExit"]; // Block0_0 -> Block0_0Exit; diff --git a/test/libyul/yulSSAControlFlowGraph/nested_for.yul b/test/libyul/yulSSAControlFlowGraph/nested_for.yul index 8d5060951d..e9e5751400 100644 --- a/test/libyul/yulSSAControlFlowGraph/nested_for.yul +++ b/test/libyul/yulSSAControlFlowGraph/nested_for.yul @@ -25,201 +25,201 @@ // // Entry0 [label="Entry"]; // Entry0 -> Block0_0; -// Block0_0 [label="\ +// Block0_0 [fillcolor="#FF746C", style=filled, label="\ // Block 0; (0, max 24)\nLiveIn: \l\ -// LiveOut: v1[1]\l\nv1 := 0\l\ +// LiveOut: v0[1]\l\nUsed: \l\nv0 := 0x00\l\ // "]; // Block0_0 -> Block0_0Exit [arrowhead=none]; // Block0_0Exit [label="Jump" shape=oval]; // Block0_0Exit -> Block0_1 [style="solid"]; -// Block0_1 [label="\ -// Block 1; (1, max 24)\nLiveIn: v3[3]\l\ -// LiveOut: v3[1]\l\nv3 := φ(\l\ -// Block 0 => v1,\l\ -// Block 3 => v43\l\ +// Block0_1 [fillcolor="#FF746C", style=filled, label="\ +// Block 1; (1, max 24)\nLiveIn: phi0[3]\l\ +// LiveOut: phi0[1]\l\nUsed: phi0[2]\l\nphi0 := φ(\l\ +// Block 0 => v0,\l\ +// Block 3 => v20\l\ // )\l\ -// v4 := lt(3, v3)\l\ +// v1 := lt(0x03, phi0)\l\ // "]; // Block0_1 -> Block0_1Exit; -// Block0_1Exit [label="{ If v4 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0_1Exit [label="{ If v1 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block0_1Exit:0 -> Block0_4 [style="solid"]; // Block0_1Exit:1 -> Block0_2 [style="solid"]; // Block0_2 [label="\ -// Block 2; (2, max 23)\nLiveIn: v3[1]\l\ -// LiveOut: v5[1], v3[1]\l\nv5 := 0\l\ +// Block 2; (2, max 23)\nLiveIn: phi0[1]\l\ +// LiveOut: v2[1], phi0[1]\l\nUsed: \l\nv2 := 0x00\l\ // "]; // Block0_2 -> Block0_2Exit [arrowhead=none]; // Block0_2Exit [label="Jump" shape=oval]; // Block0_2Exit -> Block0_5 [style="solid"]; -// Block0_4 [label="\ +// Block0_4 [fillcolor="#FF746C", style=filled, label="\ // Block 4; (24, max 24)\nLiveIn: \l\ -// LiveOut: \l\n"]; +// LiveOut: \l\nUsed: \l\n"]; // Block0_4Exit [label="MainExit"]; // Block0_4 -> Block0_4Exit; // Block0_5 [label="\ -// Block 5; (3, max 23)\nLiveIn: v6[3], v3[1]\l\ -// LiveOut: v6[1], v3[1]\l\nv6 := φ(\l\ -// Block 2 => v5,\l\ -// Block 7 => v42\l\ +// Block 5; (3, max 23)\nLiveIn: phi1[3], phi0[1]\l\ +// LiveOut: phi1[1], phi0[1]\l\nUsed: phi1[2]\l\nphi1 := φ(\l\ +// Block 2 => v2,\l\ +// Block 7 => v19\l\ // )\l\ -// v7 := lt(3, v6)\l\ +// v3 := lt(0x03, phi1)\l\ // "]; // Block0_5 -> Block0_5Exit; -// Block0_5Exit [label="{ If v7 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0_5Exit [label="{ If v3 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block0_5Exit:0 -> Block0_8 [style="solid"]; // Block0_5Exit:1 -> Block0_6 [style="solid"]; // Block0_6 [label="\ -// Block 6; (4, max 21)\nLiveIn: v6[1], v3[1]\l\ -// LiveOut: v8[1], v6[1], v3[1]\l\nv8 := 0\l\ +// Block 6; (4, max 21)\nLiveIn: phi1[1], phi0[1]\l\ +// LiveOut: v4[1], phi1[1], phi0[1]\l\nUsed: \l\nv4 := 0x00\l\ // "]; // Block0_6 -> Block0_6Exit [arrowhead=none]; // Block0_6Exit [label="Jump" shape=oval]; // Block0_6Exit -> Block0_9 [style="solid"]; // Block0_8 [label="\ -// Block 8; (22, max 23)\nLiveIn: v3[1]\l\ -// LiveOut: v3[1]\l\n"]; +// Block 8; (22, max 23)\nLiveIn: phi0[1]\l\ +// LiveOut: phi0[1]\l\nUsed: \l\n"]; // Block0_8 -> Block0_8Exit [arrowhead=none]; // Block0_8Exit [label="Jump" shape=oval]; // Block0_8Exit -> Block0_3 [style="solid"]; // Block0_9 [label="\ -// Block 9; (5, max 21)\nLiveIn: v9[3], v6[1], v3[1]\l\ -// LiveOut: v9[1], v6[1], v3[1]\l\nv9 := φ(\l\ -// Block 6 => v8,\l\ -// Block 11 => v38\l\ +// Block 9; (5, max 21)\nLiveIn: phi2[3], phi1[1], phi0[1]\l\ +// LiveOut: phi2[1], phi1[1], phi0[1]\l\nUsed: phi2[2]\l\nphi2 := φ(\l\ +// Block 6 => v4,\l\ +// Block 11 => v18\l\ // )\l\ -// v10 := lt(3, v9)\l\ +// v5 := lt(0x03, phi2)\l\ // "]; // Block0_9 -> Block0_9Exit; -// Block0_9Exit [label="{ If v10 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0_9Exit [label="{ If v5 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block0_9Exit:0 -> Block0_12 [style="solid"]; // Block0_9Exit:1 -> Block0_10 [style="solid"]; // Block0_3 [label="\ -// Block 3; (23, max 23)\nLiveIn: v3[1]\l\ -// LiveOut: v43[1]\l\nv43 := add(1, v3)\l\ +// Block 3; (23, max 23)\nLiveIn: phi0[1]\l\ +// LiveOut: v20[1]\l\nUsed: phi0[1]\l\nv20 := add(0x01, phi0)\l\ // "]; // Block0_3 -> Block0_3Exit [arrowhead=none]; // Block0_3Exit [label="Jump" shape=oval]; // Block0_3Exit -> Block0_1 [style="dashed"]; // Block0_10 [label="\ -// Block 10; (6, max 19)\nLiveIn: v9[1], v6[1], v3[1]\l\ -// LiveOut: v9[1], v6[1], v3[1]\l\nv11 := mload(0)\l\ +// Block 10; (6, max 19)\nLiveIn: phi2[1], phi1[1], phi0[1]\l\ +// LiveOut: phi2[1], phi1[1], phi0[1]\l\nUsed: \l\nv6 := mload(0x00)\l\ // "]; // Block0_10 -> Block0_10Exit; -// Block0_10Exit [label="{ If v11 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0_10Exit [label="{ If v6 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block0_10Exit:0 -> Block0_14 [style="solid"]; // Block0_10Exit:1 -> Block0_13 [style="solid"]; // Block0_12 [label="\ -// Block 12; (20, max 21)\nLiveIn: v6[1], v3[1]\l\ -// LiveOut: v6[1], v3[1]\l\n"]; +// Block 12; (20, max 21)\nLiveIn: phi1[1], phi0[1]\l\ +// LiveOut: phi1[1], phi0[1]\l\nUsed: \l\n"]; // Block0_12 -> Block0_12Exit [arrowhead=none]; // Block0_12Exit [label="Jump" shape=oval]; // Block0_12Exit -> Block0_7 [style="solid"]; // Block0_13 [label="\ -// Block 13; (7, max 19)\nLiveIn: v9[1], v6[1], v3[1]\l\ -// LiveOut: v12[1], v9[1], v6[1], v3[1]\l\nv12 := 0\l\ +// Block 13; (7, max 19)\nLiveIn: phi2[1], phi1[1], phi0[1]\l\ +// LiveOut: v7[1], phi2[1], phi1[1], phi0[1]\l\nUsed: \l\nv7 := 0x00\l\ // "]; // Block0_13 -> Block0_13Exit [arrowhead=none]; // Block0_13Exit [label="Jump" shape=oval]; // Block0_13Exit -> Block0_15 [style="solid"]; // Block0_14 [label="\ -// Block 14; (12, max 19)\nLiveIn: v9[1], v6[1], v3[1]\l\ -// LiveOut: v9[1], v6[1], v3[1]\l\nv24 := mload(1)\l\ +// Block 14; (12, max 19)\nLiveIn: phi2[1], phi1[1], phi0[1]\l\ +// LiveOut: phi2[1], phi1[1], phi0[1]\l\nUsed: \l\nv12 := mload(0x01)\l\ // "]; // Block0_14 -> Block0_14Exit; -// Block0_14Exit [label="{ If v24 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0_14Exit [label="{ If v12 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block0_14Exit:0 -> Block0_20 [style="solid"]; // Block0_14Exit:1 -> Block0_19 [style="solid"]; // Block0_7 [label="\ -// Block 7; (21, max 21)\nLiveIn: v6[1], v3[1]\l\ -// LiveOut: v42[1], v3[1]\l\nv42 := add(1, v6)\l\ +// Block 7; (21, max 21)\nLiveIn: phi1[1], phi0[1]\l\ +// LiveOut: v19[1], phi0[1]\l\nUsed: phi1[1]\l\nv19 := add(0x01, phi1)\l\ // "]; // Block0_7 -> Block0_7Exit [arrowhead=none]; // Block0_7Exit [label="Jump" shape=oval]; // Block0_7Exit -> Block0_5 [style="dashed"]; // Block0_15 [label="\ -// Block 15; (8, max 19)\nLiveIn: v13[4], v9[1], v6[1], v3[1]\l\ -// LiveOut: v13[2], v9[1], v6[1], v3[1]\l\nv13 := φ(\l\ -// Block 13 => v12,\l\ -// Block 17 => v21\l\ +// Block 15; (8, max 19)\nLiveIn: phi3[4], phi2[1], phi1[1], phi0[1]\l\ +// LiveOut: phi3[2], phi2[1], phi1[1], phi0[1]\l\nUsed: phi3[2]\l\nphi3 := φ(\l\ +// Block 13 => v7,\l\ +// Block 17 => v11\l\ // )\l\ -// v14 := lt(3, v13)\l\ +// v8 := lt(0x03, phi3)\l\ // "]; // Block0_15 -> Block0_15Exit; -// Block0_15Exit [label="{ If v14 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0_15Exit [label="{ If v8 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block0_15Exit:0 -> Block0_18 [style="solid"]; // Block0_15Exit:1 -> Block0_16 [style="solid"]; // Block0_19 [label="\ -// Block 19; (13, max 19)\nLiveIn: v9[1], v6[1], v3[1]\l\ -// LiveOut: v25[1], v9[1], v6[1], v3[1]\l\nv25 := 0\l\ +// Block 19; (13, max 19)\nLiveIn: phi2[1], phi1[1], phi0[1]\l\ +// LiveOut: v13[1], phi2[1], phi1[1], phi0[1]\l\nUsed: \l\nv13 := 0x00\l\ // "]; // Block0_19 -> Block0_19Exit [arrowhead=none]; // Block0_19Exit [label="Jump" shape=oval]; // Block0_19Exit -> Block0_21 [style="solid"]; // Block0_20 [label="\ -// Block 20; (18, max 19)\nLiveIn: v9[1], v6[1], v3[1]\l\ -// LiveOut: v9[1], v6[1], v3[1]\l\n"]; +// Block 20; (18, max 19)\nLiveIn: phi2[1], phi1[1], phi0[1]\l\ +// LiveOut: phi2[1], phi1[1], phi0[1]\l\nUsed: \l\n"]; // Block0_20 -> Block0_20Exit [arrowhead=none]; // Block0_20Exit [label="Jump" shape=oval]; // Block0_20Exit -> Block0_11 [style="solid"]; // Block0_16 [label="\ -// Block 16; (9, max 10)\nLiveIn: v13[2], v9[1], v6[1], v3[1]\l\ -// LiveOut: v13[1], v9[1], v6[1], v3[1]\l\nv18 := add(v6, v3)\l\ -// v19 := add(v9, v18)\l\ -// sstore(v19, v13)\l\ +// Block 16; (9, max 10)\nLiveIn: phi3[2], phi2[1], phi1[1], phi0[1]\l\ +// LiveOut: phi3[1], phi2[1], phi1[1], phi0[1]\l\nUsed: phi3[1]\l\nv9 := add(phi1, phi0)\l\ +// v10 := add(phi2, v9)\l\ +// sstore(v10, phi3)\l\ // "]; // Block0_16 -> Block0_16Exit [arrowhead=none]; // Block0_16Exit [label="Jump" shape=oval]; // Block0_16Exit -> Block0_17 [style="solid"]; // Block0_18 [label="\ -// Block 18; (11, max 19)\nLiveIn: v9[1], v6[1], v3[1]\l\ -// LiveOut: v9[1], v6[1], v3[1]\l\n"]; +// Block 18; (11, max 19)\nLiveIn: phi2[1], phi1[1], phi0[1]\l\ +// LiveOut: phi2[1], phi1[1], phi0[1]\l\nUsed: \l\n"]; // Block0_18 -> Block0_18Exit [arrowhead=none]; // Block0_18Exit [label="Jump" shape=oval]; // Block0_18Exit -> Block0_14 [style="solid"]; // Block0_21 [label="\ -// Block 21; (14, max 19)\nLiveIn: v26[4], v9[1], v6[1], v3[1]\l\ -// LiveOut: v26[2], v9[1], v6[1], v3[1]\l\nv26 := φ(\l\ -// Block 19 => v25,\l\ -// Block 23 => v33\l\ +// Block 21; (14, max 19)\nLiveIn: phi9[4], phi2[1], phi1[1], phi0[1]\l\ +// LiveOut: phi9[2], phi2[1], phi1[1], phi0[1]\l\nUsed: phi9[2]\l\nphi9 := φ(\l\ +// Block 19 => v13,\l\ +// Block 23 => v17\l\ // )\l\ -// v27 := lt(3, v26)\l\ +// v14 := lt(0x03, phi9)\l\ // "]; // Block0_21 -> Block0_21Exit; -// Block0_21Exit [label="{ If v27 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0_21Exit [label="{ If v14 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block0_21Exit:0 -> Block0_24 [style="solid"]; // Block0_21Exit:1 -> Block0_22 [style="solid"]; // Block0_11 [label="\ -// Block 11; (19, max 19)\nLiveIn: v9[1], v6[1], v3[1]\l\ -// LiveOut: v38[1], v6[1], v3[1]\l\nv38 := add(1, v9)\l\ +// Block 11; (19, max 19)\nLiveIn: phi2[1], phi1[1], phi0[1]\l\ +// LiveOut: v18[1], phi1[1], phi0[1]\l\nUsed: phi2[1]\l\nv18 := add(0x01, phi2)\l\ // "]; // Block0_11 -> Block0_11Exit [arrowhead=none]; // Block0_11Exit [label="Jump" shape=oval]; // Block0_11Exit -> Block0_9 [style="dashed"]; // Block0_17 [label="\ -// Block 17; (10, max 10)\nLiveIn: v13[1], v9[1], v6[1], v3[1]\l\ -// LiveOut: v21[1], v9[1], v6[1], v3[1]\l\nv21 := add(1, v13)\l\ +// Block 17; (10, max 10)\nLiveIn: phi3[1], phi2[1], phi1[1], phi0[1]\l\ +// LiveOut: v11[1], phi2[1], phi1[1], phi0[1]\l\nUsed: phi3[1]\l\nv11 := add(0x01, phi3)\l\ // "]; // Block0_17 -> Block0_17Exit [arrowhead=none]; // Block0_17Exit [label="Jump" shape=oval]; // Block0_17Exit -> Block0_15 [style="dashed"]; // Block0_22 [label="\ -// Block 22; (15, max 16)\nLiveIn: v26[2], v9[1], v6[1], v3[1]\l\ -// LiveOut: v26[1], v9[1], v6[1], v3[1]\l\nv31 := add(v6, v3)\l\ -// v32 := add(v9, v31)\l\ -// sstore(v32, v26)\l\ +// Block 22; (15, max 16)\nLiveIn: phi9[2], phi2[1], phi1[1], phi0[1]\l\ +// LiveOut: phi9[1], phi2[1], phi1[1], phi0[1]\l\nUsed: phi9[1]\l\nv15 := add(phi1, phi0)\l\ +// v16 := add(phi2, v15)\l\ +// sstore(v16, phi9)\l\ // "]; // Block0_22 -> Block0_22Exit [arrowhead=none]; // Block0_22Exit [label="Jump" shape=oval]; // Block0_22Exit -> Block0_23 [style="solid"]; // Block0_24 [label="\ -// Block 24; (17, max 19)\nLiveIn: v9[1], v6[1], v3[1]\l\ -// LiveOut: v9[1], v6[1], v3[1]\l\n"]; +// Block 24; (17, max 19)\nLiveIn: phi2[1], phi1[1], phi0[1]\l\ +// LiveOut: phi2[1], phi1[1], phi0[1]\l\nUsed: \l\n"]; // Block0_24 -> Block0_24Exit [arrowhead=none]; // Block0_24Exit [label="Jump" shape=oval]; // Block0_24Exit -> Block0_20 [style="solid"]; // Block0_23 [label="\ -// Block 23; (16, max 16)\nLiveIn: v26[1], v9[1], v6[1], v3[1]\l\ -// LiveOut: v33[1], v9[1], v6[1], v3[1]\l\nv33 := add(1, v26)\l\ +// Block 23; (16, max 16)\nLiveIn: phi9[1], phi2[1], phi1[1], phi0[1]\l\ +// LiveOut: v17[1], phi2[1], phi1[1], phi0[1]\l\nUsed: phi9[1]\l\nv17 := add(0x01, phi9)\l\ // "]; // Block0_23 -> Block0_23Exit [arrowhead=none]; // Block0_23Exit [label="Jump" shape=oval]; diff --git a/test/libyul/yulSSAControlFlowGraph/nested_function.yul b/test/libyul/yulSSAControlFlowGraph/nested_function.yul index 4387e54aa9..2ae7f5522b 100644 --- a/test/libyul/yulSSAControlFlowGraph/nested_function.yul +++ b/test/libyul/yulSSAControlFlowGraph/nested_function.yul @@ -37,9 +37,9 @@ // // Entry0 [label="Entry"]; // Entry0 -> Block0_0; -// Block0_0 [label="\ +// Block0_0 [fillcolor="#FF746C", style=filled, label="\ // Block 0; (0, max 0)\nLiveIn: \l\ -// LiveOut: \l\n"]; +// LiveOut: \l\nUsed: \l\n"]; // Block0_0Exit [label="MainExit"]; // Block0_0 -> Block0_0Exit; // FunctionEntry_f_0 [label="function f: @@ -47,20 +47,20 @@ // FunctionEntry_f_0 -> Block1_0; // Block1_0 [label="\ // Block 0; (0, max 0)\nLiveIn: v0[2], v1[1]\l\ -// LiveOut: v4[1]\l\nv3 := add(v1, v0)\l\ -// v4 := sub(v0, v3)\l\ +// LiveOut: v3[1]\l\nUsed: v0[2], v1[1]\l\nv2 := add(v1, v0)\l\ +// v3 := sub(v0, v2)\l\ // "]; -// Block1_0Exit [label="FunctionReturn[v4]"]; +// Block1_0Exit [label="FunctionReturn[v3]"]; // Block1_0 -> Block1_0Exit; // FunctionEntry_g_0 [label="function g: // g()"]; // FunctionEntry_g_0 -> Block2_0; // Block2_0 [label="\ // Block 0; (0, max 0)\nLiveIn: \l\ -// LiveOut: \l\nv1 := v()\l\ -// v2 := f(2, v1)\l\ -// v3 := z()\l\ -// sstore(v2, v3)\l\ +// LiveOut: \l\nUsed: \l\nv0 := v()\l\ +// v1 := f(0x02, v0)\l\ +// v2 := z()\l\ +// sstore(v1, v2)\l\ // "]; // Block2_0Exit [label="FunctionReturn[]"]; // Block2_0 -> Block2_0Exit; @@ -69,89 +69,89 @@ // FunctionEntry_z_0 -> Block3_0; // Block3_0 [label="\ // Block 0; (0, max 0)\nLiveIn: \l\ -// LiveOut: v1[1]\l\nv1 := w()\l\ +// LiveOut: v0[1]\l\nUsed: \l\nv0 := w()\l\ // "]; -// Block3_0Exit [label="FunctionReturn[v1]"]; +// Block3_0Exit [label="FunctionReturn[v0]"]; // Block3_0 -> Block3_0Exit; // FunctionEntry_w_0 [label="function w: // rw1 := w()"]; // FunctionEntry_w_0 -> Block4_0; // Block4_0 [label="\ // Block 0; (0, max 0)\nLiveIn: \l\ -// LiveOut: v2[1]\l\nv2 := 1\l\ +// LiveOut: v0[1]\l\nUsed: \l\nv0 := 0x01\l\ // "]; -// Block4_0Exit [label="FunctionReturn[v2]"]; +// Block4_0Exit [label="FunctionReturn[v0]"]; // Block4_0 -> Block4_0Exit; // FunctionEntry_v_0 [label="function v: // r := v()"]; // FunctionEntry_v_0 -> Block5_0; // Block5_0 [label="\ // Block 0; (0, max 0)\nLiveIn: \l\ -// LiveOut: v1[1]\l\nv1 := w()\l\ +// LiveOut: v0[1]\l\nUsed: \l\nv0 := w()\l\ // "]; -// Block5_0Exit [label="FunctionReturn[v1]"]; +// Block5_0Exit [label="FunctionReturn[v0]"]; // Block5_0 -> Block5_0Exit; // FunctionEntry_w_0 [label="function w: // rw2 := w()"]; // FunctionEntry_w_0 -> Block6_0; // Block6_0 [label="\ // Block 0; (0, max 0)\nLiveIn: \l\ -// LiveOut: v2[1]\l\nv2 := 17\l\ +// LiveOut: v0[1]\l\nUsed: \l\nv0 := 0x11\l\ // "]; -// Block6_0Exit [label="FunctionReturn[v2]"]; +// Block6_0Exit [label="FunctionReturn[v0]"]; // Block6_0 -> Block6_0Exit; // FunctionEntry_cycle1_0 [label="function cycle1: // r := cycle1()"]; // FunctionEntry_cycle1_0 -> Block7_0; // Block7_0 [label="\ // Block 0; (0, max 2)\nLiveIn: \l\ -// LiveOut: \l\nv2 := mload(3)\l\ +// LiveOut: \l\nUsed: \l\nv0 := mload(0x03)\l\ // "]; // Block7_0 -> Block7_0Exit; -// Block7_0Exit [label="{ If v2 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block7_0Exit [label="{ If v0 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block7_0Exit:0 -> Block7_2 [style="solid"]; // Block7_0Exit:1 -> Block7_1 [style="solid"]; // Block7_1 [label="\ // Block 1; (1, max 2)\nLiveIn: \l\ -// LiveOut: v3[1]\l\nv3 := cycle2()\l\ +// LiveOut: v1[1]\l\nUsed: \l\nv1 := cycle2()\l\ // "]; // Block7_1 -> Block7_1Exit [arrowhead=none]; // Block7_1Exit [label="Jump" shape=oval]; // Block7_1Exit -> Block7_2 [style="solid"]; // Block7_2 [label="\ -// Block 2; (2, max 2)\nLiveIn: v4[3]\l\ -// LiveOut: v4[1]\l\nv4 := φ(\l\ -// Block 0 => 0,\l\ -// Block 1 => v3\l\ +// Block 2; (2, max 2)\nLiveIn: phi0[3]\l\ +// LiveOut: phi0[1]\l\nUsed: phi0[2]\l\nphi0 := φ(\l\ +// Block 0 => 0x00,\l\ +// Block 1 => v1\l\ // )\l\ // "]; -// Block7_2Exit [label="FunctionReturn[v4]"]; +// Block7_2Exit [label="FunctionReturn[phi0]"]; // Block7_2 -> Block7_2Exit; // FunctionEntry_cycle2_0 [label="function cycle2: // r := cycle2()"]; // FunctionEntry_cycle2_0 -> Block8_0; // Block8_0 [label="\ // Block 0; (0, max 2)\nLiveIn: \l\ -// LiveOut: \l\nv2 := mload(4)\l\ +// LiveOut: \l\nUsed: \l\nv0 := mload(0x04)\l\ // "]; // Block8_0 -> Block8_0Exit; -// Block8_0Exit [label="{ If v2 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block8_0Exit [label="{ If v0 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block8_0Exit:0 -> Block8_2 [style="solid"]; // Block8_0Exit:1 -> Block8_1 [style="solid"]; // Block8_1 [label="\ // Block 1; (1, max 2)\nLiveIn: \l\ -// LiveOut: v3[1]\l\nv3 := cycle1()\l\ +// LiveOut: v1[1]\l\nUsed: \l\nv1 := cycle1()\l\ // "]; // Block8_1 -> Block8_1Exit [arrowhead=none]; // Block8_1Exit [label="Jump" shape=oval]; // Block8_1Exit -> Block8_2 [style="solid"]; // Block8_2 [label="\ -// Block 2; (2, max 2)\nLiveIn: v4[3]\l\ -// LiveOut: v4[1]\l\nv4 := φ(\l\ -// Block 0 => 0,\l\ -// Block 1 => v3\l\ +// Block 2; (2, max 2)\nLiveIn: phi0[3]\l\ +// LiveOut: phi0[1]\l\nUsed: phi0[2]\l\nphi0 := φ(\l\ +// Block 0 => 0x00,\l\ +// Block 1 => v1\l\ // )\l\ // "]; -// Block8_2Exit [label="FunctionReturn[v4]"]; +// Block8_2Exit [label="FunctionReturn[phi0]"]; // Block8_2 -> Block8_2Exit; // } diff --git a/test/libyul/yulSSAControlFlowGraph/switch.yul b/test/libyul/yulSSAControlFlowGraph/switch.yul index 5a670467e0..56e50d5629 100644 --- a/test/libyul/yulSSAControlFlowGraph/switch.yul +++ b/test/libyul/yulSSAControlFlowGraph/switch.yul @@ -23,50 +23,50 @@ // Entry0 -> Block0_0; // Block0_0 [label="\ // Block 0; (0, max 5)\nLiveIn: \l\ -// LiveOut: v3[1]\l\nv1 := calldataload(3)\l\ -// v3 := sload(0)\l\ -// v4 := eq(0, v3)\l\ +// LiveOut: v1[1]\l\nUsed: \l\nv0 := calldataload(0x03)\l\ +// v1 := sload(0x00)\l\ +// v2 := eq(0x00, v1)\l\ // "]; // Block0_0 -> Block0_0Exit; -// Block0_0Exit [label="{ If v4 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0_0Exit [label="{ If v2 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block0_0Exit:0 -> Block0_3 [style="solid"]; // Block0_0Exit:1 -> Block0_2 [style="solid"]; // Block0_2 [label="\ // Block 2; (1, max 2)\nLiveIn: \l\ -// LiveOut: v6[1]\l\nv6 := calldataload(77)\l\ +// LiveOut: v3[1]\l\nUsed: \l\nv3 := calldataload(0x4d)\l\ // "]; // Block0_2 -> Block0_2Exit [arrowhead=none]; // Block0_2Exit [label="Jump" shape=oval]; // Block0_2Exit -> Block0_1 [style="solid"]; // Block0_3 [label="\ -// Block 3; (3, max 5)\nLiveIn: v3[1]\l\ -// LiveOut: \l\nv7 := eq(1, v3)\l\ +// Block 3; (3, max 5)\nLiveIn: v1[1]\l\ +// LiveOut: \l\nUsed: v1[1]\l\nv4 := eq(0x01, v1)\l\ // "]; // Block0_3 -> Block0_3Exit; -// Block0_3Exit [label="{ If v7 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0_3Exit [label="{ If v4 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block0_3Exit:0 -> Block0_5 [style="solid"]; // Block0_3Exit:1 -> Block0_4 [style="solid"]; // Block0_1 [label="\ -// Block 1; (2, max 2)\nLiveIn: v13[2]\l\ -// LiveOut: \l\nv13 := φ(\l\ -// Block 2 => v6,\l\ -// Block 4 => v10,\l\ -// Block 5 => v12\l\ +// Block 1; (2, max 2)\nLiveIn: phi0[2]\l\ +// LiveOut: \l\nUsed: phi0[2]\l\nphi0 := φ(\l\ +// Block 2 => v3,\l\ +// Block 4 => v5,\l\ +// Block 5 => v6\l\ // )\l\ -// sstore(0, v13)\l\ +// sstore(0x00, phi0)\l\ // "]; // Block0_1Exit [label="MainExit"]; // Block0_1 -> Block0_1Exit; // Block0_4 [label="\ // Block 4; (4, max 4)\nLiveIn: \l\ -// LiveOut: v10[1]\l\nv10 := calldataload(88)\l\ +// LiveOut: v5[1]\l\nUsed: \l\nv5 := calldataload(0x58)\l\ // "]; // Block0_4 -> Block0_4Exit [arrowhead=none]; // Block0_4Exit [label="Jump" shape=oval]; // Block0_4Exit -> Block0_1 [style="solid"]; // Block0_5 [label="\ // Block 5; (5, max 5)\nLiveIn: \l\ -// LiveOut: v12[1]\l\nv12 := calldataload(99)\l\ +// LiveOut: v6[1]\l\nUsed: \l\nv6 := calldataload(0x63)\l\ // "]; // Block0_5 -> Block0_5Exit [arrowhead=none]; // Block0_5Exit [label="Jump" shape=oval]; diff --git a/test/libyul/yulSSAControlFlowGraph/switch_const.yul b/test/libyul/yulSSAControlFlowGraph/switch_const.yul index 3de0537dc5..559061195a 100644 --- a/test/libyul/yulSSAControlFlowGraph/switch_const.yul +++ b/test/libyul/yulSSAControlFlowGraph/switch_const.yul @@ -43,12 +43,12 @@ // // Entry0 [label="Entry"]; // Entry0 -> Block0_0; -// Block0_0 [label="\ +// Block0_0 [fillcolor="#FF746C", style=filled, label="\ // Block 0; (0, max 0)\nLiveIn: \l\ -// LiveOut: \l\nv1 := calldataload(3)\l\ -// v4 := calldataload(88)\l\ -// v7 := calldataload(99)\l\ -// sstore(0, v7)\l\ +// LiveOut: \l\nUsed: \l\nv0 := calldataload(0x03)\l\ +// v1 := calldataload(0x58)\l\ +// v2 := calldataload(0x63)\l\ +// sstore(0x00, v2)\l\ // "]; // Block0_0Exit [label="MainExit"]; // Block0_0 -> Block0_0Exit; From d5d29bd78f54c2b0445d8c46d14eeb69b47576e3 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 20 Oct 2025 14:30:46 +0200 Subject: [PATCH 065/243] SSACFG: exporting to json is stateless --- libyul/YulStack.cpp | 3 +- .../backends/evm/ssa/SSACFGJsonExporter.cpp | 249 +++++----- libyul/backends/evm/ssa/SSACFGJsonExporter.h | 20 +- .../standard_yul_cfg_json_export/output.json | 444 +++++++++--------- .../strict_asm_yul_cfg_json_export/output | 58 +-- test/cmdlineTests/yul_cfg_json_export/output | 444 +++++++++--------- 6 files changed, 590 insertions(+), 628 deletions(-) diff --git a/libyul/YulStack.cpp b/libyul/YulStack.cpp index 75b39134f7..34de47b929 100644 --- a/libyul/YulStack.cpp +++ b/libyul/YulStack.cpp @@ -405,8 +405,7 @@ Json YulStack::cfgJson() const keepLiteralAssignments ); std::unique_ptr liveness = std::make_unique(*controlFlow); - ssa::SSACFGJsonExporter exporter(*controlFlow, liveness.get()); - return exporter.run(); + return ssa::json::exportControlFlow(*controlFlow, liveness.get()); }; std::function>)> exportCFGFromSubObjects; diff --git a/libyul/backends/evm/ssa/SSACFGJsonExporter.cpp b/libyul/backends/evm/ssa/SSACFGJsonExporter.cpp index 68f65d377e..9b03c13c95 100644 --- a/libyul/backends/evm/ssa/SSACFGJsonExporter.cpp +++ b/libyul/backends/evm/ssa/SSACFGJsonExporter.cpp @@ -17,73 +17,119 @@ // SPDX-License-Identifier: GPL-3.0 #include + #include #include #include +#include +#include #include -#include #include using namespace solidity; +using namespace solidity::yul; using namespace solidity::yul::ssa; +using namespace solidity::yul::ssa::json; -SSACFGJsonExporter::SSACFGJsonExporter(ControlFlow const& _controlFlow, ControlFlowLiveness const* _liveness): m_controlFlow(_controlFlow), m_liveness(_liveness) +namespace +{ +Json toJson(SSACFG const& _cfg, std::vector const& _values) { + Json ret = Json::array(); + for (auto const& value: _values) + ret.push_back(value.str(_cfg)); + return ret; } -std::string SSACFGJsonExporter::varToString(SSACFG const& _cfg, SSACFG::ValueId _var) +Json toJson(Json& _ret, SSACFG const& _cfg, SSACFG::Operation const& _operation) { - if (_var.value == std::numeric_limits::max()) - return std::string("INVALID"); - auto const& info = _cfg.valueInfo(_var); - return std::visit( - util::GenericVisitor{ - [&](SSACFG::UnreachableValue const&) -> std::string { - return "[unreachable]"; - }, - [&](SSACFG::LiteralValue const& _literal) { - return toCompactHexWithPrefix(_literal.value); - }, - [&](auto const&) { - return "v" + std::to_string(_var.value); - } + Json opJson = Json::object(); + std::visit(util::GenericVisitor{ + [&](SSACFG::Call const& _call) { + _ret["type"] = "FunctionCall"; + opJson["op"] = _call.function.get().name.str(); }, - info - ); -} + [&](SSACFG::LiteralAssignment const&) { + yulAssert(_operation.inputs.size() == 1); + yulAssert(_operation.inputs.back().isLiteral()); + opJson["op"] = "LiteralAssignment"; + }, + [&](SSACFG::BuiltinCall const& _call) { + _ret["type"] = "BuiltinCall"; + Json builtinArgsJson = Json::array(); + auto const& builtin = _call.builtin.get(); + if (!builtin.literalArguments.empty()) + { + auto const& functionCallArgs = _call.call.get().arguments; + for (size_t i = 0; i < builtin.literalArguments.size(); ++i) + { + std::optional const& argument = builtin.literalArguments[i]; + if (argument.has_value() && i < functionCallArgs.size()) + { + // The function call argument at index i must be a literal if builtin.literalArguments[i] is not nullopt + yulAssert(std::holds_alternative(functionCallArgs[i])); + builtinArgsJson.push_back(formatLiteral(std::get(functionCallArgs[i]))); + } + } + } -Json SSACFGJsonExporter::run() -{ - if (m_liveness) - yulAssert(&m_liveness->controlFlow.get() == &m_controlFlow); + if (!builtinArgsJson.empty()) + opJson["literalArgs"] = builtinArgsJson; - Json yulObjectJson = Json::object(); - yulObjectJson["blocks"] = exportBlock(*m_controlFlow.mainGraph, SSACFG::BlockId{0}, m_liveness ? m_liveness->mainLiveness.get() : nullptr); + opJson["op"] = _call.builtin.get().name; + }, + }, _operation.kind); - Json functionsJson = Json::object(); - size_t index = 0; - for (auto const& [function, functionGraph]: m_controlFlow.functionGraphMapping) - functionsJson[function->name.str()] = exportFunction(*functionGraph, m_liveness ? m_liveness->functionLiveness[index++].get() : nullptr); - yulObjectJson["functions"] = functionsJson; + opJson["in"] = toJson(_cfg, _operation.inputs); + opJson["out"] = toJson(_cfg, _operation.outputs); - return yulObjectJson; + return opJson; } -Json SSACFGJsonExporter::exportFunction(SSACFG const& _cfg, LivenessAnalysis const* _liveness) +Json toJson(SSACFG const& _cfg, SSACFG::BlockId _blockId, LivenessAnalysis const* _liveness) { - Json functionJson = Json::object(); - functionJson["type"] = "Function"; - functionJson["entry"] = "Block" + std::to_string(_cfg.entry.value); - static auto constexpr argsTransform = [](auto const& _arg) { return fmt::format("v{}", std::get<1>(_arg).value); }; - functionJson["arguments"] = _cfg.arguments | ranges::views::transform(argsTransform) | ranges::to; - functionJson["numReturns"] = _cfg.returns.size(); - functionJson["blocks"] = exportBlock(_cfg, _cfg.entry, _liveness); - return functionJson; + auto const valueToString = [&](LivenessAnalysis::LivenessData::LiveCounts::value_type const& _live) { return _live.first.str(_cfg); }; + + Json blockJson = Json::object(); + auto const& block = _cfg.block(_blockId); + + blockJson["id"] = "Block" + std::to_string(_blockId.value); + if (_liveness) + { + Json livenessJson = Json::object(); + livenessJson["in"] = _liveness->liveIn(_blockId) + | ranges::views::transform(valueToString) + | ranges::to(); + livenessJson["out"] = _liveness->liveOut(_blockId) + | ranges::views::transform(valueToString) + | ranges::to(); + blockJson["liveness"] = livenessJson; + } + blockJson["instructions"] = Json::array(); + if (!block.phis.empty()) + { + blockJson["entries"] = block.entries + | ranges::views::transform([](auto const& entry) { return "Block" + std::to_string(entry.value); }) + | ranges::to(); + for (auto const& phi: block.phis) + { + auto const& phiInfo = _cfg.phiInfo(phi); + Json phiJson = Json::object(); + phiJson["op"] = "PhiFunction"; + phiJson["in"] = toJson(_cfg, phiInfo.arguments); + phiJson["out"] = toJson(_cfg, std::vector{phi}); + blockJson["instructions"].push_back(phiJson); + } + } + for (auto const& operation: block.operations) + blockJson["instructions"].push_back(toJson(blockJson, _cfg, operation)); + + return blockJson; } -Json SSACFGJsonExporter::exportBlock(SSACFG const& _cfg, SSACFG::BlockId _entryId, LivenessAnalysis const* _liveness) +Json exportBlock(SSACFG const& _cfg, SSACFG::BlockId _entryId, LivenessAnalysis const* _liveness) { Json blocksJson = Json::array(); util::BreadthFirstSearch bfs{{{_entryId}}}; @@ -97,16 +143,14 @@ Json SSACFGJsonExporter::exportBlock(SSACFG const& _cfg, SSACFG::BlockId _entryI [&](SSACFG::BasicBlock::MainExit const&) { exitBlockJson["type"] = "MainExit"; }, - [&](SSACFG::BasicBlock::Jump const& _jump) - { + [&](SSACFG::BasicBlock::Jump const& _jump){ exitBlockJson["targets"] = { "Block" + std::to_string(_jump.target.value) }; exitBlockJson["type"] = "Jump"; _addChild(_jump.target); }, - [&](SSACFG::BasicBlock::ConditionalJump const& _conditionalJump) - { + [&](SSACFG::BasicBlock::ConditionalJump const& _conditionalJump) { exitBlockJson["targets"] = { "Block" + std::to_string(_conditionalJump.zero.value), "Block" + std::to_string(_conditionalJump.nonZero.value) }; - exitBlockJson["cond"] = varToString(_cfg, _conditionalJump.condition); + exitBlockJson["cond"] = _conditionalJump.condition.str(_cfg); exitBlockJson["type"] = "ConditionalJump"; _addChild(_conditionalJump.zero); @@ -130,100 +174,35 @@ Json SSACFGJsonExporter::exportBlock(SSACFG const& _cfg, SSACFG::BlockId _entryI return blocksJson; } -Json SSACFGJsonExporter::toJson(SSACFG const& _cfg, SSACFG::BlockId _blockId, LivenessAnalysis const* _liveness) +Json exportFunction(SSACFG const& _cfg, LivenessAnalysis const* _liveness) { - auto const valueToString = [&](LivenessAnalysis::LivenessData::LiveCounts::value_type const& _live) { return varToString(_cfg, _live.first); }; - - Json blockJson = Json::object(); - auto const& block = _cfg.block(_blockId); - - blockJson["id"] = "Block" + std::to_string(_blockId.value); - if (_liveness) - { - Json livenessJson = Json::object(); - livenessJson["in"] = _liveness->liveIn(_blockId) - | ranges::views::transform(valueToString) - | ranges::to(); - livenessJson["out"] = _liveness->liveOut(_blockId) - | ranges::views::transform(valueToString) - | ranges::to(); - blockJson["liveness"] = livenessJson; - } - blockJson["instructions"] = Json::array(); - if (!block.phis.empty()) - { - blockJson["entries"] = block.entries - | ranges::views::transform([](auto const& entry) { return "Block" + std::to_string(entry.value); }) - | ranges::to(); - for (auto const& phi: block.phis) - { - auto* phiInfo = std::get_if(&_cfg.valueInfo(phi)); - yulAssert(phiInfo); - Json phiJson = Json::object(); - phiJson["op"] = "PhiFunction"; - phiJson["in"] = toJson(_cfg, phiInfo->arguments); - phiJson["out"] = toJson(_cfg, std::vector{phi}); - blockJson["instructions"].push_back(phiJson); - } - } - for (auto const& operation: block.operations) - blockJson["instructions"].push_back(toJson(blockJson, _cfg, operation)); + Json functionJson = Json::object(); + functionJson["type"] = "Function"; + functionJson["entry"] = "Block" + std::to_string(_cfg.entry.value); + static auto constexpr argsTransform = [](auto const& _arg) { return fmt::format("v{}", std::get<1>(_arg).value()); }; + functionJson["arguments"] = _cfg.arguments | ranges::views::transform(argsTransform) | ranges::to; + functionJson["numReturns"] = _cfg.returns.size(); + functionJson["blocks"] = exportBlock(_cfg, _cfg.entry, _liveness); + return functionJson; +} - return blockJson; } -Json SSACFGJsonExporter::toJson(Json& _ret, SSACFG const& _cfg, SSACFG::Operation const& _operation) +Json json::exportControlFlow(ControlFlow const& _controlFlow, ControlFlowLiveness const* _liveness) { - Json opJson = Json::object(); - std::visit(util::GenericVisitor{ - [&](SSACFG::Call const& _call) - { - _ret["type"] = "FunctionCall"; - opJson["op"] = _call.function.get().name.str(); - }, - [&](SSACFG::LiteralAssignment const&) - { - yulAssert(_operation.inputs.size() == 1); - yulAssert(_cfg.isLiteralValue(_operation.inputs.back())); - opJson["op"] = "LiteralAssignment"; - }, - [&](SSACFG::BuiltinCall const& _call) - { - _ret["type"] = "BuiltinCall"; - Json builtinArgsJson = Json::array(); - auto const& builtin = _call.builtin.get(); - if (!builtin.literalArguments.empty()) - { - auto const& functionCallArgs = _call.call.get().arguments; - for (size_t i = 0; i < builtin.literalArguments.size(); ++i) - { - std::optional const& argument = builtin.literalArguments[i]; - if (argument.has_value() && i < functionCallArgs.size()) - { - // The function call argument at index i must be a literal if builtin.literalArguments[i] is not nullopt - yulAssert(std::holds_alternative(functionCallArgs[i])); - builtinArgsJson.push_back(formatLiteral(std::get(functionCallArgs[i]))); - } - } - } - - if (!builtinArgsJson.empty()) - opJson["literalArgs"] = builtinArgsJson; - - opJson["op"] = _call.builtin.get().name; - }, - }, _operation.kind); + if (_liveness) + yulAssert(&_liveness->controlFlow.get() == &_controlFlow); - opJson["in"] = toJson(_cfg, _operation.inputs); - opJson["out"] = toJson(_cfg, _operation.outputs); + Json yulObjectJson = Json::object(); + yulObjectJson["blocks"] = exportBlock(*_controlFlow.mainGraph(), SSACFG::BlockId{0}, _liveness ? _liveness->cfgLiveness.front().get() : nullptr); - return opJson; -} + Json functionsJson = Json::object(); + size_t index = 1; + for (auto const& [function, functionGraph]: _controlFlow.functionGraphMapping | ranges::views::drop(1)) + { + functionsJson[function->name.str()] = exportFunction(*functionGraph, _liveness ? _liveness->cfgLiveness[index++].get() : nullptr); + } + yulObjectJson["functions"] = functionsJson; -Json SSACFGJsonExporter::toJson(SSACFG const& _cfg, std::vector const& _values) -{ - Json ret = Json::array(); - for (auto const& value: _values) - ret.push_back(varToString(_cfg, value)); - return ret; + return yulObjectJson; } diff --git a/libyul/backends/evm/ssa/SSACFGJsonExporter.h b/libyul/backends/evm/ssa/SSACFGJsonExporter.h index 7198595812..08f846fc5e 100644 --- a/libyul/backends/evm/ssa/SSACFGJsonExporter.h +++ b/libyul/backends/evm/ssa/SSACFGJsonExporter.h @@ -20,26 +20,10 @@ #include #include -#include -namespace solidity::yul::ssa +namespace solidity::yul::ssa::json { -class SSACFGJsonExporter -{ -public: - SSACFGJsonExporter(ControlFlow const& _controlFlow, ControlFlowLiveness const* _liveness=nullptr); - Json run(); - Json exportBlock(SSACFG const& _cfg, SSACFG::BlockId _blockId, LivenessAnalysis const* _liveness); - Json exportFunction(SSACFG const& _cfg, LivenessAnalysis const* _liveness); - std::string varToString(SSACFG const& _cfg, SSACFG::ValueId _var); - -private: - ControlFlow const& m_controlFlow; - ControlFlowLiveness const* m_liveness; - Json toJson(SSACFG const& _cfg, SSACFG::BlockId _blockId, LivenessAnalysis const* _liveness); - Json toJson(Json& _ret, SSACFG const& _cfg, SSACFG::Operation const& _operation); - Json toJson(SSACFG const& _cfg, std::vector const& _values); -}; +Json exportControlFlow(ControlFlow const& _controlFlow, ControlFlowLiveness const* _liveness); } diff --git a/test/cmdlineTests/standard_yul_cfg_json_export/output.json b/test/cmdlineTests/standard_yul_cfg_json_export/output.json index 07b1c3f639..09674ec77f 100644 --- a/test/cmdlineTests/standard_yul_cfg_json_export/output.json +++ b/test/cmdlineTests/standard_yul_cfg_json_export/output.json @@ -7,7 +7,7 @@ "blocks": [ { "exit": { - "cond": "v2", + "cond": "v1", "targets": [ "Block2", "Block1" @@ -38,7 +38,7 @@ "in": [], "op": "callvalue", "out": [ - "v2" + "v1" ] } ], @@ -63,7 +63,7 @@ ], "op": "datasize", "out": [ - "v4" + "v2" ] }, { @@ -73,13 +73,13 @@ ], "op": "dataoffset", "out": [ - "v5" + "v3" ] }, { "in": [ - "v4", - "v5", + "v2", + "v3", "v0" ], "op": "codecopy", @@ -87,7 +87,7 @@ }, { "in": [ - "v4", + "v2", "v0" ], "op": "return", @@ -130,7 +130,7 @@ "blocks": [ { "exit": { - "cond": "v5", + "cond": "v3", "targets": [ "Block2", "Block1" @@ -161,26 +161,26 @@ "in": [], "op": "calldatasize", "out": [ - "v3" + "v1" ] }, { "in": [ "0x04", - "v3" + "v1" ], "op": "lt", "out": [ - "v4" + "v2" ] }, { "in": [ - "v4" + "v2" ], "op": "iszero", "out": [ - "v5" + "v3" ] } ], @@ -215,7 +215,7 @@ }, { "exit": { - "cond": "v11", + "cond": "v6", "targets": [ "Block4", "Block3" @@ -230,27 +230,27 @@ ], "op": "calldataload", "out": [ - "v7" + "v4" ] }, { "in": [ - "v7", + "v4", "0xe0" ], "op": "shr", "out": [ - "v9" + "v5" ] }, { "in": [ - "v9", + "v5", "0x26121ff0" ], "op": "eq", "out": [ - "v11" + "v6" ] } ], @@ -280,7 +280,7 @@ }, { "exit": { - "cond": "v12", + "cond": "v7", "targets": [ "Block6", "Block5" @@ -293,7 +293,7 @@ "in": [], "op": "callvalue", "out": [ - "v12" + "v7" ] } ], @@ -309,7 +309,7 @@ }, { "exit": { - "cond": "v17", + "cond": "v11", "targets": [ "Block9", "Block8" @@ -324,34 +324,34 @@ ], "op": "not", "out": [ - "v14" + "v8" ] }, { "in": [], "op": "calldatasize", "out": [ - "v15" + "v9" ] }, { "in": [ - "v14", - "v15" + "v8", + "v9" ], "op": "add", "out": [ - "v16" + "v10" ] }, { "in": [ "0x00", - "v16" + "v10" ], "op": "slt", "out": [ - "v17" + "v11" ] } ], @@ -454,7 +454,7 @@ "blocks": [ { "exit": { - "cond": "v2", + "cond": "v1", "targets": [ "Block2", "Block1" @@ -485,7 +485,7 @@ "in": [], "op": "callvalue", "out": [ - "v2" + "v1" ] } ], @@ -510,7 +510,7 @@ ], "op": "datasize", "out": [ - "v4" + "v2" ] }, { @@ -520,13 +520,13 @@ ], "op": "dataoffset", "out": [ - "v5" + "v3" ] }, { "in": [ - "v4", - "v5", + "v2", + "v3", "v0" ], "op": "codecopy", @@ -534,7 +534,7 @@ }, { "in": [ - "v4", + "v2", "v0" ], "op": "return", @@ -577,7 +577,7 @@ "blocks": [ { "exit": { - "cond": "v5", + "cond": "v3", "targets": [ "Block2", "Block1" @@ -608,26 +608,26 @@ "in": [], "op": "calldatasize", "out": [ - "v3" + "v1" ] }, { "in": [ "0x04", - "v3" + "v1" ], "op": "lt", "out": [ - "v4" + "v2" ] }, { "in": [ - "v4" + "v2" ], "op": "iszero", "out": [ - "v5" + "v3" ] } ], @@ -662,7 +662,7 @@ }, { "exit": { - "cond": "v11", + "cond": "v6", "targets": [ "Block4", "Block3" @@ -677,27 +677,27 @@ ], "op": "calldataload", "out": [ - "v7" + "v4" ] }, { "in": [ - "v7", + "v4", "0xe0" ], "op": "shr", "out": [ - "v9" + "v5" ] }, { "in": [ - "v9", + "v5", "0x26121ff0" ], "op": "eq", "out": [ - "v11" + "v6" ] } ], @@ -727,7 +727,7 @@ }, { "exit": { - "cond": "v12", + "cond": "v7", "targets": [ "Block6", "Block5" @@ -740,7 +740,7 @@ "in": [], "op": "callvalue", "out": [ - "v12" + "v7" ] } ], @@ -756,7 +756,7 @@ }, { "exit": { - "cond": "v17", + "cond": "v11", "targets": [ "Block9", "Block8" @@ -771,34 +771,34 @@ ], "op": "not", "out": [ - "v14" + "v8" ] }, { "in": [], "op": "calldatasize", "out": [ - "v15" + "v9" ] }, { "in": [ - "v14", - "v15" + "v8", + "v9" ], "op": "add", "out": [ - "v16" + "v10" ] }, { "in": [ "0x00", - "v16" + "v10" ], "op": "slt", "out": [ - "v17" + "v11" ] } ], @@ -835,7 +835,7 @@ }, { "exit": { - "cond": "v28", + "cond": "v16", "targets": [ "Block12", "Block11" @@ -851,47 +851,47 @@ ], "op": "datasize", "out": [ - "v18" + "v12" ] }, { "in": [ - "v18", + "v12", "v0" ], "op": "add", "out": [ - "v24" + "v13" ] }, { "in": [ "v0", - "v24" + "v13" ], "op": "lt", "out": [ - "v25" + "v14" ] }, { "in": [ "0xffffffffffffffff", - "v24" + "v13" ], "op": "gt", "out": [ - "v27" + "v15" ] }, { "in": [ - "v25", - "v27" + "v14", + "v15" ], "op": "or", "out": [ - "v28" + "v16" ] } ], @@ -901,8 +901,8 @@ ], "out": [ "v0", - "v24", - "v18" + "v13", + "v12" ] }, "type": "BuiltinCall" @@ -930,7 +930,7 @@ }, { "exit": { - "cond": "v42", + "cond": "v21", "targets": [ "Block15", "Block14" @@ -946,13 +946,13 @@ ], "op": "dataoffset", "out": [ - "v35" + "v18" ] }, { "in": [ + "v12", "v18", - "v35", "v0" ], "op": "datacopy", @@ -961,42 +961,42 @@ { "in": [ "v0", - "v24" + "v13" ], "op": "sub", "out": [ - "v40" + "v19" ] }, { "in": [ - "v40", + "v19", "v0", "0x00" ], "op": "create", "out": [ - "v41" + "v20" ] }, { "in": [ - "v41" + "v20" ], "op": "iszero", "out": [ - "v42" + "v21" ] } ], "liveness": { "in": [ "v0", - "v24", - "v18" + "v13", + "v12" ], "out": [ - "v41" + "v20" ] }, "type": "BuiltinCall" @@ -1014,12 +1014,12 @@ ], "op": "shl", "out": [ - "v30" + "v17" ] }, { "in": [ - "v30", + "v17", "0x00" ], "op": "mstore", @@ -1050,7 +1050,7 @@ }, { "exit": { - "cond": "v60", + "cond": "v32", "targets": [ "Block18", "Block17" @@ -1065,7 +1065,7 @@ ], "op": "mload", "out": [ - "v46" + "v25" ] }, { @@ -1075,13 +1075,13 @@ ], "op": "shl", "out": [ - "v49" + "v26" ] }, { "in": [ - "v49", - "v46" + "v26", + "v25" ], "op": "mstore", "out": [] @@ -1093,67 +1093,67 @@ ], "op": "shl", "out": [ - "v53" + "v27" ] }, { "in": [ "0x01", - "v53" + "v27" ], "op": "sub", "out": [ - "v54" + "v28" ] }, { "in": [ - "v54", - "v41" + "v28", + "v20" ], "op": "and", "out": [ - "v57" + "v29" ] }, { "in": [], "op": "gas", "out": [ - "v58" + "v30" ] }, { "in": [ "0x20", - "v46", + "v25", "0x04", - "v46", - "v57", - "v58" + "v25", + "v29", + "v30" ], "op": "staticcall", "out": [ - "v59" + "v31" ] }, { "in": [ - "v59" + "v31" ], "op": "iszero", "out": [ - "v60" + "v32" ] } ], "liveness": { "in": [ - "v41" + "v20" ], "out": [ - "v46", - "v59" + "v25", + "v31" ] }, "type": "BuiltinCall" @@ -1170,21 +1170,21 @@ ], "op": "mload", "out": [ - "v43" + "v22" ] }, { "in": [], "op": "returndatasize", "out": [ - "v44" + "v23" ] }, { "in": [ - "v44", + "v23", "0x00", - "v43" + "v22" ], "op": "returndatacopy", "out": [] @@ -1193,13 +1193,13 @@ "in": [], "op": "returndatasize", "out": [ - "v45" + "v24" ] }, { "in": [ - "v45", - "v43" + "v24", + "v22" ], "op": "revert", "out": [] @@ -1213,7 +1213,7 @@ }, { "exit": { - "cond": "v59", + "cond": "v31", "targets": [ "Block21", "Block20" @@ -1228,18 +1228,18 @@ ], "op": "LiteralAssignment", "out": [ - "v64" + "v36" ] } ], "liveness": { "in": [ - "v46", - "v59" + "v25", + "v31" ], "out": [ - "v64", - "v46" + "v36", + "v25" ] } }, @@ -1255,21 +1255,21 @@ ], "op": "mload", "out": [ - "v61" + "v33" ] }, { "in": [], "op": "returndatasize", "out": [ - "v62" + "v34" ] }, { "in": [ - "v62", + "v34", "0x00", - "v61" + "v33" ], "op": "returndatacopy", "out": [] @@ -1278,13 +1278,13 @@ "in": [], "op": "returndatasize", "out": [ - "v63" + "v35" ] }, { "in": [ - "v63", - "v61" + "v35", + "v33" ], "op": "revert", "out": [] @@ -1308,12 +1308,12 @@ "instructions": [ { "in": [ - "v64", - "v95" + "v36", + "v52" ], "op": "PhiFunction", "out": [ - "v97" + "phi26" ] }, { @@ -1322,13 +1322,13 @@ ], "op": "mload", "out": [ - "v96" + "v53" ] }, { "in": [ - "v97", - "v96" + "phi26", + "v53" ], "op": "mstore", "out": [] @@ -1336,7 +1336,7 @@ { "in": [ "0x20", - "v96" + "v53" ], "op": "return", "out": [] @@ -1344,7 +1344,7 @@ ], "liveness": { "in": [ - "v97" + "phi26" ], "out": [] }, @@ -1352,7 +1352,7 @@ }, { "exit": { - "cond": "v69", + "cond": "v39", "targets": [ "Block23", "Block22" @@ -1367,34 +1367,34 @@ ], "op": "LiteralAssignment", "out": [ - "v67" + "v37" ] }, { "in": [], "op": "returndatasize", "out": [ - "v68" + "v38" ] }, { "in": [ - "v68", + "v38", "0x20" ], "op": "gt", "out": [ - "v69" + "v39" ] } ], "liveness": { "in": [ - "v46" + "v25" ], "out": [ - "v67", - "v46" + "v37", + "v25" ] }, "type": "BuiltinCall" @@ -1405,7 +1405,7 @@ "Block22" ], "exit": { - "cond": "v82", + "cond": "v47", "targets": [ "Block25", "Block24" @@ -1416,12 +1416,12 @@ "instructions": [ { "in": [ - "v67", - "v70" + "v37", + "v40" ], "op": "PhiFunction", "out": [ - "v73" + "phi14" ] }, { @@ -1430,79 +1430,79 @@ ], "op": "not", "out": [ - "v72" + "v41" ] }, { "in": [ "0x1f", - "v73" + "phi14" ], "op": "add", "out": [ - "v74" + "v42" ] }, { "in": [ - "v72", - "v74" + "v41", + "v42" ], "op": "and", "out": [ - "v75" + "v43" ] }, { "in": [ - "v75", - "v46" + "v43", + "v25" ], "op": "add", "out": [ - "v79" + "v44" ] }, { "in": [ - "v46", - "v79" + "v25", + "v44" ], "op": "lt", "out": [ - "v80" + "v45" ] }, { "in": [ "0xffffffffffffffff", - "v79" + "v44" ], "op": "gt", "out": [ - "v81" + "v46" ] }, { "in": [ - "v80", - "v81" + "v45", + "v46" ], "op": "or", "out": [ - "v82" + "v47" ] } ], "liveness": { "in": [ - "v46", - "v73" + "v25", + "phi14" ], "out": [ - "v46", - "v73", - "v79" + "v25", + "phi14", + "v44" ] }, "type": "BuiltinCall" @@ -1520,24 +1520,24 @@ "in": [], "op": "returndatasize", "out": [ - "v70" + "v40" ] } ], "liveness": { "in": [ - "v46" + "v25" ], "out": [ - "v70", - "v46" + "v40", + "v25" ] }, "type": "BuiltinCall" }, { "exit": { - "cond": "v92", + "cond": "v51", "targets": [ "Block28", "Block27" @@ -1548,7 +1548,7 @@ "instructions": [ { "in": [ - "v79", + "v44", "0x40" ], "op": "mstore", @@ -1556,43 +1556,43 @@ }, { "in": [ - "v73", - "v46" + "phi14", + "v25" ], "op": "add", "out": [ - "v90" + "v49" ] }, { "in": [ - "v46", - "v90" + "v25", + "v49" ], "op": "sub", "out": [ - "v91" + "v50" ] }, { "in": [ "0x20", - "v91" + "v50" ], "op": "slt", "out": [ - "v92" + "v51" ] } ], "liveness": { "in": [ - "v46", - "v73", - "v79" + "v25", + "phi14", + "v44" ], "out": [ - "v46" + "v25" ] }, "type": "BuiltinCall" @@ -1610,12 +1610,12 @@ ], "op": "shl", "out": [ - "v83" + "v48" ] }, { "in": [ - "v83", + "v48", "0x00" ], "op": "mstore", @@ -1655,20 +1655,20 @@ "instructions": [ { "in": [ - "v46" + "v25" ], "op": "mload", "out": [ - "v95" + "v52" ] } ], "liveness": { "in": [ - "v46" + "v25" ], "out": [ - "v95" + "v52" ] }, "type": "BuiltinCall" @@ -1701,7 +1701,7 @@ "blocks": [ { "exit": { - "cond": "v2", + "cond": "v1", "targets": [ "Block2", "Block1" @@ -1732,7 +1732,7 @@ "in": [], "op": "callvalue", "out": [ - "v2" + "v1" ] } ], @@ -1757,7 +1757,7 @@ ], "op": "datasize", "out": [ - "v4" + "v2" ] }, { @@ -1767,13 +1767,13 @@ ], "op": "dataoffset", "out": [ - "v5" + "v3" ] }, { "in": [ - "v4", - "v5", + "v2", + "v3", "v0" ], "op": "codecopy", @@ -1781,7 +1781,7 @@ }, { "in": [ - "v4", + "v2", "v0" ], "op": "return", @@ -1824,7 +1824,7 @@ "blocks": [ { "exit": { - "cond": "v5", + "cond": "v3", "targets": [ "Block2", "Block1" @@ -1855,26 +1855,26 @@ "in": [], "op": "calldatasize", "out": [ - "v3" + "v1" ] }, { "in": [ "0x04", - "v3" + "v1" ], "op": "lt", "out": [ - "v4" + "v2" ] }, { "in": [ - "v4" + "v2" ], "op": "iszero", "out": [ - "v5" + "v3" ] } ], @@ -1909,7 +1909,7 @@ }, { "exit": { - "cond": "v11", + "cond": "v6", "targets": [ "Block4", "Block3" @@ -1924,27 +1924,27 @@ ], "op": "calldataload", "out": [ - "v7" + "v4" ] }, { "in": [ - "v7", + "v4", "0xe0" ], "op": "shr", "out": [ - "v9" + "v5" ] }, { "in": [ - "v9", + "v5", "0x26121ff0" ], "op": "eq", "out": [ - "v11" + "v6" ] } ], @@ -1974,7 +1974,7 @@ }, { "exit": { - "cond": "v12", + "cond": "v7", "targets": [ "Block6", "Block5" @@ -1987,7 +1987,7 @@ "in": [], "op": "callvalue", "out": [ - "v12" + "v7" ] } ], @@ -2003,7 +2003,7 @@ }, { "exit": { - "cond": "v17", + "cond": "v11", "targets": [ "Block9", "Block8" @@ -2018,34 +2018,34 @@ ], "op": "not", "out": [ - "v14" + "v8" ] }, { "in": [], "op": "calldatasize", "out": [ - "v15" + "v9" ] }, { "in": [ - "v14", - "v15" + "v8", + "v9" ], "op": "add", "out": [ - "v16" + "v10" ] }, { "in": [ "0x00", - "v16" + "v10" ], "op": "slt", "out": [ - "v17" + "v11" ] } ], diff --git a/test/cmdlineTests/strict_asm_yul_cfg_json_export/output b/test/cmdlineTests/strict_asm_yul_cfg_json_export/output index 40f8186a1a..7554c23407 100644 --- a/test/cmdlineTests/strict_asm_yul_cfg_json_export/output +++ b/test/cmdlineTests/strict_asm_yul_cfg_json_export/output @@ -7,7 +7,7 @@ Yul Control Flow Graph: "blocks": [ { "exit": { - "cond": "v2", + "cond": "v1", "targets": [ "Block2", "Block1" @@ -38,7 +38,7 @@ Yul Control Flow Graph: "in": [], "op": "callvalue", "out": [ - "v2" + "v1" ] } ], @@ -63,7 +63,7 @@ Yul Control Flow Graph: ], "op": "datasize", "out": [ - "v4" + "v2" ] }, { @@ -73,13 +73,13 @@ Yul Control Flow Graph: ], "op": "dataoffset", "out": [ - "v5" + "v3" ] }, { "in": [ - "v4", - "v5", + "v2", + "v3", "v0" ], "op": "codecopy", @@ -87,7 +87,7 @@ Yul Control Flow Graph: }, { "in": [ - "v4", + "v2", "v0" ], "op": "return", @@ -130,7 +130,7 @@ Yul Control Flow Graph: "blocks": [ { "exit": { - "cond": "v5", + "cond": "v3", "targets": [ "Block2", "Block1" @@ -161,26 +161,26 @@ Yul Control Flow Graph: "in": [], "op": "calldatasize", "out": [ - "v3" + "v1" ] }, { "in": [ "0x04", - "v3" + "v1" ], "op": "lt", "out": [ - "v4" + "v2" ] }, { "in": [ - "v4" + "v2" ], "op": "iszero", "out": [ - "v5" + "v3" ] } ], @@ -215,7 +215,7 @@ Yul Control Flow Graph: }, { "exit": { - "cond": "v11", + "cond": "v6", "targets": [ "Block4", "Block3" @@ -230,27 +230,27 @@ Yul Control Flow Graph: ], "op": "calldataload", "out": [ - "v7" + "v4" ] }, { "in": [ - "v7", + "v4", "0xe0" ], "op": "shr", "out": [ - "v9" + "v5" ] }, { "in": [ - "v9", + "v5", "0x26121ff0" ], "op": "eq", "out": [ - "v11" + "v6" ] } ], @@ -280,7 +280,7 @@ Yul Control Flow Graph: }, { "exit": { - "cond": "v12", + "cond": "v7", "targets": [ "Block6", "Block5" @@ -293,7 +293,7 @@ Yul Control Flow Graph: "in": [], "op": "callvalue", "out": [ - "v12" + "v7" ] } ], @@ -309,7 +309,7 @@ Yul Control Flow Graph: }, { "exit": { - "cond": "v17", + "cond": "v11", "targets": [ "Block9", "Block8" @@ -324,34 +324,34 @@ Yul Control Flow Graph: ], "op": "not", "out": [ - "v14" + "v8" ] }, { "in": [], "op": "calldatasize", "out": [ - "v15" + "v9" ] }, { "in": [ - "v14", - "v15" + "v8", + "v9" ], "op": "add", "out": [ - "v16" + "v10" ] }, { "in": [ "0x00", - "v16" + "v10" ], "op": "slt", "out": [ - "v17" + "v11" ] } ], diff --git a/test/cmdlineTests/yul_cfg_json_export/output b/test/cmdlineTests/yul_cfg_json_export/output index cf93e76fd5..d1aeaed52d 100644 --- a/test/cmdlineTests/yul_cfg_json_export/output +++ b/test/cmdlineTests/yul_cfg_json_export/output @@ -6,7 +6,7 @@ Yul Control Flow Graph: "blocks": [ { "exit": { - "cond": "v2", + "cond": "v1", "targets": [ "Block2", "Block1" @@ -37,7 +37,7 @@ Yul Control Flow Graph: "in": [], "op": "callvalue", "out": [ - "v2" + "v1" ] } ], @@ -62,7 +62,7 @@ Yul Control Flow Graph: ], "op": "datasize", "out": [ - "v4" + "v2" ] }, { @@ -72,13 +72,13 @@ Yul Control Flow Graph: ], "op": "dataoffset", "out": [ - "v5" + "v3" ] }, { "in": [ - "v4", - "v5", + "v2", + "v3", "v0" ], "op": "codecopy", @@ -86,7 +86,7 @@ Yul Control Flow Graph: }, { "in": [ - "v4", + "v2", "v0" ], "op": "return", @@ -129,7 +129,7 @@ Yul Control Flow Graph: "blocks": [ { "exit": { - "cond": "v5", + "cond": "v3", "targets": [ "Block2", "Block1" @@ -160,26 +160,26 @@ Yul Control Flow Graph: "in": [], "op": "calldatasize", "out": [ - "v3" + "v1" ] }, { "in": [ "0x04", - "v3" + "v1" ], "op": "lt", "out": [ - "v4" + "v2" ] }, { "in": [ - "v4" + "v2" ], "op": "iszero", "out": [ - "v5" + "v3" ] } ], @@ -214,7 +214,7 @@ Yul Control Flow Graph: }, { "exit": { - "cond": "v11", + "cond": "v6", "targets": [ "Block4", "Block3" @@ -229,27 +229,27 @@ Yul Control Flow Graph: ], "op": "calldataload", "out": [ - "v7" + "v4" ] }, { "in": [ - "v7", + "v4", "0xe0" ], "op": "shr", "out": [ - "v9" + "v5" ] }, { "in": [ - "v9", + "v5", "0x26121ff0" ], "op": "eq", "out": [ - "v11" + "v6" ] } ], @@ -279,7 +279,7 @@ Yul Control Flow Graph: }, { "exit": { - "cond": "v12", + "cond": "v7", "targets": [ "Block6", "Block5" @@ -292,7 +292,7 @@ Yul Control Flow Graph: "in": [], "op": "callvalue", "out": [ - "v12" + "v7" ] } ], @@ -308,7 +308,7 @@ Yul Control Flow Graph: }, { "exit": { - "cond": "v17", + "cond": "v11", "targets": [ "Block9", "Block8" @@ -323,34 +323,34 @@ Yul Control Flow Graph: ], "op": "not", "out": [ - "v14" + "v8" ] }, { "in": [], "op": "calldatasize", "out": [ - "v15" + "v9" ] }, { "in": [ - "v14", - "v15" + "v8", + "v9" ], "op": "add", "out": [ - "v16" + "v10" ] }, { "in": [ "0x00", - "v16" + "v10" ], "op": "slt", "out": [ - "v17" + "v11" ] } ], @@ -454,7 +454,7 @@ Yul Control Flow Graph: "blocks": [ { "exit": { - "cond": "v2", + "cond": "v1", "targets": [ "Block2", "Block1" @@ -485,7 +485,7 @@ Yul Control Flow Graph: "in": [], "op": "callvalue", "out": [ - "v2" + "v1" ] } ], @@ -510,7 +510,7 @@ Yul Control Flow Graph: ], "op": "datasize", "out": [ - "v4" + "v2" ] }, { @@ -520,13 +520,13 @@ Yul Control Flow Graph: ], "op": "dataoffset", "out": [ - "v5" + "v3" ] }, { "in": [ - "v4", - "v5", + "v2", + "v3", "v0" ], "op": "codecopy", @@ -534,7 +534,7 @@ Yul Control Flow Graph: }, { "in": [ - "v4", + "v2", "v0" ], "op": "return", @@ -577,7 +577,7 @@ Yul Control Flow Graph: "blocks": [ { "exit": { - "cond": "v5", + "cond": "v3", "targets": [ "Block2", "Block1" @@ -608,26 +608,26 @@ Yul Control Flow Graph: "in": [], "op": "calldatasize", "out": [ - "v3" + "v1" ] }, { "in": [ "0x04", - "v3" + "v1" ], "op": "lt", "out": [ - "v4" + "v2" ] }, { "in": [ - "v4" + "v2" ], "op": "iszero", "out": [ - "v5" + "v3" ] } ], @@ -662,7 +662,7 @@ Yul Control Flow Graph: }, { "exit": { - "cond": "v11", + "cond": "v6", "targets": [ "Block4", "Block3" @@ -677,27 +677,27 @@ Yul Control Flow Graph: ], "op": "calldataload", "out": [ - "v7" + "v4" ] }, { "in": [ - "v7", + "v4", "0xe0" ], "op": "shr", "out": [ - "v9" + "v5" ] }, { "in": [ - "v9", + "v5", "0x26121ff0" ], "op": "eq", "out": [ - "v11" + "v6" ] } ], @@ -727,7 +727,7 @@ Yul Control Flow Graph: }, { "exit": { - "cond": "v12", + "cond": "v7", "targets": [ "Block6", "Block5" @@ -740,7 +740,7 @@ Yul Control Flow Graph: "in": [], "op": "callvalue", "out": [ - "v12" + "v7" ] } ], @@ -756,7 +756,7 @@ Yul Control Flow Graph: }, { "exit": { - "cond": "v17", + "cond": "v11", "targets": [ "Block9", "Block8" @@ -771,34 +771,34 @@ Yul Control Flow Graph: ], "op": "not", "out": [ - "v14" + "v8" ] }, { "in": [], "op": "calldatasize", "out": [ - "v15" + "v9" ] }, { "in": [ - "v14", - "v15" + "v8", + "v9" ], "op": "add", "out": [ - "v16" + "v10" ] }, { "in": [ "0x00", - "v16" + "v10" ], "op": "slt", "out": [ - "v17" + "v11" ] } ], @@ -835,7 +835,7 @@ Yul Control Flow Graph: }, { "exit": { - "cond": "v28", + "cond": "v16", "targets": [ "Block12", "Block11" @@ -851,47 +851,47 @@ Yul Control Flow Graph: ], "op": "datasize", "out": [ - "v18" + "v12" ] }, { "in": [ - "v18", + "v12", "v0" ], "op": "add", "out": [ - "v24" + "v13" ] }, { "in": [ "v0", - "v24" + "v13" ], "op": "lt", "out": [ - "v25" + "v14" ] }, { "in": [ "0xffffffffffffffff", - "v24" + "v13" ], "op": "gt", "out": [ - "v27" + "v15" ] }, { "in": [ - "v25", - "v27" + "v14", + "v15" ], "op": "or", "out": [ - "v28" + "v16" ] } ], @@ -901,8 +901,8 @@ Yul Control Flow Graph: ], "out": [ "v0", - "v24", - "v18" + "v13", + "v12" ] }, "type": "BuiltinCall" @@ -930,7 +930,7 @@ Yul Control Flow Graph: }, { "exit": { - "cond": "v42", + "cond": "v21", "targets": [ "Block15", "Block14" @@ -946,13 +946,13 @@ Yul Control Flow Graph: ], "op": "dataoffset", "out": [ - "v35" + "v18" ] }, { "in": [ + "v12", "v18", - "v35", "v0" ], "op": "datacopy", @@ -961,42 +961,42 @@ Yul Control Flow Graph: { "in": [ "v0", - "v24" + "v13" ], "op": "sub", "out": [ - "v40" + "v19" ] }, { "in": [ - "v40", + "v19", "v0", "0x00" ], "op": "create", "out": [ - "v41" + "v20" ] }, { "in": [ - "v41" + "v20" ], "op": "iszero", "out": [ - "v42" + "v21" ] } ], "liveness": { "in": [ "v0", - "v24", - "v18" + "v13", + "v12" ], "out": [ - "v41" + "v20" ] }, "type": "BuiltinCall" @@ -1014,12 +1014,12 @@ Yul Control Flow Graph: ], "op": "shl", "out": [ - "v30" + "v17" ] }, { "in": [ - "v30", + "v17", "0x00" ], "op": "mstore", @@ -1050,7 +1050,7 @@ Yul Control Flow Graph: }, { "exit": { - "cond": "v60", + "cond": "v32", "targets": [ "Block18", "Block17" @@ -1065,7 +1065,7 @@ Yul Control Flow Graph: ], "op": "mload", "out": [ - "v46" + "v25" ] }, { @@ -1075,13 +1075,13 @@ Yul Control Flow Graph: ], "op": "shl", "out": [ - "v49" + "v26" ] }, { "in": [ - "v49", - "v46" + "v26", + "v25" ], "op": "mstore", "out": [] @@ -1093,67 +1093,67 @@ Yul Control Flow Graph: ], "op": "shl", "out": [ - "v53" + "v27" ] }, { "in": [ "0x01", - "v53" + "v27" ], "op": "sub", "out": [ - "v54" + "v28" ] }, { "in": [ - "v54", - "v41" + "v28", + "v20" ], "op": "and", "out": [ - "v57" + "v29" ] }, { "in": [], "op": "gas", "out": [ - "v58" + "v30" ] }, { "in": [ "0x20", - "v46", + "v25", "0x04", - "v46", - "v57", - "v58" + "v25", + "v29", + "v30" ], "op": "staticcall", "out": [ - "v59" + "v31" ] }, { "in": [ - "v59" + "v31" ], "op": "iszero", "out": [ - "v60" + "v32" ] } ], "liveness": { "in": [ - "v41" + "v20" ], "out": [ - "v46", - "v59" + "v25", + "v31" ] }, "type": "BuiltinCall" @@ -1170,21 +1170,21 @@ Yul Control Flow Graph: ], "op": "mload", "out": [ - "v43" + "v22" ] }, { "in": [], "op": "returndatasize", "out": [ - "v44" + "v23" ] }, { "in": [ - "v44", + "v23", "0x00", - "v43" + "v22" ], "op": "returndatacopy", "out": [] @@ -1193,13 +1193,13 @@ Yul Control Flow Graph: "in": [], "op": "returndatasize", "out": [ - "v45" + "v24" ] }, { "in": [ - "v45", - "v43" + "v24", + "v22" ], "op": "revert", "out": [] @@ -1213,7 +1213,7 @@ Yul Control Flow Graph: }, { "exit": { - "cond": "v59", + "cond": "v31", "targets": [ "Block21", "Block20" @@ -1228,18 +1228,18 @@ Yul Control Flow Graph: ], "op": "LiteralAssignment", "out": [ - "v64" + "v36" ] } ], "liveness": { "in": [ - "v46", - "v59" + "v25", + "v31" ], "out": [ - "v64", - "v46" + "v36", + "v25" ] } }, @@ -1255,21 +1255,21 @@ Yul Control Flow Graph: ], "op": "mload", "out": [ - "v61" + "v33" ] }, { "in": [], "op": "returndatasize", "out": [ - "v62" + "v34" ] }, { "in": [ - "v62", + "v34", "0x00", - "v61" + "v33" ], "op": "returndatacopy", "out": [] @@ -1278,13 +1278,13 @@ Yul Control Flow Graph: "in": [], "op": "returndatasize", "out": [ - "v63" + "v35" ] }, { "in": [ - "v63", - "v61" + "v35", + "v33" ], "op": "revert", "out": [] @@ -1308,12 +1308,12 @@ Yul Control Flow Graph: "instructions": [ { "in": [ - "v64", - "v95" + "v36", + "v52" ], "op": "PhiFunction", "out": [ - "v97" + "phi26" ] }, { @@ -1322,13 +1322,13 @@ Yul Control Flow Graph: ], "op": "mload", "out": [ - "v96" + "v53" ] }, { "in": [ - "v97", - "v96" + "phi26", + "v53" ], "op": "mstore", "out": [] @@ -1336,7 +1336,7 @@ Yul Control Flow Graph: { "in": [ "0x20", - "v96" + "v53" ], "op": "return", "out": [] @@ -1344,7 +1344,7 @@ Yul Control Flow Graph: ], "liveness": { "in": [ - "v97" + "phi26" ], "out": [] }, @@ -1352,7 +1352,7 @@ Yul Control Flow Graph: }, { "exit": { - "cond": "v69", + "cond": "v39", "targets": [ "Block23", "Block22" @@ -1367,34 +1367,34 @@ Yul Control Flow Graph: ], "op": "LiteralAssignment", "out": [ - "v67" + "v37" ] }, { "in": [], "op": "returndatasize", "out": [ - "v68" + "v38" ] }, { "in": [ - "v68", + "v38", "0x20" ], "op": "gt", "out": [ - "v69" + "v39" ] } ], "liveness": { "in": [ - "v46" + "v25" ], "out": [ - "v67", - "v46" + "v37", + "v25" ] }, "type": "BuiltinCall" @@ -1405,7 +1405,7 @@ Yul Control Flow Graph: "Block22" ], "exit": { - "cond": "v82", + "cond": "v47", "targets": [ "Block25", "Block24" @@ -1416,12 +1416,12 @@ Yul Control Flow Graph: "instructions": [ { "in": [ - "v67", - "v70" + "v37", + "v40" ], "op": "PhiFunction", "out": [ - "v73" + "phi14" ] }, { @@ -1430,79 +1430,79 @@ Yul Control Flow Graph: ], "op": "not", "out": [ - "v72" + "v41" ] }, { "in": [ "0x1f", - "v73" + "phi14" ], "op": "add", "out": [ - "v74" + "v42" ] }, { "in": [ - "v72", - "v74" + "v41", + "v42" ], "op": "and", "out": [ - "v75" + "v43" ] }, { "in": [ - "v75", - "v46" + "v43", + "v25" ], "op": "add", "out": [ - "v79" + "v44" ] }, { "in": [ - "v46", - "v79" + "v25", + "v44" ], "op": "lt", "out": [ - "v80" + "v45" ] }, { "in": [ "0xffffffffffffffff", - "v79" + "v44" ], "op": "gt", "out": [ - "v81" + "v46" ] }, { "in": [ - "v80", - "v81" + "v45", + "v46" ], "op": "or", "out": [ - "v82" + "v47" ] } ], "liveness": { "in": [ - "v46", - "v73" + "v25", + "phi14" ], "out": [ - "v46", - "v73", - "v79" + "v25", + "phi14", + "v44" ] }, "type": "BuiltinCall" @@ -1520,24 +1520,24 @@ Yul Control Flow Graph: "in": [], "op": "returndatasize", "out": [ - "v70" + "v40" ] } ], "liveness": { "in": [ - "v46" + "v25" ], "out": [ - "v70", - "v46" + "v40", + "v25" ] }, "type": "BuiltinCall" }, { "exit": { - "cond": "v92", + "cond": "v51", "targets": [ "Block28", "Block27" @@ -1548,7 +1548,7 @@ Yul Control Flow Graph: "instructions": [ { "in": [ - "v79", + "v44", "0x40" ], "op": "mstore", @@ -1556,43 +1556,43 @@ Yul Control Flow Graph: }, { "in": [ - "v73", - "v46" + "phi14", + "v25" ], "op": "add", "out": [ - "v90" + "v49" ] }, { "in": [ - "v46", - "v90" + "v25", + "v49" ], "op": "sub", "out": [ - "v91" + "v50" ] }, { "in": [ "0x20", - "v91" + "v50" ], "op": "slt", "out": [ - "v92" + "v51" ] } ], "liveness": { "in": [ - "v46", - "v73", - "v79" + "v25", + "phi14", + "v44" ], "out": [ - "v46" + "v25" ] }, "type": "BuiltinCall" @@ -1610,12 +1610,12 @@ Yul Control Flow Graph: ], "op": "shl", "out": [ - "v83" + "v48" ] }, { "in": [ - "v83", + "v48", "0x00" ], "op": "mstore", @@ -1655,20 +1655,20 @@ Yul Control Flow Graph: "instructions": [ { "in": [ - "v46" + "v25" ], "op": "mload", "out": [ - "v95" + "v52" ] } ], "liveness": { "in": [ - "v46" + "v25" ], "out": [ - "v95" + "v52" ] }, "type": "BuiltinCall" @@ -1701,7 +1701,7 @@ Yul Control Flow Graph: "blocks": [ { "exit": { - "cond": "v2", + "cond": "v1", "targets": [ "Block2", "Block1" @@ -1732,7 +1732,7 @@ Yul Control Flow Graph: "in": [], "op": "callvalue", "out": [ - "v2" + "v1" ] } ], @@ -1757,7 +1757,7 @@ Yul Control Flow Graph: ], "op": "datasize", "out": [ - "v4" + "v2" ] }, { @@ -1767,13 +1767,13 @@ Yul Control Flow Graph: ], "op": "dataoffset", "out": [ - "v5" + "v3" ] }, { "in": [ - "v4", - "v5", + "v2", + "v3", "v0" ], "op": "codecopy", @@ -1781,7 +1781,7 @@ Yul Control Flow Graph: }, { "in": [ - "v4", + "v2", "v0" ], "op": "return", @@ -1824,7 +1824,7 @@ Yul Control Flow Graph: "blocks": [ { "exit": { - "cond": "v5", + "cond": "v3", "targets": [ "Block2", "Block1" @@ -1855,26 +1855,26 @@ Yul Control Flow Graph: "in": [], "op": "calldatasize", "out": [ - "v3" + "v1" ] }, { "in": [ "0x04", - "v3" + "v1" ], "op": "lt", "out": [ - "v4" + "v2" ] }, { "in": [ - "v4" + "v2" ], "op": "iszero", "out": [ - "v5" + "v3" ] } ], @@ -1909,7 +1909,7 @@ Yul Control Flow Graph: }, { "exit": { - "cond": "v11", + "cond": "v6", "targets": [ "Block4", "Block3" @@ -1924,27 +1924,27 @@ Yul Control Flow Graph: ], "op": "calldataload", "out": [ - "v7" + "v4" ] }, { "in": [ - "v7", + "v4", "0xe0" ], "op": "shr", "out": [ - "v9" + "v5" ] }, { "in": [ - "v9", + "v5", "0x26121ff0" ], "op": "eq", "out": [ - "v11" + "v6" ] } ], @@ -1974,7 +1974,7 @@ Yul Control Flow Graph: }, { "exit": { - "cond": "v12", + "cond": "v7", "targets": [ "Block6", "Block5" @@ -1987,7 +1987,7 @@ Yul Control Flow Graph: "in": [], "op": "callvalue", "out": [ - "v12" + "v7" ] } ], @@ -2003,7 +2003,7 @@ Yul Control Flow Graph: }, { "exit": { - "cond": "v17", + "cond": "v11", "targets": [ "Block9", "Block8" @@ -2018,34 +2018,34 @@ Yul Control Flow Graph: ], "op": "not", "out": [ - "v14" + "v8" ] }, { "in": [], "op": "calldatasize", "out": [ - "v15" + "v9" ] }, { "in": [ - "v14", - "v15" + "v8", + "v9" ], "op": "add", "out": [ - "v16" + "v10" ] }, { "in": [ "0x00", - "v16" + "v10" ], "op": "slt", "out": [ - "v17" + "v11" ] } ], From 74ea280441897d33b28b3838eed8be984ebd1481 Mon Sep 17 00:00:00 2001 From: ParKing666 <123419961+ParKing666@users.noreply.github.com> Date: Mon, 27 Oct 2025 10:27:35 +0100 Subject: [PATCH 066/243] Update smtchecker.rst: fix int128 range is from -2^127 to 2^127-1, was ^128. In the text description of the State Properties paragraph: [...] As an example, consider a 2D grid where both axis have coordinates in the range (-2^128, 2^128 - 1). [...] In the example: [...] require(x > type(int128).min && x < type(int128).max); require(y > type(int128).min && y < type(int128).max); [...] With int128 we use a bit for the sign, then 127 bits remain for the value. Changed in the text description to be coherent: ...in the range (-2^127, 2^127 - 1). --- docs/smtchecker.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/smtchecker.rst b/docs/smtchecker.rst index 69b620dbc3..4f1b49c8d0 100644 --- a/docs/smtchecker.rst +++ b/docs/smtchecker.rst @@ -263,7 +263,7 @@ A common type of properties in smart contracts are properties that involve the state of the contract. Multiple transactions might be needed to make an assertion fail for such a property. -As an example, consider a 2D grid where both axis have coordinates in the range (-2^128, 2^128 - 1). +As an example, consider a 2D grid where both axis have coordinates in the range (-2^127, 2^127 - 1). Let us place a robot at position (0, 0). The robot can only move diagonally, one step at a time, and cannot move outside the grid. The robot's state machine can be represented by the smart contract below. From b5de13ae07686e0e1f1d69a76322efccf7941639 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Fri, 24 Oct 2025 12:49:31 +0200 Subject: [PATCH 067/243] CI: add arm image to buildpack deps, make runs-on explicit --- .github/workflows/buildpack-deps.yml | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/buildpack-deps.yml b/.github/workflows/buildpack-deps.yml index d4e7c5a7e8..528722534f 100644 --- a/.github/workflows/buildpack-deps.yml +++ b/.github/workflows/buildpack-deps.yml @@ -1,6 +1,7 @@ name: buildpack-deps permissions: + packages: write pull-requests: write on: @@ -11,20 +12,31 @@ on: - 'scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz' - 'scripts/docker/buildpack-deps/Dockerfile.ubuntu2404' - 'scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang' + - 'scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.arm' jobs: buildpack-deps: + runs-on: ${{ matrix.runs-on }} + env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} DOCKER_REPOSITORY: ghcr.io/argotorg/solidity-buildpack-deps IMAGE_NAME: buildpack-deps - runs-on: ubuntu-latest - strategy: fail-fast: false matrix: - image_variant: [emscripten, ubuntu.clang.ossfuzz, ubuntu2404, ubuntu2404.clang] + include: + - image_variant: emscripten + runs-on: ubuntu-latest + - image_variant: ubuntu.clang.ossfuzz + runs-on: ubuntu-latest + - image_variant: ubuntu2404 + runs-on: ubuntu-latest + - image_variant: ubuntu2404.clang + runs-on: ubuntu-latest + - image_variant: ubuntu2404.arm + runs-on: ubuntu-24.04-arm steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 From b0b8857b795944b81a6f5aa72baf31551aa80f68 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Fri, 24 Oct 2025 12:35:59 +0200 Subject: [PATCH 068/243] CI: add arm image --- .../ci/buildpack-deps_test_ubuntu2404.arm.sh | 1 + .../buildpack-deps/Dockerfile.ubuntu2404.arm | 111 ++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 120000 scripts/ci/buildpack-deps_test_ubuntu2404.arm.sh create mode 100644 scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.arm diff --git a/scripts/ci/buildpack-deps_test_ubuntu2404.arm.sh b/scripts/ci/buildpack-deps_test_ubuntu2404.arm.sh new file mode 120000 index 0000000000..c07a74de4f --- /dev/null +++ b/scripts/ci/buildpack-deps_test_ubuntu2404.arm.sh @@ -0,0 +1 @@ +build.sh \ No newline at end of file diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.arm b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.arm new file mode 100644 index 0000000000..a24549d4b8 --- /dev/null +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.arm @@ -0,0 +1,111 @@ +# vim:syntax=dockerfile +#------------------------------------------------------------------------------ +# Dockerfile for building and testing Solidity Compiler on CI +# Target: Ubuntu 24.04 (Noble) +# URL: https://hub.docker.com/r/ethereum/solidity-buildpack-deps +# +# This file is part of solidity. +# +# solidity is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# solidity is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with solidity. If not, see +# +# (c) 2016-2025 solidity contributors. +#------------------------------------------------------------------------------ +FROM --platform=linux/arm64 buildpack-deps:noble AS base +LABEL version="1" + +ARG DEBIAN_FRONTEND=noninteractive + +# From Python3.11, pip requires a virtual environment, and will thus terminate when installing packages globally. +# Since we're building this image from scratch, it's perfectly fine to use the below flag. +ENV PIP_BREAK_SYSTEM_PACKAGES=1 + +RUN <<-EOF + set -ex + z3_version="4.13.3" + dist=$(grep DISTRIB_CODENAME /etc/lsb-release | cut -d= -f2) + echo "deb http://ppa.launchpad.net/ethereum/cpp-build-deps/ubuntu $dist main" >> /etc/apt/sources.list + apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1c52189c923f6ca9 + apt-get update + apt-get install --quiet=2 --no-install-recommends \ + build-essential \ + cmake \ + jq \ + libboost-filesystem-dev \ + libboost-program-options-dev \ + libboost-system-dev \ + libboost-test-dev \ + libcln-dev \ + locales-all \ + lsof \ + ninja-build \ + python3-pip \ + python3-sphinx \ + software-properties-common \ + sudo \ + unzip \ + zip + pip3 install \ + codecov \ + colorama \ + deepdiff \ + parsec \ + pygments-lexer-solidity \ + pylint \ + requests \ + tabulate \ + z3-solver=="${z3_version}" +EOF + +# Eldarica +RUN <<-EOF + set -ex + apt-get update + apt-get install --quiet=2 openjdk-11-jre + eldarica_version="2.1" + wget "https://github.com/uuverifiers/eldarica/releases/download/v${eldarica_version}/eldarica-bin-${eldarica_version}.zip" -O /opt/eld_binaries.zip + test "$(sha256sum /opt/eld_binaries.zip)" = "0ac43f45c0925383c9d2077f62bbb515fd792375f3b2b101b30c9e81dcd7785c /opt/eld_binaries.zip" + unzip /opt/eld_binaries.zip -d /opt + rm -f /opt/eld_binaries.zip +EOF + +# CVC5 +RUN <<-EOF + set -ex + cvc5_version="1.2.0" + cvc5_archive_name="cvc5-Linux-arm64-static" + wget "https://github.com/cvc5/cvc5/releases/download/cvc5-${cvc5_version}/${cvc5_archive_name}.zip" -O /opt/cvc5.zip + test "$(sha256sum /opt/cvc5.zip)" = "142ac177af1ce37922308f69fe519d06e6cb4d0a62a312f28c25001e94c2fb56 /opt/cvc5.zip" + unzip -j /opt/cvc5.zip "${cvc5_archive_name}/bin/cvc5" -d /usr/bin + rm -f /opt/cvc5.zip +EOF + +FROM base AS libraries + +# EVMONE (built from source, no arm release available) +RUN <<-EOF + set -ex + git clone --depth=1 --recurse-submodules --branch v0.16.0 https://github.com/ipsilon/evmone.git /tmp/evmone + mkdir -p /tmp/evmone/build + cd /tmp/evmone/build + cmake .. + make -j + make install +EOF + +FROM base +COPY --from=libraries /usr/lib /usr/lib +COPY --from=libraries /usr/bin /usr/bin +COPY --from=libraries /usr/include /usr/include +COPY --from=libraries /opt/eldarica /opt/eldarica +ENV PATH="$PATH:/opt/eldarica" From 43ecb694853ebac08fb46eb588aa6f30e73d4ad4 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Fri, 24 Oct 2025 13:28:09 +0200 Subject: [PATCH 069/243] ci: build linux arm static binaries --- .circleci/compare_bytecode_reports.sh | 1 + .circleci/config.yml | 70 +++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/.circleci/compare_bytecode_reports.sh b/.circleci/compare_bytecode_reports.sh index 7574b0656c..529d5fc79f 100755 --- a/.circleci/compare_bytecode_reports.sh +++ b/.circleci/compare_bytecode_reports.sh @@ -28,6 +28,7 @@ no_cli_platforms=( ) native_platforms=( ubuntu2004-static + ubuntu_arm ubuntu osx osx_intel diff --git a/.circleci/config.yml b/.circleci/config.yml index 76f349a42d..f0c0c1fd31 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -15,6 +15,10 @@ parameters: type: string # ghcr.io/argotorg/solidity-buildpack-deps:ubuntu2404-6 default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:c0412c53e59ce0c96bde4c08e7332ea12e4cadba9bbac829621947897fa21272" + ubuntu-2404-arm-docker-image: + type: string + # ghcr.io/argotorg/solidity-buildpack-deps:ubuntu2404.arm-1 + default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:6cdb928fa8743d0b5d515c2c489b8d545c541f2370af28d5d3a71532056a0f22" ubuntu-2404-clang-docker-image: type: string # ghcr.io/argotorg/solidity-buildpack-deps:ubuntu2404.clang-7 @@ -606,6 +610,24 @@ defaults: MAKEFLAGS: -j 5 CPUs: 5 + - base_ubuntu2404_arm_medium: &base_ubuntu2404_arm_medium + docker: + - image: << pipeline.parameters.ubuntu-2404-arm-docker-image >> + resource_class: arm.medium + environment: &base_ubuntu2404_arm_medium_env + TERM: xterm + MAKEFLAGS: -j 2 + CPUs: 2 + + - base_ubuntu2404_arm_large: &base_ubuntu2404_arm_large + docker: + - image: << pipeline.parameters.ubuntu-2404-arm-docker-image >> + resource_class: arm.large + environment: &base_ubuntu2404_arm_large_env + TERM: xterm + MAKEFLAGS: -j 7 + CPUs: 7 + - base_ubuntu2404_xlarge: &base_ubuntu2404_xlarge <<: *base_ubuntu2404 resource_class: xlarge @@ -1072,6 +1094,31 @@ jobs: - solc/solc-static-linux - matrix_notify_failure_unless_pr + b_ubu_static_arm: + <<: *base_ubuntu2404_arm_large + environment: + <<: *base_ubuntu2404_arm_large_env + CMAKE_OPTIONS: -DCMAKE_BUILD_TYPE=Release -DSOLC_LINK_STATIC=ON + steps: + - checkout + - run_build + - run: + name: strip binary + command: strip build/solc/solc + - store_artifacts: + path: build/solc/solc + destination: solc-static-linux-arm + - run: mv build/solc/solc build/solc/solc-static-linux-arm + - run: mv build/solc/soltest build/solc/soltest-linux-arm + - run: mv build/solc/solfuzzer build/solc/solfuzzer-linux-arm + - persist_to_workspace: + root: build + paths: + - solc/solc-static-linux-arm + - test/soltest-linux-arm + - test/tools/solfuzzer-linux-arm + - matrix_notify_failure_unless_pr + b_ubu_min_req: <<: *base_ubuntu2404_large steps: @@ -1770,6 +1817,22 @@ jobs: binary_path: "build/solc/Release/solc.exe" preset: "<< parameters.preset >>" + b_bytecode_ubu_arm: + parameters: + preset: + type: string + <<: *base_ubuntu2404_arm_medium + parallelism: 2 # For prepare_bytecode_report + steps: + - checkout + - attach_workspace: + at: build + - prepare_bytecode_report: + label: "ubuntu_arm" + binary_type: native + binary_path: "build/solc/solc-static-linux-arm" + preset: "<< parameters.preset >>" + b_bytecode_ems: parameters: preset: @@ -1910,6 +1973,7 @@ workflows: # Static build - b_ubu_static: *requires_nothing + - b_ubu_static_arm: *requires_nothing # Ubuntu build and tests - b_ubu: *requires_nothing @@ -1990,6 +2054,11 @@ workflows: matrix: *bytecode_compare_preset_matrix requires: - b_ubu + - b_bytecode_ubu_arm: + <<: *on_all_tags_and_branches + matrix: *bytecode_compare_preset_matrix + requires: + - b_ubu_static_arm - b_bytecode_win: <<: *on_all_tags_and_branches matrix: *bytecode_compare_preset_matrix @@ -2015,6 +2084,7 @@ workflows: requires: - b_bytecode_ubu_static - b_bytecode_ubu + - b_bytecode_ubu_arm - b_bytecode_win - b_bytecode_osx - b_bytecode_osx_intel From be37565066eefb8fa79740698d39d11c8a704b2e Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 27 Oct 2025 21:10:49 +0100 Subject: [PATCH 070/243] Enable Linux arm64 binaries for testing and releases. --- .circleci/config.yml | 42 +++++++++++++++++++++++++----------------- Changelog.md | 1 + ReleaseChecklist.md | 4 ++-- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f0c0c1fd31..476b72eb38 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -284,6 +284,16 @@ commands: - test/soltest - test/tools/solfuzzer + persist_executables_to_workspace_arm: + description: Persist compiled target executables to workspace on Linux arm64 + steps: + - persist_to_workspace: + root: build + paths: + - solc/solc-static-linux-arm + - test/soltest-linux-arm + - test/tools/solfuzzer-linux-arm + persist_executables_to_workspace_osx: description: Persist compiled target executables to workspace on macOS steps: @@ -1109,14 +1119,9 @@ jobs: path: build/solc/solc destination: solc-static-linux-arm - run: mv build/solc/solc build/solc/solc-static-linux-arm - - run: mv build/solc/soltest build/solc/soltest-linux-arm - - run: mv build/solc/solfuzzer build/solc/solfuzzer-linux-arm - - persist_to_workspace: - root: build - paths: - - solc/solc-static-linux-arm - - test/soltest-linux-arm - - test/tools/solfuzzer-linux-arm + - run: mv build/test/soltest build/test/soltest-linux-arm + - run: mv build/test/tools/solfuzzer build/test/tools/solfuzzer-linux-arm + - persist_executables_to_workspace_arm - matrix_notify_failure_unless_pr b_ubu_min_req: @@ -1874,10 +1879,11 @@ jobs: name: Gather and rename binaries from dependent jobs command: | mkdir github/ - cp workspace/solc/solc-static-linux github/solc-static-linux - cp workspace/build/solc/solc github/solc-macos - cp workspace/solc/Release/solc.exe github/solc-windows.exe - cp workspace/soljson.js github/soljson.js + cp workspace/solc/solc-static-linux github/solc-static-linux + cp workspace/solc/solc-static-linux-arm github/solc-static-linux-arm + cp workspace/build/solc/solc github/solc-macos + cp workspace/solc/Release/solc.exe github/solc-windows.exe + cp workspace/soljson.js github/soljson.js cd github/ tar --create --file ../github-binaries.tar * @@ -1891,12 +1897,13 @@ jobs: sed -En 's/^Version: ([0-9.]+.*\+commit\.[0-9a-f]+(\.mod)?).*$/\1/p' ) - mkdir -p solc-bin/{linux-amd64,macosx-amd64,windows-amd64,bin} + mkdir -p solc-bin/{linux-amd64,linux-arm64,macosx-amd64,windows-amd64,bin} - mv github/solc-static-linux "solc-bin/linux-amd64/solc-linux-amd64-v${full_version}" - mv github/solc-macos "solc-bin/macosx-amd64/solc-macosx-amd64-v${full_version}" - mv github/solc-windows.exe "solc-bin/windows-amd64/solc-windows-amd64-v${full_version}.exe" - mv github/soljson.js "solc-bin/bin/soljson-v${full_version}.js" + mv github/solc-static-linux-arm "solc-bin/linux-arm64/solc-linux-arm64-v${full_version}" + mv github/solc-static-linux "solc-bin/linux-amd64/solc-linux-amd64-v${full_version}" + mv github/solc-macos "solc-bin/macosx-amd64/solc-macosx-amd64-v${full_version}" + mv github/solc-windows.exe "solc-bin/windows-amd64/solc-windows-amd64-v${full_version}.exe" + mv github/soljson.js "solc-bin/bin/soljson-v${full_version}.js" cd solc-bin/ tar --create --file ../solc-bin-binaries.tar * @@ -2094,6 +2101,7 @@ workflows: - c_release_binaries: <<: *on_version_tags requires: + - b_ubu_static_arm - b_ubu_static - b_osx - b_win diff --git a/Changelog.md b/Changelog.md index 748495074e..f772f6dd8b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,7 @@ Bugfixes: * Yul Optimizer: Fix edge case in which invalid Yul code is produced by ExpressionSimplifier due to expressions being substituted that contain out-of-scope variables. Build System: +* Enable Linux arm64 binaries for testing and releases. * Update minimum version requirements of Boost to 1.83.0 for non-windows builds and of GCC and Clang to 13.3 and 18.1.3, respectively. Fixes infinite recursion on `boost::rational` comparison affecting compiler binaries built with GCC<14.0 and Boost<1.75. ### 0.8.30 (2025-05-07) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index fe751e896f..d8d63b09ae 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -69,7 +69,7 @@ At least a day before the release: ### Upload Release Artifacts and Publish Binaries - [ ] Take the source tarball (`solidity_x.x.x.tar.gz`) from `c_source_tarball` run of the tagged commit on Circle CI and upload it to the release page. - [ ] Take the `github-binaries.tar` tarball from `c_release_binaries` run of the tagged commit on Circle CI and add all binaries from it to the release page. - Make sure it contains four binaries: `solc-windows.exe`, `solc-macos`, `solc-static-linux` and `soljson.js`. + Make sure it contains five binaries: `solc-windows.exe`, `solc-macos`, `solc-static-linux`, `solc-static-linux-arm`, and `soljson.js`. - [ ] Take the `solc-bin-binaries.tar` tarball from `c_release_binaries` run of the tagged commit on Circle CI and add all binaries from it to solc-bin. - [ ] Run `npm install` if you've got a clean checkout of the solc-bin repo. - [ ] Run `npm run update -- --reuse-hashes` in `solc-bin` and verify that the script has updated `list.js`, `list.txt` and `list.json` files correctly and that symlinks to the new release have been added in `solc-bin/wasm/` and `solc-bin/emscripten-wasm32/`. @@ -134,7 +134,7 @@ At least a day before the release: - [ ] Wait for the CI runs on the tag itself. - [ ] Take the source tarball (`solidity_x.x.x-pre.N.tar.gz`) from `c_source_tarball` run of the tagged commit on Circle CI and upload it to the release page. - [ ] Take the `github-binaries.tar` tarball from `c_release_binaries` run of the tagged commit on Circle CI and add all binaries from it to the release page. - Make sure it contains four binaries: `solc-windows.exe`, `solc-macos`, `solc-static-linux` and `soljson.js`. + Make sure it contains five binaries: `solc-windows.exe`, `solc-macos`, `solc-static-linux`, `solc-static-linux-arm` and `soljson.js`. - [ ] Take the `solc-bin-binaries.tar` tarball from `c_release_binaries` run of the tagged commit on Circle CI and add all binaries from it to solc-bin. - [ ] Run `npm install` if you've got a clean checkout of the solc-bin repo. - [ ] Run `npm run update -- --reuse-hashes` in `solc-bin` and verify that the script has updated `list.js`, `list.txt` and `list.json` files correctly and that symlinks to the new release have been added in `solc-bin/wasm/` and `solc-bin/emscripten-wasm32/`. From 45fdbccf5b187723f4be66b0e8e1e1e713577b3b Mon Sep 17 00:00:00 2001 From: ParKing666 <123419961+ParKing666@users.noreply.github.com> Date: Sun, 26 Oct 2025 17:57:11 +0100 Subject: [PATCH 071/243] Fix: max int8 has 7 bits set to 1, not 8 bits before: 0000...0000 1111 1111 after: 0000...0000 0111 1111 --- docs/internals/variable_cleanup.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/internals/variable_cleanup.rst b/docs/internals/variable_cleanup.rst index bab3c8bef2..6def08f227 100644 --- a/docs/internals/variable_cleanup.rst +++ b/docs/internals/variable_cleanup.rst @@ -91,7 +91,7 @@ Positive 0000...0000 0000 0001 0000...0000 0000 0010 .... - 0000...0000 1111 1111 + 0000...0000 0111 1111 The compiler will ``signextend`` the sign bit, which is 1 for negative and 0 for positive values, overwriting the higher bits: From 2129c0d6e5c7327f8bf2dcebfb13b080a68d509e Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Sat, 1 Nov 2025 14:02:57 +0100 Subject: [PATCH 072/243] docs: fix links to solc-bin github page and remix offline installation --- docs/installing-solidity.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 6f3dfbb7fb..bda0d102bf 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -32,7 +32,7 @@ Remix `Access Remix online `_, you do not need to install anything. If you want to use it without connection to the Internet, go to -https://github.com/argotorg/remix-live/tree/gh-pages#readme and follow the instructions on that page. +https://github.com/ethereum/remix-live/tree/gh-pages#readme and follow the instructions on that page. Remix is also a convenient option for testing nightly builds without installing multiple Solidity versions. @@ -291,14 +291,14 @@ This means that: .. warning:: - The binaries are also available at https://ethereum.github.io/solc-bin/ but this page + The binaries are also available at https://argotorg.github.io/solc-bin/ but this page stopped being updated just after the release of version 0.7.2, will not receive any new releases or nightly builds for any platform and does not serve the new directory structure, including non-emscripten builds. If you are using it, please switch to https://binaries.soliditylang.org, which is a drop-in replacement. This allows us to make changes to the underlying hosting in a transparent way and - minimize disruption. Unlike the ``ethereum.github.io`` domain, which we do not have any control + minimize disruption. Unlike the ``argotorg.github.io`` domain, which we do not have any control over, ``binaries.soliditylang.org`` is guaranteed to work and maintain the same URL structure in the long-term. From 9d2a907c2ffa903ae928105e07f44a573b665f18 Mon Sep 17 00:00:00 2001 From: Doryu Date: Sun, 2 Nov 2025 20:28:19 +0100 Subject: [PATCH 073/243] Update isolate_tests.py --- scripts/wasm-rebuild/docker-scripts/isolate_tests.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/wasm-rebuild/docker-scripts/isolate_tests.py b/scripts/wasm-rebuild/docker-scripts/isolate_tests.py index cc84f89bb2..fa7b68a64a 100755 --- a/scripts/wasm-rebuild/docker-scripts/isolate_tests.py +++ b/scripts/wasm-rebuild/docker-scripts/isolate_tests.py @@ -7,8 +7,7 @@ import re import os import hashlib -# Pylint for some reason insists that isfile() is unused -from os.path import join, isfile # pylint: disable=unused-import +from os.path import join def extract_test_cases(path): From dff13e351561e51c75baa320904456ff8cc55099 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Fri, 31 Oct 2025 10:31:35 +0100 Subject: [PATCH 074/243] SSACFG backend: add stack abstraction --- libyul/CMakeLists.txt | 4 +- libyul/backends/evm/ssa/Stack.cpp | 97 ++++++++++ libyul/backends/evm/ssa/Stack.h | 305 ++++++++++++++++++++++++++++++ 3 files changed, 405 insertions(+), 1 deletion(-) create mode 100644 libyul/backends/evm/ssa/Stack.cpp create mode 100644 libyul/backends/evm/ssa/Stack.h diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 15b06a6235..ff94a3905e 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -88,8 +88,10 @@ add_library(yul backends/evm/ssa/SSACFG.h backends/evm/ssa/SSACFGBuilder.cpp backends/evm/ssa/SSACFGBuilder.h - backends/evm/ssa/SSACFGJsonExporter.h backends/evm/ssa/SSACFGJsonExporter.cpp + backends/evm/ssa/SSACFGJsonExporter.h + backends/evm/ssa/Stack.cpp + backends/evm/ssa/Stack.h optimiser/ASTCopier.cpp optimiser/ASTCopier.h optimiser/ASTWalker.cpp diff --git a/libyul/backends/evm/ssa/Stack.cpp b/libyul/backends/evm/ssa/Stack.cpp new file mode 100644 index 0000000000..17c8c3f446 --- /dev/null +++ b/libyul/backends/evm/ssa/Stack.cpp @@ -0,0 +1,97 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include + +#include +#include + +namespace +{ +size_t junkTailSize(solidity::yul::ssa::StackData const& _stackData) +{ + auto junkTail = _stackData | ranges::views::take_while([](auto const& slot) { return slot.isJunk(); }); + return static_cast(ranges::distance(junkTail)); +} +} + +namespace solidity::yul::ssa +{ + +std::string slotToString(StackSlot const& _slot) +{ + switch (_slot.kind()) + { + case StackSlot::Kind::ValueID: + if (_slot.isLiteralValueID()) + return fmt::format("lit{}", _slot.valueID().value()); + else + return fmt::format("v{}", _slot.valueID().value()); + case StackSlot::Kind::Junk: + return "JUNK"; + case StackSlot::Kind::FunctionCallReturnLabel: + return fmt::format("FunctionCallReturnLabel[{}]", _slot.functionCallReturnLabel()); + case StackSlot::Kind::FunctionReturnLabel: + return fmt::format("ReturnLabel[{}]", _slot.functionReturnLabel()); + } + util::unreachable(); +} + +std::string slotToString(StackSlot const& _slot, SSACFG const& _cfg) +{ + if (_slot.kind() == StackSlot::Kind::ValueID) + return _slot.valueID().str(_cfg); + + return slotToString(_slot); +} + +std::string stackToString(StackData const& _stackData) +{ + auto const numJunk = junkTailSize(_stackData); + if (numJunk > 0) + return fmt::format( + "[JUNK x {}, {}]", + numJunk, + fmt::join(_stackData | ranges::views::drop(numJunk) | ranges::views::transform([&](auto const& _slot) { return slotToString(_slot); }), ", ") + ); + + return fmt::format( + "[{}]", + fmt::join(_stackData | ranges::views::transform([&](auto const& _slot) { return slotToString(_slot); }), ", ") + ); +} + +std::string stackToString(StackData const& _stackData, SSACFG const& _cfg) +{ + auto const numJunk = junkTailSize(_stackData); + if (numJunk > 0) + return fmt::format( + "[JUNK x {}, {}]", + numJunk, + fmt::join(_stackData | ranges::views::drop(numJunk) | ranges::views::transform([&](auto const& _slot) { return slotToString(_slot, _cfg); }), ", ") + ); + + return fmt::format( + "[{}]", + fmt::join(_stackData | ranges::views::transform([&](auto const& _slot) { return slotToString(_slot, _cfg); }), ", ") + ); +} + +} diff --git a/libyul/backends/evm/ssa/Stack.h b/libyul/backends/evm/ssa/Stack.h new file mode 100644 index 0000000000..20207ebc7e --- /dev/null +++ b/libyul/backends/evm/ssa/Stack.h @@ -0,0 +1,305 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include + +#include +#include + +#include +#include + +namespace solidity::yul +{ + +struct FunctionCall; + +namespace ssa +{ + +/// Registry for tracking function call sites. +/// +/// Maps FunctionCall AST nodes to unique numeric IDs. These IDs are used +/// to generate return labels for function calls in the EVM bytecode. +class CallSites +{ +public: + using CallSiteID = std::uint32_t; + + std::optional callSiteID(FunctionCall const* _functionCall) const + { + if (auto const it = ranges::find(m_data, _functionCall); it != m_data.end()) + return static_cast(std::distance(m_data.begin(), it)); + return std::nullopt; + } + + FunctionCall const& functionCall(CallSiteID _callSite) const + { + yulAssert(_callSite < m_data.size()); + return *m_data[_callSite]; + } + + CallSiteID addCallSite(FunctionCall const* _functionCall) + { + if (auto const id = callSiteID(_functionCall)) + return *id; + yulAssert(_functionCall); + m_data.emplace_back(_functionCall); + return static_cast(m_data.size() - 1); + } +private: + std::vector m_data; +}; + +/// A discriminated union corresponding to a single EVM stack slot. +/// Can represent: +/// - ValueID: SSA values (including literals) +/// - Junk: Placeholder/unused values +/// - FunctionCallReturnLabel: Return addresses for function calls +/// - FunctionReturnLabel: Identifies the calling function's graph +/// +/// Memory layout is optimized: 8 bytes size for cache efficiency, trivially copyable, standard layout, trivial +class StackSlot +{ +public: + enum struct Kind: std::uint8_t + { + ValueID, // u32 + Junk, // empty + FunctionCallReturnLabel, // index into corresponding stack layout's call sites + FunctionReturnLabel // identifying the function graph via ControlFlow + }; + + constexpr StackSlot() = default; + constexpr StackSlot(StackSlot const&) = default; + constexpr StackSlot(StackSlot&&) = default; + constexpr StackSlot& operator=(StackSlot const&) = default; + constexpr StackSlot& operator=(StackSlot&&) = default; + + constexpr bool isValueID() const noexcept { return kind() == Kind::ValueID; } + constexpr bool isLiteralValueID() const noexcept { return m_valueIdKind == SSACFG::ValueId::Kind::Literal; } + constexpr bool isFunctionReturnLabel() const noexcept { return kind() == Kind::FunctionReturnLabel; } + constexpr bool isFunctionCallReturnLabel() const noexcept { return kind() == Kind::FunctionCallReturnLabel; } + constexpr bool isJunk() const noexcept { return kind() == Kind::Junk; } + constexpr Kind kind() const noexcept { return m_kind; } + + ControlFlow::FunctionGraphID functionReturnLabel() const { yulAssert(isFunctionReturnLabel()); return m_payload; } + CallSites::CallSiteID functionCallReturnLabel() const { yulAssert(isFunctionCallReturnLabel()); return m_payload; } + SSACFG::ValueId valueID() const { yulAssert(isValueID()); return {m_payload, m_valueIdKind}; } + + static constexpr StackSlot makeJunk() { return {0, Kind::Junk}; } + static constexpr StackSlot makeValueID(SSACFG::ValueId const& _valueID) { return {_valueID.value(), Kind::ValueID, _valueID.kind()}; } + static constexpr StackSlot makeFunctionReturnLabel(ControlFlow::FunctionGraphID const _graphID) { return {_graphID, Kind::FunctionReturnLabel}; } + static constexpr StackSlot makeFunctionCallReturnLabel(CallSites::CallSiteID const _callSiteID) { return {_callSiteID, Kind::FunctionCallReturnLabel}; } + + auto operator<=>(StackSlot const&) const = default; +private: + constexpr StackSlot(std::uint32_t const _payload, Kind const _kind, SSACFG::ValueId::Kind const _valueIdKind = SSACFG::ValueId::Kind::Unreachable): + m_payload(_payload), + m_kind(_kind), + m_valueIdKind(_valueIdKind) + {} + + /// interpretation depends on kind + std::uint32_t m_payload; + Kind m_kind; + SSACFG::ValueId::Kind m_valueIdKind; +}; +static_assert(sizeof(StackSlot) == 8, "Want cache efficiency, benchmark this if you go beyond 8 bytes"); +static_assert(std::is_trivially_copyable_v, "Should be able to use memcpy semantics"); +static_assert(std::is_standard_layout_v, "Want to have a predictable layout"); +static_assert(std::is_trivial_v, "Want to have no init/cpy overhead"); + +using StackData = std::vector; +std::string slotToString(StackSlot const& _slot); +std::string slotToString(StackSlot const& _slot, SSACFG const& _cfg); +std::string stackToString(StackData const& _stackData, SSACFG const& _cfg); +std::string stackToString(StackData const& _stackData); + +template +concept StackManipulationCallbackConcept = requires( + StackManipulationCallback& _callback, + StackSlot _slot, + size_t _depth +) +{ + { _callback.swap(_depth) } -> std::same_as; + { _callback.dup(_depth) } -> std::same_as; + { _callback.push(_slot) } -> std::same_as; + { _callback.pop() } -> std::same_as; +}; + +struct NoOpStackManipulationCallbacks +{ + static void swap(size_t) {} + static void dup(size_t) {} + static void push(StackSlot const&) {} + static void pop() {} +}; +static_assert(StackManipulationCallbackConcept); + +template< + StackManipulationCallbackConcept CallbacksType = NoOpStackManipulationCallbacks +> +class Stack +{ + static size_t constexpr reachableStackDepth = 16; +public: + using Callbacks = CallbacksType; + + using Slot = StackSlot; + using Data = StackData; + + /// Array index into stack from the bottom (offset 0 = bottom). + /// Natural for array-like access and iteration; used when treating the stack as a data structure. + struct Offset + { + explicit constexpr Offset(size_t _value) : value(_value) {} + size_t value; + auto operator<=>(Offset const&) const = default; + }; + // comparison operations with size_t + friend constexpr auto operator<=>(Offset lhs, size_t rhs) noexcept { return lhs.value <=> rhs; } + friend constexpr auto operator<=>(size_t lhs, Offset rhs) noexcept { return lhs <=> rhs.value; } + + /// Distance from the stack top (depth 0 = top). + /// Natural for stack operations (SWAP1 = swap with depth 1); used for operations that + /// conceptually work "from the top". + struct Depth + { + explicit constexpr Depth(size_t _value) : value(_value) {} + size_t value; + auto operator<=>(Depth const&) const = default; + }; + // comparison operations with size_t + friend constexpr auto operator<=>(Depth lhs, size_t rhs) noexcept { return lhs.value <=> rhs; } + friend constexpr auto operator<=>(size_t lhs, Depth rhs) noexcept { return lhs <=> rhs.value; } + + Stack( + Data& _data, + Callbacks _callbacks + ): + m_data(&_data), + m_callbacks(std::move(_callbacks)) + {} + + Slot const& top() const + { + yulAssert(!m_data->empty()); + return m_data->back(); + } + + void swap(Depth const& _depth) { swap(depthToOffset(_depth)); } + void swap(Offset const& _offset) + { + yulAssert(swapReachable(_offset), "Stack too deep"); + std::swap((*m_data)[_offset.value], m_data->back()); + if constexpr (!std::is_same_v) + m_callbacks.swap(offsetToDepth(_offset).value); + } + + /// if the stack state needs to be updated without notifying the callback, the template parameter can be set to false + template + void pop() + { + yulAssert(!m_data->empty()); + m_data->pop_back(); + if constexpr (callback && !std::is_same_v) + m_callbacks.pop(); + } + + /// if the stack state needs to be updated without notifying the callback, the template parameter can be set to false + template + void push(Slot const& _slot) + { + m_data->emplace_back(_slot); + if constexpr (callback && !std::is_same_v) + m_callbacks.push(_slot); + } + + void dup(Depth const& _depth) { dup(depthToOffset(_depth)); } + void dup(Offset const& _offset) + { + yulAssert(dupReachable(_offset), "Stack too deep"); + m_data->push_back((*m_data)[_offset.value]); + if constexpr (!std::is_same_v) + m_callbacks.dup(offsetToDepth(_offset).value + 1); + } + + bool dupReachable(Offset const& _offset) const noexcept { return dupReachable(offsetToDepth(_offset)); } + bool dupReachable(Depth const& _depth) const noexcept { return _depth < size() && _depth.value + 1 <= reachableStackDepth; } + bool swapReachable(Offset const& _offset) const noexcept { return swapReachable(offsetToDepth(_offset)); } + bool swapReachable(Depth const& _depth) const noexcept { return _depth < size() && 1 <= _depth.value && _depth.value <= reachableStackDepth; } + + void declareJunk(Depth const& _depth) { (*m_data)[depthToOffset(_depth).value] = Slot::makeJunk(); } + + Slot const& slot(Depth const& _depth) const { return (*m_data)[depthToOffset(_depth).value]; } + Slot const& slot(Offset const& _offset) const { return slot(offsetToDepth(_offset)); } + bool empty() const noexcept { return size() == 0; } + size_t size() const noexcept { return m_data->size(); } + + std::optional findSlotDepth(Slot const& _value) const + { + auto rview = *this | ranges::views::reverse; + auto it = ranges::find(rview, _value); + + if (it == ranges::end(rview)) + return std::nullopt; + + return Depth{static_cast(std::distance(ranges::begin(rview), it))}; + } + + static bool constexpr canBeFreelyGenerated(Slot const& _slot) + { + return _slot.isLiteralValueID() || _slot.isJunk() || _slot.isFunctionCallReturnLabel(); + } + + Slot const& operator[](Offset const& _index) const noexcept { return (*m_data)[_index.value]; } + auto begin() const { return ranges::begin(*m_data); } + auto end() const { return ranges::end(*m_data); } + + Data const& data() const + { + return *m_data; + } + + Callbacks const& callbacks() const { return m_callbacks; } + + /// index scheme conversion offset -> depth + Depth offsetToDepth(Offset const& _offset) const + { + yulAssert(_offset < size(), "Offset out of range"); + return Depth{size() - _offset.value - 1}; + } + /// index scheme conversion depth -> offset + Offset depthToOffset(Depth const& _depth) const + { + yulAssert(_depth < size(), "Depth out of range"); + return Offset{size() - _depth.value - 1}; + } + +private: + Data* m_data; + Callbacks m_callbacks; +}; + +} +} From b65295c39d03a85e37280241f43f19ef0403e297 Mon Sep 17 00:00:00 2001 From: Doryu Date: Tue, 4 Nov 2025 20:04:51 +0100 Subject: [PATCH 075/243] Update parse_eth_gas_report.py --- scripts/externalTests/parse_eth_gas_report.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/scripts/externalTests/parse_eth_gas_report.py b/scripts/externalTests/parse_eth_gas_report.py index c5cc58e587..ea8cbba66a 100755 --- a/scripts/externalTests/parse_eth_gas_report.py +++ b/scripts/externalTests/parse_eth_gas_report.py @@ -115,13 +115,11 @@ def to_json(self): def parse_bool(input_string: str) -> bool: - if input_string == 'true': - return True - elif input_string == 'false': - return True - else: + if input_string not in ('true', 'false'): raise ValueError(f"Invalid boolean value: '{input_string}'") + return input_string == 'true' + def parse_optional_int(input_string: str, default: Optional[int] = None) -> Optional[int]: if input_string.strip() == '-': From bb3013529678e50785b58f222f92daffa00a1f69 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Mon, 1 Dec 2025 15:21:50 +0100 Subject: [PATCH 076/243] Bump ext-uniswap node to large --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 476b72eb38..1bb7870627 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -831,7 +831,7 @@ defaults: project: uniswap binary_type: native image: cimg/node:lts - resource_class: medium # Tests run out of memory on a smaller machine + resource_class: large # Tests run out of memory on a smaller machine - job_native_test_ext_prb_math: &job_native_test_ext_prb_math <<: *requires_b_ubu_static From cd1f865427f4ad83229e3c4fddec409073073124 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Fri, 21 Nov 2025 12:22:10 -0300 Subject: [PATCH 077/243] Set osaka as default EVM version --- .circleci/config.yml | 2 +- .circleci/soltest_all.sh | 2 +- Changelog.md | 1 + docs/using-the-compiler.rst | 11 +++++---- liblangutil/EVMVersion.h | 2 +- scripts/externalTests/common.sh | 2 +- scripts/externalTests/runners/base.py | 2 +- scripts/tests.sh | 4 ++-- test/cmdlineTests/metadata/output | 2 +- .../standard_metadata/output.json | 2 +- .../output.json | 2 +- .../output.json | 2 +- .../output.json | 2 +- .../output.json | 4 ++-- .../output | 2 +- .../output | 2 +- test/libsolidity/StandardCompiler.cpp | 23 ++++++++++--------- 17 files changed, 35 insertions(+), 32 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1bb7870627..6255d2834e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -34,7 +34,7 @@ parameters: default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:fc53d68a4680ffa7d5f70164e13a903478964f15bcc07434d74833a05f4fbc19" evm-version: type: string - default: prague + default: osaka orbs: win: circleci/windows@2.2.0 diff --git a/.circleci/soltest_all.sh b/.circleci/soltest_all.sh index 86e62c333d..8d6fc486b8 100755 --- a/.circleci/soltest_all.sh +++ b/.circleci/soltest_all.sh @@ -38,7 +38,7 @@ EVMS_WITH_EOF=(osaka) # set EVM_VALUES to the default values. IFS=" " read -ra EVM_VALUES <<< "${1:-${DEFAULT_EVM_VALUES[@]}}" -DEFAULT_EVM=prague +DEFAULT_EVM=osaka OPTIMIZE_VALUES=(0 1) # TODO: EOF is marked as experimental in evmone. Reenable when proper handling for that is added here. EOF_VERSIONS=(0) diff --git a/Changelog.md b/Changelog.md index f772f6dd8b..72551a4f8b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Language Features: Compiler Features: * ethdebug: Experimental support for instructions and source locations under EOF. +* EVM: Set default EVM Version to `osaka`. Bugfixes: * Assembler: Fix not using a fixed-width type for IDs being assigned to subassemblies nested more than one level away, resulting in inconsistent `--asm-json` output between target architectures. diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index a3943ea94d..93d61af291 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -181,9 +181,9 @@ at each version. Backward compatibility is not guaranteed between each version. - Introduces ``blobhash()`` in inline assembly and a corresponding global function to retrieve versioned hashes of blobs associated with the transaction (see `EIP-4844 `_). - Opcode ``mcopy`` is available in assembly (see `EIP-5656 `_). - Opcodes ``tstore`` and ``tload`` are available in assembly (see `EIP-1153 `_). -- ``prague`` (**default**) -- ``osaka`` (**experimental**) - - Experimental compilation to EOF is available starting from this version. (`EIP-7692 `_) +- ``prague`` +- ``osaka`` (**default**) + - ``clz`` builtin function is available in inline assembly. (`EIP-7939 `_) .. index:: ! standard JSON, ! --standard-json .. _compiler-api: @@ -356,10 +356,11 @@ Input Description // Version of the EVM to compile for (optional). // Affects type checking and code generation. Can be homestead, // tangerineWhistle, spuriousDragon, byzantium, constantinople, - // petersburg, istanbul, berlin, london, paris, shanghai, cancun, prague (default) or osaka (experimental). - "evmVersion": "prague", + // petersburg, istanbul, berlin, london, paris, shanghai, cancun, prague or osaka (default). + "evmVersion": "osaka", // EVM Object Format version to compile for (optional, experimental). // Currently the only valid value is 1. If not specified, legacy non-EOF bytecode will be generated. + // Requires `evmVersion` >= osaka. "eofVersion": null, // Optional: Change compilation pipeline to go through the Yul intermediate representation. // This is false by default. diff --git a/liblangutil/EVMVersion.h b/liblangutil/EVMVersion.h index 799fd2e0fa..d4b25e1003 100644 --- a/liblangutil/EVMVersion.h +++ b/liblangutil/EVMVersion.h @@ -170,7 +170,7 @@ class EVMVersion Prague, Osaka, }; - static auto constexpr currentVersion = Version::Prague; + static auto constexpr currentVersion = Version::Osaka; constexpr EVMVersion(Version _version): m_version(_version) {} diff --git a/scripts/externalTests/common.sh b/scripts/externalTests/common.sh index 6e435da956..88c656ca23 100644 --- a/scripts/externalTests/common.sh +++ b/scripts/externalTests/common.sh @@ -22,7 +22,7 @@ set -e # Requires $REPO_ROOT to be defined and "${REPO_ROOT}/scripts/common.sh" to be included before. -CURRENT_EVM_VERSION=prague +CURRENT_EVM_VERSION=osaka AVAILABLE_PRESETS=( legacy-no-optimize diff --git a/scripts/externalTests/runners/base.py b/scripts/externalTests/runners/base.py index 20ba67ddb4..1158ebced3 100644 --- a/scripts/externalTests/runners/base.py +++ b/scripts/externalTests/runners/base.py @@ -41,7 +41,7 @@ from test_helpers import settings_from_preset from test_helpers import SettingsPreset -CURRENT_EVM_VERSION: str = "prague" +CURRENT_EVM_VERSION: str = "osaka" @dataclass class TestConfig: diff --git a/scripts/tests.sh b/scripts/tests.sh index 5ff962be65..22df9a3960 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -115,9 +115,9 @@ do for vm in $EVM_VERSIONS do FORCE_ABIV1_RUNS="no" - if [[ "$vm" == "prague" ]] + if [[ "$vm" == "osaka" ]] then - FORCE_ABIV1_RUNS="no yes" # run both in paris + FORCE_ABIV1_RUNS="no yes" # run both when testing the current EVM version fi for abiv1 in $FORCE_ABIV1_RUNS do diff --git a/test/cmdlineTests/metadata/output b/test/cmdlineTests/metadata/output index a62f2ed145..3e390b70a9 100644 --- a/test/cmdlineTests/metadata/output +++ b/test/cmdlineTests/metadata/output @@ -1,4 +1,4 @@ ======= input.sol:C ======= Metadata: -{"compiler":{"version": ""},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"input.sol":"C"},"evmVersion":"prague","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"input.sol":{"keccak256":"0x5cf617b1707a484e3c4bd59643013dec76ab7d75900b46855214729ae3e0ceb0","license":"GPL-3.0","urls":["bzz-raw://ac418a02dfadf87234150d3568f33269e3f49460345cb39300e017a6d755eff2","dweb:/ipfs/QmQq3owBu25x2WV46HB1WyKzJpxiAPecU7eMKqtXCF7eeS"]}},"version":1} +{"compiler":{"version": ""},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"input.sol":"C"},"evmVersion":"osaka","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"input.sol":{"keccak256":"0x5cf617b1707a484e3c4bd59643013dec76ab7d75900b46855214729ae3e0ceb0","license":"GPL-3.0","urls":["bzz-raw://ac418a02dfadf87234150d3568f33269e3f49460345cb39300e017a6d755eff2","dweb:/ipfs/QmQq3owBu25x2WV46HB1WyKzJpxiAPecU7eMKqtXCF7eeS"]}},"version":1} diff --git a/test/cmdlineTests/standard_metadata/output.json b/test/cmdlineTests/standard_metadata/output.json index 155e15db17..1435272786 100644 --- a/test/cmdlineTests/standard_metadata/output.json +++ b/test/cmdlineTests/standard_metadata/output.json @@ -2,7 +2,7 @@ "contracts": { "C": { "C": { - "metadata": "{\"compiler\":{\"version\":\"\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"C\":\"C\"},\"evmVersion\":\"prague\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"C\":{\"keccak256\":\"0x5cf617b1707a484e3c4bd59643013dec76ab7d75900b46855214729ae3e0ceb0\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://ac418a02dfadf87234150d3568f33269e3f49460345cb39300e017a6d755eff2\",\"dweb:/ipfs/QmQq3owBu25x2WV46HB1WyKzJpxiAPecU7eMKqtXCF7eeS\"]}},\"version\":1}" + "metadata": "{\"compiler\":{\"version\":\"\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"C\":\"C\"},\"evmVersion\":\"osaka\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"C\":{\"keccak256\":\"0x5cf617b1707a484e3c4bd59643013dec76ab7d75900b46855214729ae3e0ceb0\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://ac418a02dfadf87234150d3568f33269e3f49460345cb39300e017a6d755eff2\",\"dweb:/ipfs/QmQq3owBu25x2WV46HB1WyKzJpxiAPecU7eMKqtXCF7eeS\"]}},\"version\":1}" } } }, diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_empty_sequence_metadata/output.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_empty_sequence_metadata/output.json index 07087b30fd..7c0a853e6e 100644 --- a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_empty_sequence_metadata/output.json +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_empty_sequence_metadata/output.json @@ -2,7 +2,7 @@ "contracts": { "A": { "C": { - "metadata": "{\"compiler\":{\"version\":\"\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"A\":\"C\"},\"evmVersion\":\"prague\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"details\":{\"constantOptimizer\":false,\"cse\":false,\"deduplicate\":false,\"inliner\":false,\"jumpdestRemover\":true,\"orderLiterals\":false,\"peephole\":true,\"simpleCounterForLoopUncheckedIncrement\":true,\"yul\":false,\"yulDetails\":{\"optimizerSteps\":\":\"}},\"runs\":200},\"remappings\":[]},\"sources\":{\"A\":{\"keccak256\":\"0xb284c39999cb85b80be315a6e9e322adf67a783c66e91ba4439168694580a66d\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://098cee915fad095b8a996813768bd7d5e8c9e40c405e8c43d0572bb7bbc17334\",\"dweb:/ipfs/QmZmUzvSryrrD7pJ9S32iQnEWn4QBL4J1NdbQqL2Xc3yTr\"]}},\"version\":1}" + "metadata": "{\"compiler\":{\"version\":\"\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"A\":\"C\"},\"evmVersion\":\"osaka\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"details\":{\"constantOptimizer\":false,\"cse\":false,\"deduplicate\":false,\"inliner\":false,\"jumpdestRemover\":true,\"orderLiterals\":false,\"peephole\":true,\"simpleCounterForLoopUncheckedIncrement\":true,\"yul\":false,\"yulDetails\":{\"optimizerSteps\":\":\"}},\"runs\":200},\"remappings\":[]},\"sources\":{\"A\":{\"keccak256\":\"0xb284c39999cb85b80be315a6e9e322adf67a783c66e91ba4439168694580a66d\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://098cee915fad095b8a996813768bd7d5e8c9e40c405e8c43d0572bb7bbc17334\",\"dweb:/ipfs/QmZmUzvSryrrD7pJ9S32iQnEWn4QBL4J1NdbQqL2Xc3yTr\"]}},\"version\":1}" } } }, diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_empty_sequence_whitespaces_metadata/output.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_empty_sequence_whitespaces_metadata/output.json index 07087b30fd..7c0a853e6e 100644 --- a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_empty_sequence_whitespaces_metadata/output.json +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_empty_sequence_whitespaces_metadata/output.json @@ -2,7 +2,7 @@ "contracts": { "A": { "C": { - "metadata": "{\"compiler\":{\"version\":\"\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"A\":\"C\"},\"evmVersion\":\"prague\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"details\":{\"constantOptimizer\":false,\"cse\":false,\"deduplicate\":false,\"inliner\":false,\"jumpdestRemover\":true,\"orderLiterals\":false,\"peephole\":true,\"simpleCounterForLoopUncheckedIncrement\":true,\"yul\":false,\"yulDetails\":{\"optimizerSteps\":\":\"}},\"runs\":200},\"remappings\":[]},\"sources\":{\"A\":{\"keccak256\":\"0xb284c39999cb85b80be315a6e9e322adf67a783c66e91ba4439168694580a66d\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://098cee915fad095b8a996813768bd7d5e8c9e40c405e8c43d0572bb7bbc17334\",\"dweb:/ipfs/QmZmUzvSryrrD7pJ9S32iQnEWn4QBL4J1NdbQqL2Xc3yTr\"]}},\"version\":1}" + "metadata": "{\"compiler\":{\"version\":\"\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"A\":\"C\"},\"evmVersion\":\"osaka\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"details\":{\"constantOptimizer\":false,\"cse\":false,\"deduplicate\":false,\"inliner\":false,\"jumpdestRemover\":true,\"orderLiterals\":false,\"peephole\":true,\"simpleCounterForLoopUncheckedIncrement\":true,\"yul\":false,\"yulDetails\":{\"optimizerSteps\":\":\"}},\"runs\":200},\"remappings\":[]},\"sources\":{\"A\":{\"keccak256\":\"0xb284c39999cb85b80be315a6e9e322adf67a783c66e91ba4439168694580a66d\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://098cee915fad095b8a996813768bd7d5e8c9e40c405e8c43d0572bb7bbc17334\",\"dweb:/ipfs/QmZmUzvSryrrD7pJ9S32iQnEWn4QBL4J1NdbQqL2Xc3yTr\"]}},\"version\":1}" } } }, diff --git a/test/cmdlineTests/standard_outputs_on_compilation_error/output.json b/test/cmdlineTests/standard_outputs_on_compilation_error/output.json index eae7a321f0..bce13b38e5 100644 --- a/test/cmdlineTests/standard_outputs_on_compilation_error/output.json +++ b/test/cmdlineTests/standard_outputs_on_compilation_error/output.json @@ -32,7 +32,7 @@ "x()": "0c55699c" } }, - "metadata": "{\"compiler\":{\"version\":\"\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"x\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"C\":\"C\"},\"evmVersion\":\"prague\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"C\":{\"keccak256\":\"0x67a13ebd685e4c6f792e71eb747dac57edb99e94d04d841ee6c979ae517934ce\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://665b000da768823654f680d02686c1e59d682a0b3882e43a77fed9f80ce64ae8\",\"dweb:/ipfs/QmVnKvuidH6KiCdNQpoAQUtDbB8hXkafVLXWMNitUcxnqC\"]}},\"version\":1}", + "metadata": "{\"compiler\":{\"version\":\"\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"x\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"C\":\"C\"},\"evmVersion\":\"osaka\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"C\":{\"keccak256\":\"0x67a13ebd685e4c6f792e71eb747dac57edb99e94d04d841ee6c979ae517934ce\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://665b000da768823654f680d02686c1e59d682a0b3882e43a77fed9f80ce64ae8\",\"dweb:/ipfs/QmVnKvuidH6KiCdNQpoAQUtDbB8hXkafVLXWMNitUcxnqC\"]}},\"version\":1}", "storageLayout": { "storage": [], "types": null diff --git a/test/cmdlineTests/standard_undeployable_contract_all_outputs/output.json b/test/cmdlineTests/standard_undeployable_contract_all_outputs/output.json index be87a5176d..9dec9899a7 100644 --- a/test/cmdlineTests/standard_undeployable_contract_all_outputs/output.json +++ b/test/cmdlineTests/standard_undeployable_contract_all_outputs/output.json @@ -35,7 +35,7 @@ "irAst": null, "irOptimized": "", "irOptimizedAst": null, - "metadata": "{\"compiler\":{\"version\":\"\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"C\":\"C\"},\"evmVersion\":\"prague\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"C\":{\"keccak256\":\"0xa8b7bfe5eff9112e6573d2860721faef28e8920ee251acb458303a05c1ec7df2\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://a2333e25b5034de4f98729e0a737309ba8e0db4371016f312e8f991f6f01613f\",\"dweb:/ipfs/QmVLwS2grVYV6qiDvNmeEYWzb6WDne9Ze47NXERSLPM4fJ\"]}},\"version\":1}", + "metadata": "{\"compiler\":{\"version\":\"\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"C\":\"C\"},\"evmVersion\":\"osaka\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"C\":{\"keccak256\":\"0xa8b7bfe5eff9112e6573d2860721faef28e8920ee251acb458303a05c1ec7df2\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://a2333e25b5034de4f98729e0a737309ba8e0db4371016f312e8f991f6f01613f\",\"dweb:/ipfs/QmVLwS2grVYV6qiDvNmeEYWzb6WDne9Ze47NXERSLPM4fJ\"]}},\"version\":1}", "storageLayout": { "storage": [], "types": null @@ -84,7 +84,7 @@ "irAst": null, "irOptimized": "", "irOptimizedAst": null, - "metadata": "{\"compiler\":{\"version\":\"\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"C\":\"I\"},\"evmVersion\":\"prague\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"C\":{\"keccak256\":\"0xa8b7bfe5eff9112e6573d2860721faef28e8920ee251acb458303a05c1ec7df2\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://a2333e25b5034de4f98729e0a737309ba8e0db4371016f312e8f991f6f01613f\",\"dweb:/ipfs/QmVLwS2grVYV6qiDvNmeEYWzb6WDne9Ze47NXERSLPM4fJ\"]}},\"version\":1}", + "metadata": "{\"compiler\":{\"version\":\"\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"C\":\"I\"},\"evmVersion\":\"osaka\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"C\":{\"keccak256\":\"0xa8b7bfe5eff9112e6573d2860721faef28e8920ee251acb458303a05c1ec7df2\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://a2333e25b5034de4f98729e0a737309ba8e0db4371016f312e8f991f6f01613f\",\"dweb:/ipfs/QmVLwS2grVYV6qiDvNmeEYWzb6WDne9Ze47NXERSLPM4fJ\"]}},\"version\":1}", "storageLayout": { "storage": [], "types": null diff --git a/test/cmdlineTests/yul_optimizer_disabled_sequence_empty/output b/test/cmdlineTests/yul_optimizer_disabled_sequence_empty/output index de60e7d1dc..a8d3556c84 100644 --- a/test/cmdlineTests/yul_optimizer_disabled_sequence_empty/output +++ b/test/cmdlineTests/yul_optimizer_disabled_sequence_empty/output @@ -1,4 +1,4 @@ ======= input.sol:C ======= Metadata: -{"compiler":{"version": ""},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"input.sol":"C"},"evmVersion":"prague","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"details":{"constantOptimizer":false,"cse":false,"deduplicate":false,"inliner":false,"jumpdestRemover":true,"orderLiterals":false,"peephole":true,"simpleCounterForLoopUncheckedIncrement":true,"yul":false,"yulDetails":{"optimizerSteps":":"}},"runs":200},"remappings":[]},"sources":{"input.sol":{"keccak256":"0xc2db3500808896ce1e69de2fe20cecab7ae2ffbb47cdf6ba8321296d95f49fc5","license":"GPL-3.0","urls":["bzz-raw://fde21393c068cd9f2d2b10ba4782db54f6f1c9a725074b17fa742531076be8a4","dweb:/ipfs/QmeTD6mR7YrWNyRowKRS7xs6cJNeMF3T49GAHzGM1bquyM"]}},"version":1} +{"compiler":{"version": ""},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"input.sol":"C"},"evmVersion":"osaka","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"details":{"constantOptimizer":false,"cse":false,"deduplicate":false,"inliner":false,"jumpdestRemover":true,"orderLiterals":false,"peephole":true,"simpleCounterForLoopUncheckedIncrement":true,"yul":false,"yulDetails":{"optimizerSteps":":"}},"runs":200},"remappings":[]},"sources":{"input.sol":{"keccak256":"0xc2db3500808896ce1e69de2fe20cecab7ae2ffbb47cdf6ba8321296d95f49fc5","license":"GPL-3.0","urls":["bzz-raw://fde21393c068cd9f2d2b10ba4782db54f6f1c9a725074b17fa742531076be8a4","dweb:/ipfs/QmeTD6mR7YrWNyRowKRS7xs6cJNeMF3T49GAHzGM1bquyM"]}},"version":1} diff --git a/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/output b/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/output index 2b32c23ee3..0c4c0acec2 100644 --- a/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/output +++ b/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/output @@ -130,4 +130,4 @@ object "C_28" { } Metadata: -{"compiler":{"version": ""},"language":"Solidity","output":{"abi":[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"foo","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"input.sol":"C"},"evmVersion":"prague","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"details":{"constantOptimizer":false,"cse":false,"deduplicate":false,"inliner":false,"jumpdestRemover":true,"orderLiterals":false,"peephole":true,"simpleCounterForLoopUncheckedIncrement":true,"yul":false,"yulDetails":{"optimizerSteps":":"}},"runs":200},"remappings":[]},"sources":{"input.sol":{"keccak256":"0x3fc910e345ce1ee62bfa6b0f66931ee632c08265b25b6139cfbbfe4d2f8d5dd8","license":"GPL-3.0","urls":["bzz-raw://e557e9ad2c2e420a669c06ae456b0b790d77d2d6d492cd8540e6b244388a5140","dweb:/ipfs/QmaNiZmC2Mo3YxGiehs1n3dVTjZwD7FguX7EUtpeshMVuR"]}},"version":1} +{"compiler":{"version": ""},"language":"Solidity","output":{"abi":[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"foo","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"input.sol":"C"},"evmVersion":"osaka","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"details":{"constantOptimizer":false,"cse":false,"deduplicate":false,"inliner":false,"jumpdestRemover":true,"orderLiterals":false,"peephole":true,"simpleCounterForLoopUncheckedIncrement":true,"yul":false,"yulDetails":{"optimizerSteps":":"}},"runs":200},"remappings":[]},"sources":{"input.sol":{"keccak256":"0x3fc910e345ce1ee62bfa6b0f66931ee632c08265b25b6139cfbbfe4d2f8d5dd8","license":"GPL-3.0","urls":["bzz-raw://e557e9ad2c2e420a669c06ae456b0b790d77d2d6d492cd8540e6b244388a5140","dweb:/ipfs/QmaNiZmC2Mo3YxGiehs1n3dVTjZwD7FguX7EUtpeshMVuR"]}},"version":1} diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 93a6320329..7be21863ab 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -38,6 +38,7 @@ #include using namespace solidity::evmasm; +using namespace solidity::langutil; using namespace std::string_literals; namespace solidity::frontend::test @@ -46,15 +47,15 @@ namespace solidity::frontend::test namespace { -langutil::Error::Severity str2Severity(std::string const& _cat) +Error::Severity str2Severity(std::string const& _cat) { - std::map cats{ - {"info", langutil::Error::Severity::Info}, - {"Info", langutil::Error::Severity::Info}, - {"warning", langutil::Error::Severity::Warning}, - {"Warning", langutil::Error::Severity::Warning}, - {"error", langutil::Error::Severity::Error}, - {"Error", langutil::Error::Severity::Error} + std::map cats{ + {"info", Error::Severity::Info}, + {"Info", Error::Severity::Info}, + {"warning", Error::Severity::Warning}, + {"Warning", Error::Severity::Warning}, + {"error", Error::Severity::Error}, + {"Error", Error::Severity::Error} }; return cats.at(_cat); } @@ -86,7 +87,7 @@ bool containsAtMostWarnings(Json const& _compilerResult) { BOOST_REQUIRE(error.is_object()); BOOST_REQUIRE(error["severity"].is_string()); - if (langutil::Error::isError(str2Severity(error["severity"].get()))) + if (Error::isError(str2Severity(error["severity"].get()))) return false; } @@ -1171,14 +1172,14 @@ BOOST_AUTO_TEST_CASE(evm_version) )"; }; Json result; - for (auto const& version: langutil::EVMVersion::allVersions()) + for (auto const& version: EVMVersion::allVersions()) { result = compile(inputForVersion(fmt::format("\"evmVersion\": \"{}\",", version.name()))); BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].get().find(fmt::format("\"evmVersion\":\"{}\"", version.name())) != std::string::npos); } // test default result = compile(inputForVersion("")); - BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].get().find("\"evmVersion\":\"prague\"") != std::string::npos); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].get().find(fmt::format("\"evmVersion\":\"{}\"", EVMVersion::current().name())) != std::string::npos); // test invalid result = compile(inputForVersion("\"evmVersion\": \"invalid\",")); BOOST_CHECK(result["errors"][0]["message"].get() == "Invalid EVM version requested."); From 25ec75c786feb4b73f287e0ff5304bc5677ea1ff Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Tue, 2 Dec 2025 12:06:03 +0100 Subject: [PATCH 078/243] Set blockGasLimit to 2^24 for external tests --- scripts/externalTests/common.sh | 12 ++++++++++++ test/externalTests/gp2.sh | 3 ++- test/externalTests/pool-together.sh | 3 ++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/scripts/externalTests/common.sh b/scripts/externalTests/common.sh index 88c656ca23..0465a4fec5 100644 --- a/scripts/externalTests/common.sh +++ b/scripts/externalTests/common.sh @@ -334,6 +334,12 @@ function force_hardhat_compiler_settings echo "module.exports.solidity = ${compiler_settings};" echo "module.exports.networks.hardhat = module.exports.networks.hardhat || { hardfork: '${evm_version}' }" echo "module.exports.networks.hardhat.hardfork = '${evm_version}'" + if [[ $evm_version == "osaka" ]]; then + # Transaction gas limit introduced by EIP-7825; the default value is 30_000_000 + # and should be automatically handled by EDR - however, EDR with the relevant change + # was not released in time. + echo "module.exports.networks.hardhat.blockGasLimit = 16_777_216" + fi else [[ $config_file == *\.ts ]] || assertFail [[ $config_var_name != "" ]] || assertFail @@ -342,6 +348,12 @@ function force_hardhat_compiler_settings echo "${config_var_name}.solidity = {compilers: [${compiler_settings}]};" echo "${config_var_name}.networks!.hardhat = ${config_var_name}.networks!.hardhat ?? { hardfork: '${evm_version}' };" echo "${config_var_name}.networks!.hardhat!.hardfork = '${evm_version}'" + if [[ $evm_version == "osaka" ]]; then + # Transaction gas limit introduced by EIP-7825; the default value is 30_000_000 + # and should be automatically handled by EDR - however, EDR with the relevant change + # was not released in time. + echo "${config_var_name}.networks!.hardhat!.blockGasLimit = 16_777_216" + fi fi >> "$config_file" } diff --git a/test/externalTests/gp2.sh b/test/externalTests/gp2.sh index fb33482375..cc8b56b665 100755 --- a/test/externalTests/gp2.sh +++ b/test/externalTests/gp2.sh @@ -67,7 +67,8 @@ function gp2_test force_hardhat_unlimited_contract_size "$config_file" "$config_var" yarn # Hardhat 3.0+ breaks the test suite - yarn add hardhat@2.26.3 + # v2.27.1 is the last v2 Hardhat (introduces Osaka support) + yarn add hardhat@2.27.1 # Ignore bench directory which fails to compile with current hardhat and ethers versions. # bench/trace/gas.ts:123:19 - error TS2339: Property 'equals' does not exist on type 'Uint8Array'. diff --git a/test/externalTests/pool-together.sh b/test/externalTests/pool-together.sh index 4f1c688141..e871b4e57c 100755 --- a/test/externalTests/pool-together.sh +++ b/test/externalTests/pool-together.sh @@ -67,7 +67,8 @@ function pool_together_test force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" "$config_var" yarn install # Hardhat 3.0+ breaks the test suite - yarn add hardhat@2.26.3 + # v2.27.1 is the last v2 Hardhat (introduces Osaka support) + yarn add hardhat@2.27.1 # These come with already compiled artifacts. We want them recompiled with latest compiler. rm -r node_modules/@pooltogether/yield-source-interface/artifacts/ From 5fc0f341aa681965d7dcea2ddda7c66c101af53b Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Tue, 2 Dec 2025 18:02:43 +0100 Subject: [PATCH 079/243] Disable RSA suite in OZ-contracts --- test/externalTests/zeppelin.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/externalTests/zeppelin.sh b/test/externalTests/zeppelin.sh index 9bc3e29132..99df125bba 100755 --- a/test/externalTests/zeppelin.sh +++ b/test/externalTests/zeppelin.sh @@ -105,6 +105,9 @@ function zeppelin_test # Fails with ProviderError: Invalid transaction: GasFloorMoreThanGasLimit sed -i "177s|+ 2_000n|+ 10_000n|" test/metatx/ERC2771Forwarder.test.js + # Fails under evmVersion=osaka, likely due to transaction gas limits introduced by EIP-7825 or by the modexp upper bound EIP-7823. + sed -i "s|describe(\('RSA'\)|describe.skip(\1|g" test/utils/cryptography/RSA.test.js + neutralize_package_json_hooks force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH" force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" From b1acbf78a6ac749d25e97b42f5bdc21a3a995827 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 10 Oct 2025 14:20:02 +0200 Subject: [PATCH 080/243] Discontinue support for Ubuntu PPAs --- .gitignore | 3 - Changelog.md | 1 + ReleaseChecklist.md | 18 --- docs/installing-solidity.rst | 20 +-- scripts/common.sh | 22 --- scripts/release_ppa.sh | 292 ----------------------------------- 6 files changed, 4 insertions(+), 352 deletions(-) delete mode 100755 scripts/release_ppa.sh diff --git a/.gitignore b/.gitignore index a9430fd982..8d7ee768a7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,6 @@ /commit_hash.txt /prerelease.txt -# Auth config for ppa release -/.release_ppa_auth - # Compiled Object files *.slo *.lo diff --git a/Changelog.md b/Changelog.md index 72551a4f8b..b34e4d4cb7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -13,6 +13,7 @@ Bugfixes: Build System: * Enable Linux arm64 binaries for testing and releases. +* Ubuntu PPA Packages: Discontinue the PPA as a binary distribution channel. * Update minimum version requirements of Boost to 1.83.0 for non-windows builds and of GCC and Clang to 13.3 and 18.1.3, respectively. Fixes infinite recursion on `boost::rational` comparison affecting compiler binaries built with GCC<14.0 and Boost<1.75. ### 0.8.30 (2025-05-07) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index d8d63b09ae..4eada5f65e 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -4,10 +4,7 @@ - [ ] GitHub account with access to [solidity](https://github.com/argotorg/solidity), [solc-js](https://github.com/argotorg/solc-js), [solc-bin](https://github.com/argotorg/solc-bin), [solidity-website](https://github.com/argotorg/solidity-website). - [ ] DockerHub account with push rights to the [`solc` image](https://hub.docker.com/r/ethereum/solc). -- [ ] Launchpad (Ubuntu One) account with a membership in the ["Ethereum" team](https://launchpad.net/~ethereum) and - a gnupg key for your email in the `ethereum.org` domain (has to be version 1, gpg2 won't work). - [ ] Ubuntu/Debian dependencies of the Docker script: `docker-buildx`. -- [ ] Ubuntu/Debian dependencies of the PPA scripts: `devscripts`, `debhelper`, `dput`, `git`, `wget`, `ca-certificates`. - [ ] [npm Registry](https://www.npmjs.com) account added as a collaborator for the [`solc` package](https://www.npmjs.com/package/solc). - [ ] Access to the [solidity_lang Twitter account](https://twitter.com/solidity_lang). - [ ] [Reddit](https://www.reddit.com) account that is at least 10 days old with a minimum of 20 comment karma (`/r/ethereum` requirements). @@ -24,8 +21,6 @@ At least a day before the release: - [ ] Rerun CI on the top commits of main branches in all repositories that do not have daily activity by creating a test branch or PR: - [ ] `solc-js` - [ ] `solc-bin` (make sure the bytecode comparison check did run) -- [ ] (Optional) Create a prerelease in our Ubuntu PPA by following the steps in the PPA section below on `develop` rather than on a tag. - This is recommended especially when dealing with PPA for the first time, when we add a new Ubuntu version or when the PPA scripts were modified in this release cycle. - [ ] Verify that the release tarball of `solc-js` works. Bump version locally, add `soljson.js` from CI, build it, compare the file structure with the previous version, install it locally and try to use it. - [ ] Review [Learning from Past Releases](https://notes.argot.org/@solidity-release-mistakes) to make sure you don't repeat the same mistakes. @@ -82,19 +77,6 @@ At least a day before the release: - [ ] Make sure `docker-buildx` is installed. - [ ] Run `./scripts/docker_deploy_manual.sh v$VERSION`. -### PPA -- [ ] Create `.release_ppa_auth` at the root of your local Solidity checkout and set `LAUNCHPAD_EMAIL` and `LAUNCHPAD_KEYID` to your key's email and key id. -- [ ] Double-check that the `DISTRIBUTIONS` list in `scripts/release_ppa.sh` contains the most recent versions of Ubuntu. -- [ ] Run `scripts/release_ppa.sh v$VERSION` to create the PPA release. - This will create a single package containing static binary for older Ubuntu versions in the [`~ethereum/ethereum-static` PPA](https://launchpad.net/~ethereum/+archive/ubuntu/ethereum-static) - and separate packages with dynamically-linked binaries for recent versions (those listed in `DISTRIBUTIONS`) in the [`~ethereum/ethereum` PPA](https://launchpad.net/~ethereum/+archive/ubuntu/ethereum). -- [ ] Wait for the build to be finished and published for *all architectures* (currently we only build for `amd64`, but we may add `arm` in the future). - **SERIOUSLY: DO NOT PROCEED EARLIER!!!** -- [ ] *After* the package with the static build is *published*, use it to create packages for older Ubuntu versions. - Copy the static package to the [`~ethereum/ethereum` PPA](https://launchpad.net/~ethereum/+archive/ubuntu/ethereum) - for the destination series `Trusty`, `Xenial`, `Bionic`, and `Focal` - while selecting `Copy existing binaries`. - ### Release solc-js - [ ] Wait until solc-bin was properly deployed. You can test this via remix - a test run through remix is advisable anyway. - [ ] Increment the version number, create a pull request for that, merge it after tests succeeded. diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index bda0d102bf..01a2f38204 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -116,23 +116,9 @@ Linux Packages Binary packages of Solidity are available at `solidity/releases `_. -We also have PPAs for Ubuntu, you can get the latest stable -version using the following commands: - -.. code-block:: bash - - sudo add-apt-repository ppa:ethereum/ethereum - sudo apt-get update - sudo apt-get install solc - -The nightly version can be installed using these commands: - -.. code-block:: bash - - sudo add-apt-repository ppa:ethereum/ethereum - sudo add-apt-repository ppa:ethereum/ethereum-dev - sudo apt-get update - sudo apt-get install solc +Ubuntu packages for versions up to 0.8.30 are available in the +`ethereum/ethereum PPA `_. +However, we have discontinued this distribution method and future versions will not be added there. Furthermore, some Linux distributions provide their own packages. These packages are not directly maintained by us but usually kept up-to-date by the respective package maintainers. diff --git a/scripts/common.sh b/scripts/common.sh index 0ca602ee6a..a25fe5a824 100644 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -40,28 +40,6 @@ else function printLog { echo -e "$(tput setaf 3)$1$(tput sgr0)"; } fi -function checkDputEntries -{ - local pattern="$1" - grep "${pattern}" /etc/dput.cf --quiet || \ - fail "Error: Missing ${pattern//\\/} section in /etc/dput.cf (check top comment in release_ppa.sh for more information)." -} - -function sourcePPAConfig -{ - [[ "$LAUNCHPAD_KEYID" == "" && "$LAUNCHPAD_EMAIL" == "" ]] || fail - - # source keyid and email from .release_ppa_auth - if [[ -e .release_ppa_auth ]] - then - # shellcheck source=/dev/null - source "${REPO_ROOT}/.release_ppa_auth" - fi - - [[ "$LAUNCHPAD_KEYID" != "" && "$LAUNCHPAD_EMAIL" != "" ]] || \ - fail "Error: Couldn't find variables \$LAUNCHPAD_KEYID or \$LAUNCHPAD_EMAIL in sourced file .release_ppa_auth (check top comment in $0 for more information)." -} - function printStackTrace { printWarning "" diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh deleted file mode 100755 index 6c1fd51dbf..0000000000 --- a/scripts/release_ppa.sh +++ /dev/null @@ -1,292 +0,0 @@ -#!/usr/bin/env bash -############################################################################## -## This is used to package .deb packages and upload them to the launchpad -## ppa servers for building. -## -## You can pass a branch name as argument to this script (which, if no argument is given, -## will default to "develop"). -## -## If the given branch matches a release version tag, the resulting package will be uploaded to -## ethereum/ethereum PPA, or ethereum/ethereum-dev PPA otherwise. -## -## It will clone the Solidity git from github, determine the version, -## create a source archive and push it to the ubuntu ppa servers. -## -## To interact with launchpad, you need to set the variables $LAUNCHPAD_EMAIL -## and $LAUNCHPAD_KEYID in the file .release_ppa_auth in the root directory of -## the project to your launchpad email and pgp keyid. -## This could for example look like this: -## -## LAUNCHPAD_EMAIL=your-launchpad-email@ethereum.org -## LAUNCHPAD_KEYID=123ABCFFFFFFFF -## -## Additionally the following entries in /etc/dput.cf are required: -## -## [ethereum-dev] -## fqdn = ppa.launchpad.net -## method = ftp -## incoming = ~ethereum/ethereum-dev -## login = anonymous -## -## [ethereum] -## fqdn = ppa.launchpad.net -## method = ftp -## incoming = ~ethereum/ethereum -## login = anonymous -## -## [ethereum-static] -## fqdn = ppa.launchpad.net -## method = ftp -## incoming = ~ethereum/ethereum-static -## login = anonymous -## -############################################################################## - -set -e - - -REPO_ROOT="$(dirname "$0")/.." - -# shellcheck source=scripts/common.sh -source "${REPO_ROOT}/scripts/common.sh" - -if [ -z "$1" ] -then - branch=develop -else - branch=$1 -fi - -is_release() { - [[ "${branch}" =~ ^v[0-9]+(\.[0-9]+)*$ ]] -} - -sourcePPAConfig - -packagename=solc - -# This needs to be a still active release -static_build_distribution=noble - -DISTRIBUTIONS="jammy noble oracular plucky" - -if is_release -then - DISTRIBUTIONS="$DISTRIBUTIONS STATIC" - - # Sanity checks - checkDputEntries "\[ethereum\]" - checkDputEntries "\[ethereum-static\]" -else - # Sanity check - checkDputEntries "\[ethereum-dev\]" -fi - -for distribution in $DISTRIBUTIONS -do -cd /tmp/ -rm -rf "$distribution" -mkdir "$distribution" -cd "$distribution" - -if [ "$distribution" = STATIC ] -then - pparepo=ethereum-static - CMAKE_OPTIONS="-DSOLC_LINK_STATIC=On -DCMAKE_EXE_LINKER_FLAGS=-static" -else - if is_release - then - pparepo=ethereum - else - pparepo=ethereum-dev - fi - CMAKE_OPTIONS="" -fi -ppafilesurl=https://launchpad.net/~ethereum/+archive/ubuntu/${pparepo}/+files - -# Fetch source -git clone --depth 2 --recursive https://github.com/argotorg/solidity.git -b "$branch" -mv solidity solc - -# Determine version -cd solc -version=$("$(dirname "$0")/get_version.sh") -commithash=$(git rev-parse --short=8 HEAD) -commitdate=$(git show --format=%ci HEAD | head -n 1 | cut - -b1-10 | sed -e 's/-0?/./' | sed -e 's/-0?/./') - -echo "$commithash" > commit_hash.txt -if is_release -then - debversion="$version" - echo -n > prerelease.txt # proper release -else - debversion="$version~develop-$commitdate-$commithash" -fi - -# gzip will create different tars all the time and we are not allowed -# to upload the same file twice with different contents, so we only -# create it once. -if [ ! -e "/tmp/${packagename}_${debversion}.orig.tar.gz" ] -then - tar --exclude .git -czf "/tmp/${packagename}_${debversion}.orig.tar.gz" . -fi -cp "/tmp/${packagename}_${debversion}.orig.tar.gz" ../ - -# Create debian package information - -mkdir debian -echo 9 > debian/compat -cat < debian/control -Source: solc -Section: science -Priority: extra -Maintainer: Christian (Buildserver key) -Build-Depends: debhelper (>= 9.0.0), - cmake, - g++ (>= 5.0), - git, - libgmp-dev, - libboost-all-dev, - automake, - libtool, - scons -Standards-Version: 3.9.5 -Homepage: https://ethereum.org -Vcs-Git: https://github.com/argotorg/solidity.git -Vcs-Browser: https://github.com/argotorg/solidity - -Package: solc -Architecture: any-amd64 -Multi-Arch: same -Depends: \${shlibs:Depends}, \${misc:Depends} -Conflicts: libethereum (<= 1.2.9) -Description: Solidity compiler. - The commandline interface to the Solidity smart contract compiler. -EOF -cat < debian/rules -#!/usr/bin/make -f -# -*- makefile -*- -# Sample debian/rules that uses debhelper. -# -# This file was originally written by Joey Hess and Craig Small. -# As a special exception, when this file is copied by dh-make into a -# dh-make output file, you may use that output file without restriction. -# This special exception was added by Craig Small in version 0.37 of dh-make. -# -# Modified to make a template file for a multi-binary package with separated -# build-arch and build-indep targets by Bill Allombert 2001 - -# Uncomment this to turn on verbose mode. -export DH_VERBOSE=1 - -# This has to be exported to make some magic below work. -export DH_OPTIONS - - -%: - dh \$@ --buildsystem=cmake #--with sphinxdoc - -override_dh_auto_test: - -#override_dh_installdocs: -# make -C docs html -# dh_installdocs docs/_build/html - -override_dh_shlibdeps: - dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info - -override_dh_auto_configure: - dh_auto_configure -- -DTESTS=OFF -DFETCHCONTENT_FULLY_DISCONNECTED=OFF ${CMAKE_OPTIONS} -EOF -cat < debian/copyright -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: solc -Source: https://github.com/argotorg/solidity - -Files: * -Copyright: 2014-2016 Ethereum -License: GPL-3.0+ - -Files: debian/* -Copyright: 2016 Ethereum -License: GPL-3.0+ - -License: GPL-3.0+ - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - . - This package is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - . - You should have received a copy of the GNU General Public License - along with this program. If not, see . - . - On Debian systems, the complete text of the GNU General - Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". -EOF -cat < debian/changelog -solc (0.0.1-0ubuntu1) saucy; urgency=low - - * Initial release. - - -- Christian Mon, 03 Feb 2016 14:50:20 +0000 -EOF -echo docs > debian/docs -mkdir debian/source -echo "3.0 (quilt)" > debian/source/format -chmod +x debian/rules - -versionsuffix=0ubuntu1~${distribution} -# bump version / add entry to changelog -EMAIL="$LAUNCHPAD_EMAIL" dch -v "1:${debversion}-${versionsuffix}" "git build of ${commithash}" - - -# build source package -# If packages is rejected because original source is already present, add -# -sd to remove it from the .changes file -# -d disables the build dependencies check -debuild -S -d -sa -us -uc - -# prepare .changes file for Launchpad -if [ "$distribution" = STATIC ] -then - sed -i -e "s/UNRELEASED/${static_build_distribution}/" -e s/urgency=medium/urgency=low/ ../*.changes -else - sed -i -e "s/UNRELEASED/${distribution}/" -e s/urgency=medium/urgency=low/ ../*.changes -fi - -# check if ubuntu already has the source tarball -( -cd .. -orig="${packagename}_${debversion}.orig.tar.gz" -# shellcheck disable=SC2012 -orig_size=$(ls -l "$orig" | cut -d ' ' -f 5) -orig_sha1=$(sha1sum "$orig" | cut -d ' ' -f 1) -orig_sha256=$(sha256sum "$orig" | cut -d ' ' -f 1) -orig_md5=$(md5sum "$orig" | cut -d ' ' -f 1) - -if wget --quiet -O "$orig-tmp" "$ppafilesurl/$orig" -then - echo "[WARN] Original tarball found in Ubuntu archive, using it instead" - mv "$orig-tmp" "$orig" - # shellcheck disable=SC2012 - new_size=$(ls -l ./*.orig.tar.gz | cut -d ' ' -f 5) - new_sha1=$(sha1sum "$orig" | cut -d ' ' -f 1) - new_sha256=$(sha256sum "$orig" | cut -d ' ' -f 1) - new_md5=$(md5sum "$orig" | cut -d ' ' -f 1) - sed -i -e "s,$orig_sha1,$new_sha1,g" -e "s,$orig_sha256,$new_sha256,g" -e "s,$orig_size,$new_size,g" -e "s,$orig_md5,$new_md5,g" ./*.dsc - sed -i -e "s,$orig_sha1,$new_sha1,g" -e "s,$orig_sha256,$new_sha256,g" -e "s,$orig_size,$new_size,g" -e "s,$orig_md5,$new_md5,g" ./*.changes -fi -) - -# sign the package -debsign --re-sign -k "${LAUNCHPAD_KEYID}" "../${packagename}_${debversion}-${versionsuffix}_source.changes" - -# upload -dput "${pparepo}" "../${packagename}_${debversion}-${versionsuffix}_source.changes" - -done From 9cda966876b699156521bc83ac48d88d0aee28f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 2 Dec 2025 19:37:52 +0100 Subject: [PATCH 081/243] Docs: Remove outdated paragraph about installing a solver being required for SMTChecker pragmas --- docs/layout-of-source-files.rst | 9 --------- 1 file changed, 9 deletions(-) diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index e51b3ad98e..58b38400fe 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -153,15 +153,6 @@ since Solidity 0.7.4. SMTChecker ~~~~~~~~~~ -This component has to be enabled when the Solidity compiler is built -and therefore it is not available in all Solidity binaries. -The :ref:`build instructions` explain how to activate this option. -It is activated for the Ubuntu PPA releases in most versions, -but not for the Docker images, Windows binaries or the -statically-built Linux binaries. It can be activated for solc-js via the -`smtCallback `_ if you have an SMT solver -installed locally and run solc-js via node (not via the browser). - If you use ``pragma experimental SMTChecker;``, then you get additional :ref:`safety warnings` which are obtained by querying an SMT solver. From e4e515c07530127487a57594f7eb9a6e7911465d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 2 Dec 2025 19:38:35 +0100 Subject: [PATCH 082/243] Docs: Deprecation note for SMTChecker pragma --- docs/layout-of-source-files.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index 58b38400fe..e2c7a859a8 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -160,6 +160,11 @@ The component does not yet support all features of the Solidity language and likely outputs many warnings. In case it reports unsupported features, the analysis may not be fully sound. +.. note:: + + The ``SMTChecker`` pragma is deprecated and will be removed. + To enable SMTChecker, simply select :ref:`select an engine` when invoking the compiler. + .. index:: source file, ! import, module, source unit .. _import: From f4173b2a7e4c975f0cda7ee665cab0b9404d985e Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Mon, 18 Aug 2025 20:35:10 -0300 Subject: [PATCH 083/243] Emit deprecation warnings for abi coder v1, contract comparisons, memory-safe-assembly, send and transfer functions, and virtual modifiers. --- Changelog.md | 5 ++++ libsolidity/analysis/DocStringTagParser.cpp | 11 +++++++-- libsolidity/analysis/SyntaxChecker.cpp | 18 +++++++++++++++ libsolidity/analysis/TypeChecker.cpp | 23 +++++++++++++++++++ .../model_checker_targets_all_all_engines/err | 6 +++++ .../model_checker_targets_all_bmc/err | 6 +++++ .../model_checker_targets_all_chc/err | 6 +++++ .../model_checker_targets_assert_bmc/err | 6 +++++ .../model_checker_targets_assert_chc/err | 6 +++++ .../model_checker_targets_balance_bmc/err | 6 +++++ .../model_checker_targets_balance_chc/err | 6 +++++ .../err | 6 +++++ .../err | 5 ++++ .../err | 6 +++++ .../model_checker_targets_default_bmc/err | 6 +++++ .../model_checker_targets_default_chc/err | 6 +++++ .../model_checker_targets_div_by_zero_bmc/err | 6 +++++ .../model_checker_targets_div_by_zero_chc/err | 6 +++++ .../err | 5 ++++ .../err | 6 +++++ .../model_checker_targets_overflow_bmc/err | 6 +++++ .../model_checker_targets_overflow_chc/err | 6 +++++ .../model_checker_targets_pop_empty_bmc/err | 5 ++++ .../model_checker_targets_pop_empty_chc/err | 6 +++++ .../model_checker_targets_underflow_bmc/err | 6 +++++ .../model_checker_targets_underflow_chc/err | 6 +++++ .../err | 6 +++++ .../err | 6 +++++ .../err | 6 +++++ .../err | 6 +++++ .../output.json | 19 +++++++++++++++ .../output.json | 19 +++++++++++++++ .../output.json | 19 +++++++++++++++ .../output.json | 19 +++++++++++++++ .../output.json | 19 +++++++++++++++ .../output.json | 21 +++++++++++++++++ .../output.json | 19 +++++++++++++++ .../output.json | 19 +++++++++++++++ .../output.json | 19 +++++++++++++++ .../output.json | 19 +++++++++++++++ .../output.json | 19 +++++++++++++++ .../output.json | 21 +++++++++++++++++ .../output.json | 19 +++++++++++++++ .../output.json | 19 +++++++++++++++ .../output.json | 19 +++++++++++++++ .../output.json | 21 +++++++++++++++++ .../output.json | 19 +++++++++++++++ .../output.json | 19 +++++++++++++++ .../output.json | 19 +++++++++++++++ .../output.json | 19 +++++++++++++++ .../output.json | 19 +++++++++++++++ .../output.json | 19 +++++++++++++++ .../output.json | 19 +++++++++++++++ test/cmdlineTests/viair_abicoder_v1/err | 6 +++++ .../blockchain_state/balance_spend.sol | 1 + .../blockchain_state/balance_spend_2.sol | 1 + .../blockchain_state/decreasing_balance.sol | 1 + .../blockchain_state/free_function_2.sol | 3 ++- .../blockchain_state/library_internal_2.sol | 3 ++- .../blockchain_state/library_public_2.sol | 7 +++--- .../blockchain_state/transfer_1.sol | 3 ++- .../blockchain_state/transfer_2.sol | 2 ++ .../blockchain_state/transfer_3.sol | 3 ++- .../blockchain_state/transfer_4.sol | 1 + .../smtCheckerTests/bmc_coverage/funds.sol | 1 + .../deployment/deploy_trusted_addresses.sol | 1 + .../deployment/deploy_untrusted_addresses.sol | 5 ++-- .../functions/getters/array_of_structs_1.sol | 1 + .../functions/getters/array_of_structs_2.sol | 1 + .../functions/getters/array_of_structs_3.sol | 3 ++- .../functions/getters/contract.sol | 3 ++- .../functions/getters/struct_4.sol | 3 ++- .../modifiers/modifier_abstract.sol | 1 + .../modifiers/modifier_overriding_1.sol | 3 ++- .../modifiers/modifier_overriding_2.sol | 2 ++ .../modifiers/modifier_overriding_3.sol | 4 +++- .../modifiers/modifier_overriding_4.sol | 4 ++++ .../modifier_virtual_static_call_1.sol | 1 + .../modifier_virtual_static_call_2.sol | 3 ++- .../types/address_transfer.sol | 5 ++-- .../types/address_transfer_2.sol | 8 ++++--- .../types/address_transfer_3.sol | 1 + .../types/address_transfer_insufficient.sol | 8 ++++--- .../smtCheckerTests/types/contract.sol | 3 ++- .../smtCheckerTests/types/contract_2.sol | 3 ++- .../smtCheckerTests/types/contract_3.sol | 3 +++ .../types/contract_address_conversion_2.sol | 1 + .../abiEncoder/conflicting_settings.sol | 1 + .../conflicting_settings_reverse.sol | 1 + ...flicting_settings_reverse_experimental.sol | 1 + .../syntaxTests/abiEncoder/select_v1.sol | 1 + .../abiEncoder/select_v1_quoted_string.sol | 1 + .../syntaxTests/abiEncoder/selected_twice.sol | 2 ++ ...sing_public_state_variable_via_v1_type.sol | 1 + ...sing_public_state_variable_via_v2_type.sol | 1 + ...rary_function_accepting_storage_struct.sol | 1 + ...all_to_v2_constructor_accepting_struct.sol | 1 + ...on_accepting_struct_via_named_argument.sol | 1 + ...ract_function_pointer_accepting_struct.sol | 1 + ...unction_returning_dynamic_string_array.sol | 1 + ..._v2_contract_function_returning_struct.sol | 1 + ...on_returning_struct_with_dynamic_array.sol | 1 + .../v1_call_to_v2_event_accepting_struct.sol | 1 + ...ary_attached_function_returning_struct.sol | 1 + ...rary_function_accepting_storage_struct.sol | 1 + ...o_v2_library_function_returning_struct.sol | 1 + .../abiEncoder/v1_call_to_v2_modifier.sol | 1 + .../v1_constructor_with_v2_modifier.sol | 2 ++ ...ance_from_contract_calling_v2_function.sol | 1 + ...itance_from_contract_defining_v2_event.sol | 1 + ..._defining_v2_function_accepting_struct.sol | 1 + ..._defining_v2_function_returning_struct.sol | 1 + ...itance_from_contract_emitting_v2_event.sol | 1 + .../v1_modifier_overriding_v2_modifier.sol | 2 ++ .../abiEncoder/v1_v2_v1_modifier_mix.sol | 3 +++ .../abiEncoder/v2_v1_v1_modifier_sandwich.sol | 2 ++ .../abiEncoder/v2_v1_v2_modifier_sandwich.sol | 1 + .../array/calldata_multi_dynamic_V1.sol | 1 + .../syntaxTests/constructor/nonabiv2_type.sol | 1 + .../constructor/nonabiv2_type_abstract.sol | 1 + .../implemented_without_placeholder.sol | 1 + .../modifier_different_functions.sol | 1 + .../modifiers/modifier_override.sol | 1 + .../modifiers/non_implemented_modifier.sol | 2 ++ ...icit_conversion_of_super_in_comparison.sol | 3 +++ .../errors/no_structs_in_abiv1.sol | 1 + .../syntaxTests/events/event_nested_array.sol | 1 + .../events/event_nested_array_in_struct.sol | 1 + .../syntaxTests/events/event_struct.sol | 1 + .../events/event_struct_indexed.sol | 1 + .../syntaxTests/getter/nested_structs.sol | 1 + .../inheritance_virtual_modifiers.sol | 2 ++ .../inheritance_abi_encoder_mismatch_1.sol | 1 + .../inheritance_abi_encoder_mismatch_2.sol | 2 ++ ..._data_location_change_illegal_internal.sol | 1 + .../override/modifier_ambiguous.sol | 2 ++ .../override/modifier_ambiguous_fail.sol | 2 ++ ...modifier_inherited_different_signature.sol | 2 ++ ...inherited_different_signature_override.sol | 3 +++ ...ase_and_unique_implementation_modifier.sol | 3 +++ .../virtual/modifier_virtual_err.sol | 1 + .../inheritance/virtual/simple.sol | 1 + ...memory_safe_dialect_string_and_comment.sol | 3 ++- .../inlineAssembly/natspec_multi.sol | 2 ++ .../natspec_multi_swallowed.sol | 1 + .../modifiers/definition_in_contract.sol | 3 +++ .../definition_in_contract_unimplemented.sol | 1 + .../modifiers/definition_in_interface.sol | 2 ++ .../modifiers/definition_in_library.sol | 1 + .../definition_in_library_unimplemented.sol | 1 + .../modifiers/empty_modifier_body.sol | 2 ++ .../modifiers/empty_modifier_err.sol | 2 ++ .../modifiers/legal_modifier_override.sol | 1 + .../modifiers/modifier_abstract_override.sol | 2 ++ ...ple_inheritance_unimplemented_override.sol | 2 ++ .../unimplemented_function_and_modifier.sol | 1 + .../unimplemented_override_unimplemented.sol | 3 +++ .../modifiers/use_unimplemented_from_base.sol | 2 ++ .../use_unimplemented_on_overridden_func.sol | 1 + .../modifiers/use_unimplemented_static.sol | 2 ++ ...045_returning_multi_dimensional_arrays.sol | 1 + ...urning_multi_dimensional_static_arrays.sol | 1 + ...048_returning_arrays_in_structs_arrays.sol | 1 + .../346_unused_return_value_send.sol | 1 + .../405_address_checksum_type_deduction.sol | 1 + .../413_address_methods.sol | 2 ++ ...es_not_error_transfer_payable_fallback.sol | 1 + .../535_address_overload_resolution.sol | 1 + .../585_abi_decode_with_unsupported_types.sol | 1 + .../bytecode_too_large_abiencoder_v1.sol | 1 + .../abi_encodePacked_nested_dynamic_array.sol | 1 + .../abi_encode_nested_dynamic_array.sol | 1 + .../specialFunctions/abi_encode_structs.sol | 1 + .../abi_decode_nested_dynamic_array.sol | 1 + .../abidecode/abi_decode_struct.sol | 1 + .../viewPureChecker/builtin_functions.sol | 2 ++ .../builtin_functions_view_fail.sol | 2 ++ .../viewPureChecker/eof/builtin_functions.sol | 2 ++ 178 files changed, 851 insertions(+), 28 deletions(-) create mode 100644 test/cmdlineTests/model_checker_targets_constant_condition_chc/err create mode 100644 test/cmdlineTests/model_checker_targets_out_of_bounds_bmc/err create mode 100644 test/cmdlineTests/model_checker_targets_pop_empty_bmc/err diff --git a/Changelog.md b/Changelog.md index b34e4d4cb7..ba3e76659b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,11 @@ Language Features: Compiler Features: * ethdebug: Experimental support for instructions and source locations under EOF. * EVM: Set default EVM Version to `osaka`. +* DocString Parser: Warn about deprecation of inline assembly special comment `memory-safe-assembly`. +* Syntax Checker: Warn about deprecation of ABI coder v1. +* Syntax Checker: Warn about deprecation of virtual modifiers. +* Type Checker: Warn about deprecation of `send` and `transfer` functions on instances of `address`. +* Type Checker: Warn about deprecation of comparisons between variables of contract types. Bugfixes: * Assembler: Fix not using a fixed-width type for IDs being assigned to subassemblies nested more than one level away, resulting in inconsistent `--asm-json` output between target architectures. diff --git a/libsolidity/analysis/DocStringTagParser.cpp b/libsolidity/analysis/DocStringTagParser.cpp index 5d95f42e90..b7fef128db 100644 --- a/libsolidity/analysis/DocStringTagParser.cpp +++ b/libsolidity/analysis/DocStringTagParser.cpp @@ -206,11 +206,18 @@ bool DocStringTagParser::visit(InlineAssembly const& _assembly) m_errorReporter.warning( 8544_error, _assembly.location(), - "Inline assembly marked as memory safe using both a NatSpec tag and an assembly flag. " - "If you are not concerned with backwards compatibility, only use the assembly flag, " + "Inline assembly marked as memory safe using both a NatSpec tag and an assembly block annotation. " + "If you are not concerned with backwards compatibility, only use the assembly block annotation, " "otherwise only use the NatSpec tag." ); _assembly.annotation().markedMemorySafe = true; + m_errorReporter.warning( + 2424_error, + _assembly.location(), + "Natspec memory-safe-assembly special comment for inline assembly is deprecated and " + "scheduled for removal. " + "Use the memory-safe block annotation instead." + ); } else m_errorReporter.warning( diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 1e3f5f1c2a..9bf784b365 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -149,6 +149,16 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) ); else m_sourceUnit->annotation().useABICoderV2 = (_pragma.literals()[1] == "v2"); + + if ( + _pragma.literals().size() > 1 && + _pragma.literals()[1] == "v1" + ) + m_errorReporter.warning( + 9511_error, + _pragma.location(), + "ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead." + ); } else if (_pragma.literals()[0] == "solidity") { @@ -184,6 +194,14 @@ void SyntaxChecker::endVisit(ModifierDefinition const& _modifier) { if (_modifier.isImplemented() && !m_placeholderFound) m_errorReporter.syntaxError(2883_error, _modifier.body().location(), "Modifier body does not contain '_'."); + + if (_modifier.markedVirtual()) + m_errorReporter.warning( + 8429_error, + _modifier.location(), + "Virtual modifiers are deprecated and scheduled for removal." + ); + m_placeholderFound = false; } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index e56ee157d9..9e29983bad 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1829,6 +1829,16 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) ) ); } + if ( + TokenTraits::isCompareOp(_operation.getOperator()) && + commonType->category() == Type::Category::Contract + ) + m_errorReporter.warning( + 9170_error, + _operation.location(), + "Comparison of variables of contract type is deprecated and scheduled for removal. " + "Use an explicit cast to address type and compare the addresses instead." + ); } Type const* TypeChecker::typeCheckTypeConversionAndRetrieveReturnType( @@ -3212,6 +3222,19 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) if (contractType && contractType->isSuper()) requiredLookup = VirtualLookup::Super; } + + if ( + funType->kind() == FunctionType::Kind::Send || + funType->kind() == FunctionType::Kind::Transfer + ) + m_errorReporter.warning( + 9207_error, + _memberAccess.location(), + fmt::format( + "'{}' is deprecated and scheduled for removal. Use 'call{{value: }}(\"\")' instead.", + funType->kind() == FunctionType::Kind::Send ? "send" : "transfer" + ) + ); } annotation.requiredLookup = requiredLookup; diff --git a/test/cmdlineTests/model_checker_targets_all_all_engines/err b/test/cmdlineTests/model_checker_targets_all_all_engines/err index e095ba263c..3e3871bf1f 100644 --- a/test/cmdlineTests/model_checker_targets_all_all_engines/err +++ b/test/cmdlineTests/model_checker_targets_all_all_engines/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: CHC: Underflow (resulting value less than 0) happens here. Counterexample: arr = [] diff --git a/test/cmdlineTests/model_checker_targets_all_bmc/err b/test/cmdlineTests/model_checker_targets_all_bmc/err index 3887cc6b2a..025e85f323 100644 --- a/test/cmdlineTests/model_checker_targets_all_bmc/err +++ b/test/cmdlineTests/model_checker_targets_all_bmc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: BMC: Condition is always true. --> input.sol:6:11: | diff --git a/test/cmdlineTests/model_checker_targets_all_chc/err b/test/cmdlineTests/model_checker_targets_all_chc/err index c3853983af..0c04c2f2c9 100644 --- a/test/cmdlineTests/model_checker_targets_all_chc/err +++ b/test/cmdlineTests/model_checker_targets_all_chc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: CHC: Underflow (resulting value less than 0) happens here. Counterexample: arr = [] diff --git a/test/cmdlineTests/model_checker_targets_assert_bmc/err b/test/cmdlineTests/model_checker_targets_assert_bmc/err index 27395c9dc5..255066b7c0 100644 --- a/test/cmdlineTests/model_checker_targets_assert_bmc/err +++ b/test/cmdlineTests/model_checker_targets_assert_bmc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: BMC: Assertion violation happens here. --> input.sol:11:3: | diff --git a/test/cmdlineTests/model_checker_targets_assert_chc/err b/test/cmdlineTests/model_checker_targets_assert_chc/err index ada7c3d3dc..096773e68e 100644 --- a/test/cmdlineTests/model_checker_targets_assert_chc/err +++ b/test/cmdlineTests/model_checker_targets_assert_chc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: CHC: Assertion violation happens here. Counterexample: arr = [] diff --git a/test/cmdlineTests/model_checker_targets_balance_bmc/err b/test/cmdlineTests/model_checker_targets_balance_bmc/err index 178bd66620..5576730bf5 100644 --- a/test/cmdlineTests/model_checker_targets_balance_bmc/err +++ b/test/cmdlineTests/model_checker_targets_balance_bmc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: BMC: Insufficient funds happens here. --> input.sol:10:3: | diff --git a/test/cmdlineTests/model_checker_targets_balance_chc/err b/test/cmdlineTests/model_checker_targets_balance_chc/err index 57bfe42678..c6d3091d16 100644 --- a/test/cmdlineTests/model_checker_targets_balance_chc/err +++ b/test/cmdlineTests/model_checker_targets_balance_chc/err @@ -1 +1,7 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:11:3: + | +11 | a.transfer(x); + | ^^^^^^^^^^ + Info: CHC: 1 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/cmdlineTests/model_checker_targets_constant_condition_bmc/err b/test/cmdlineTests/model_checker_targets_constant_condition_bmc/err index a7228dd768..1e73c50a0d 100644 --- a/test/cmdlineTests/model_checker_targets_constant_condition_bmc/err +++ b/test/cmdlineTests/model_checker_targets_constant_condition_bmc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: BMC: Condition is always true. --> input.sol:6:11: | diff --git a/test/cmdlineTests/model_checker_targets_constant_condition_chc/err b/test/cmdlineTests/model_checker_targets_constant_condition_chc/err new file mode 100644 index 0000000000..4876e93661 --- /dev/null +++ b/test/cmdlineTests/model_checker_targets_constant_condition_chc/err @@ -0,0 +1,5 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:11:3: + | +11 | a.transfer(x); + | ^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_targets_default_all_engines/err b/test/cmdlineTests/model_checker_targets_default_all_engines/err index 58cbc01f14..763f8f16c5 100644 --- a/test/cmdlineTests/model_checker_targets_default_all_engines/err +++ b/test/cmdlineTests/model_checker_targets_default_all_engines/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: CHC: Division by zero happens here. Counterexample: arr = [] diff --git a/test/cmdlineTests/model_checker_targets_default_bmc/err b/test/cmdlineTests/model_checker_targets_default_bmc/err index bc85e3cb4a..d7499c998e 100644 --- a/test/cmdlineTests/model_checker_targets_default_bmc/err +++ b/test/cmdlineTests/model_checker_targets_default_bmc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: BMC: Condition is always true. --> input.sol:6:11: | diff --git a/test/cmdlineTests/model_checker_targets_default_chc/err b/test/cmdlineTests/model_checker_targets_default_chc/err index e8b7382168..d4a342ac0b 100644 --- a/test/cmdlineTests/model_checker_targets_default_chc/err +++ b/test/cmdlineTests/model_checker_targets_default_chc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: CHC: Division by zero happens here. Counterexample: arr = [] diff --git a/test/cmdlineTests/model_checker_targets_div_by_zero_bmc/err b/test/cmdlineTests/model_checker_targets_div_by_zero_bmc/err index 2d554ebceb..ee8c7cc377 100644 --- a/test/cmdlineTests/model_checker_targets_div_by_zero_bmc/err +++ b/test/cmdlineTests/model_checker_targets_div_by_zero_bmc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: BMC: Division by zero happens here. --> input.sol:9:3: | diff --git a/test/cmdlineTests/model_checker_targets_div_by_zero_chc/err b/test/cmdlineTests/model_checker_targets_div_by_zero_chc/err index 81616b3bed..3e00272485 100644 --- a/test/cmdlineTests/model_checker_targets_div_by_zero_chc/err +++ b/test/cmdlineTests/model_checker_targets_div_by_zero_chc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: CHC: Division by zero happens here. Counterexample: arr = [] diff --git a/test/cmdlineTests/model_checker_targets_out_of_bounds_bmc/err b/test/cmdlineTests/model_checker_targets_out_of_bounds_bmc/err new file mode 100644 index 0000000000..4876e93661 --- /dev/null +++ b/test/cmdlineTests/model_checker_targets_out_of_bounds_bmc/err @@ -0,0 +1,5 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:11:3: + | +11 | a.transfer(x); + | ^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_targets_out_of_bounds_chc/err b/test/cmdlineTests/model_checker_targets_out_of_bounds_chc/err index 907a19f1e1..9026ef934d 100644 --- a/test/cmdlineTests/model_checker_targets_out_of_bounds_chc/err +++ b/test/cmdlineTests/model_checker_targets_out_of_bounds_chc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: CHC: Out of bounds access happens here. Counterexample: arr = [] diff --git a/test/cmdlineTests/model_checker_targets_overflow_bmc/err b/test/cmdlineTests/model_checker_targets_overflow_bmc/err index a0f9ff1e07..81dd72c236 100644 --- a/test/cmdlineTests/model_checker_targets_overflow_bmc/err +++ b/test/cmdlineTests/model_checker_targets_overflow_bmc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: BMC: Overflow (resulting value larger than 2**256 - 1) happens here. --> input.sol:8:3: | diff --git a/test/cmdlineTests/model_checker_targets_overflow_chc/err b/test/cmdlineTests/model_checker_targets_overflow_chc/err index 60298f0228..178b59a44e 100644 --- a/test/cmdlineTests/model_checker_targets_overflow_chc/err +++ b/test/cmdlineTests/model_checker_targets_overflow_chc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: CHC: Overflow (resulting value larger than 2**256 - 1) happens here. Counterexample: arr = [] diff --git a/test/cmdlineTests/model_checker_targets_pop_empty_bmc/err b/test/cmdlineTests/model_checker_targets_pop_empty_bmc/err new file mode 100644 index 0000000000..4876e93661 --- /dev/null +++ b/test/cmdlineTests/model_checker_targets_pop_empty_bmc/err @@ -0,0 +1,5 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:11:3: + | +11 | a.transfer(x); + | ^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_targets_pop_empty_chc/err b/test/cmdlineTests/model_checker_targets_pop_empty_chc/err index a012487757..e4c1bbc0ef 100644 --- a/test/cmdlineTests/model_checker_targets_pop_empty_chc/err +++ b/test/cmdlineTests/model_checker_targets_pop_empty_chc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: CHC: Empty array "pop" happens here. Counterexample: arr = [] diff --git a/test/cmdlineTests/model_checker_targets_underflow_bmc/err b/test/cmdlineTests/model_checker_targets_underflow_bmc/err index db14639238..e4b29a98cc 100644 --- a/test/cmdlineTests/model_checker_targets_underflow_bmc/err +++ b/test/cmdlineTests/model_checker_targets_underflow_bmc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: BMC: Underflow (resulting value less than 0) happens here. --> input.sol:7:3: | diff --git a/test/cmdlineTests/model_checker_targets_underflow_chc/err b/test/cmdlineTests/model_checker_targets_underflow_chc/err index 57c6649a40..f9fbf82eec 100644 --- a/test/cmdlineTests/model_checker_targets_underflow_chc/err +++ b/test/cmdlineTests/model_checker_targets_underflow_chc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: CHC: Underflow (resulting value less than 0) happens here. Counterexample: arr = [] diff --git a/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_bmc/err b/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_bmc/err index 670b064ce1..72a9474560 100644 --- a/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_bmc/err +++ b/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_bmc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: BMC: Underflow (resulting value less than 0) happens here. --> input.sol:7:3: | diff --git a/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_chc/err b/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_chc/err index 4602e76006..f91c067026 100644 --- a/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_chc/err +++ b/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_chc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: CHC: Underflow (resulting value less than 0) happens here. Counterexample: arr = [] diff --git a/test/cmdlineTests/model_checker_targets_underflow_overflow_bmc/err b/test/cmdlineTests/model_checker_targets_underflow_overflow_bmc/err index ea6dfb99cb..e6d8c061a4 100644 --- a/test/cmdlineTests/model_checker_targets_underflow_overflow_bmc/err +++ b/test/cmdlineTests/model_checker_targets_underflow_overflow_bmc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: BMC: Underflow (resulting value less than 0) happens here. --> input.sol:7:3: | diff --git a/test/cmdlineTests/model_checker_targets_underflow_overflow_chc/err b/test/cmdlineTests/model_checker_targets_underflow_overflow_chc/err index 8eed85ac3d..44faa5ae83 100644 --- a/test/cmdlineTests/model_checker_targets_underflow_overflow_chc/err +++ b/test/cmdlineTests/model_checker_targets_underflow_overflow_chc/err @@ -1,3 +1,9 @@ +Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. + --> input.sol:10:3: + | +10 | a.transfer(x); + | ^^^^^^^^^^ + Warning: CHC: Underflow (resulting value less than 0) happens here. Counterexample: arr = [] diff --git a/test/cmdlineTests/standard_model_checker_targets_assert_bmc/output.json b/test/cmdlineTests/standard_model_checker_targets_assert_bmc/output.json index 9e90ec538d..5cbb28b979 100644 --- a/test/cmdlineTests/standard_model_checker_targets_assert_bmc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_assert_bmc/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "4661", diff --git a/test/cmdlineTests/standard_model_checker_targets_assert_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_assert_chc/output.json index bf4fa66269..d7cfd4a82c 100644 --- a/test/cmdlineTests/standard_model_checker_targets_assert_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_assert_chc/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "6328", diff --git a/test/cmdlineTests/standard_model_checker_targets_balance_bmc/output.json b/test/cmdlineTests/standard_model_checker_targets_balance_bmc/output.json index 6c2c90c988..faeb47498a 100644 --- a/test/cmdlineTests/standard_model_checker_targets_balance_bmc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_balance_bmc/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "1236", diff --git a/test/cmdlineTests/standard_model_checker_targets_balance_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_balance_chc/output.json index 9d3e40b708..a323ac1ed3 100644 --- a/test/cmdlineTests/standard_model_checker_targets_balance_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_balance_chc/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "1391", diff --git a/test/cmdlineTests/standard_model_checker_targets_constantCondition_bmc/output.json b/test/cmdlineTests/standard_model_checker_targets_constantCondition_bmc/output.json index 9bfdf0b021..74897928fe 100644 --- a/test/cmdlineTests/standard_model_checker_targets_constantCondition_bmc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_constantCondition_bmc/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "6838", diff --git a/test/cmdlineTests/standard_model_checker_targets_constantCondition_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_constantCondition_chc/output.json index f047ff70a9..d6a7b5a4ef 100644 --- a/test/cmdlineTests/standard_model_checker_targets_constantCondition_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_constantCondition_chc/output.json @@ -1,4 +1,25 @@ { + "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + } + ], "sources": { "A": { "id": 0 diff --git a/test/cmdlineTests/standard_model_checker_targets_default_all_engines/output.json b/test/cmdlineTests/standard_model_checker_targets_default_all_engines/output.json index 8f20d854bf..964bcd6c60 100644 --- a/test/cmdlineTests/standard_model_checker_targets_default_all_engines/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_default_all_engines/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "4281", diff --git a/test/cmdlineTests/standard_model_checker_targets_default_bmc/output.json b/test/cmdlineTests/standard_model_checker_targets_default_bmc/output.json index e055d38d2e..00e90a5c37 100644 --- a/test/cmdlineTests/standard_model_checker_targets_default_bmc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_default_bmc/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "6838", diff --git a/test/cmdlineTests/standard_model_checker_targets_default_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_default_chc/output.json index e045a8bff4..5f1f5a9c55 100644 --- a/test/cmdlineTests/standard_model_checker_targets_default_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_default_chc/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "4281", diff --git a/test/cmdlineTests/standard_model_checker_targets_div_by_zero_bmc/output.json b/test/cmdlineTests/standard_model_checker_targets_div_by_zero_bmc/output.json index 6ba0e6f820..ccca7af6b7 100644 --- a/test/cmdlineTests/standard_model_checker_targets_div_by_zero_bmc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_div_by_zero_bmc/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "3046", diff --git a/test/cmdlineTests/standard_model_checker_targets_div_by_zero_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_div_by_zero_chc/output.json index 0789c79996..e5a54e4c87 100644 --- a/test/cmdlineTests/standard_model_checker_targets_div_by_zero_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_div_by_zero_chc/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "4281", diff --git a/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_bmc/output.json b/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_bmc/output.json index f047ff70a9..d6a7b5a4ef 100644 --- a/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_bmc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_bmc/output.json @@ -1,4 +1,25 @@ { + "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + } + ], "sources": { "A": { "id": 0 diff --git a/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_chc/output.json index f5f49fb508..ecb01eac35 100644 --- a/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_chc/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "6368", diff --git a/test/cmdlineTests/standard_model_checker_targets_overflow_bmc/output.json b/test/cmdlineTests/standard_model_checker_targets_overflow_bmc/output.json index 8d34765cdb..5dbe011f19 100644 --- a/test/cmdlineTests/standard_model_checker_targets_overflow_bmc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_overflow_bmc/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "2661", diff --git a/test/cmdlineTests/standard_model_checker_targets_overflow_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_overflow_chc/output.json index 9beb30a35b..1b0078d579 100644 --- a/test/cmdlineTests/standard_model_checker_targets_overflow_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_overflow_chc/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "4984", diff --git a/test/cmdlineTests/standard_model_checker_targets_pop_empty_bmc/output.json b/test/cmdlineTests/standard_model_checker_targets_pop_empty_bmc/output.json index f047ff70a9..d6a7b5a4ef 100644 --- a/test/cmdlineTests/standard_model_checker_targets_pop_empty_bmc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_pop_empty_bmc/output.json @@ -1,4 +1,25 @@ { + "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + } + ], "sources": { "A": { "id": 0 diff --git a/test/cmdlineTests/standard_model_checker_targets_pop_empty_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_pop_empty_chc/output.json index 4cccf8456d..6ac6acd960 100644 --- a/test/cmdlineTests/standard_model_checker_targets_pop_empty_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_pop_empty_chc/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "2529", diff --git a/test/cmdlineTests/standard_model_checker_targets_underflow_bmc/output.json b/test/cmdlineTests/standard_model_checker_targets_underflow_bmc/output.json index df17886b2f..448c4285f3 100644 --- a/test/cmdlineTests/standard_model_checker_targets_underflow_bmc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_underflow_bmc/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "4144", diff --git a/test/cmdlineTests/standard_model_checker_targets_underflow_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_underflow_chc/output.json index 442f2f47a9..67218582ba 100644 --- a/test/cmdlineTests/standard_model_checker_targets_underflow_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_underflow_chc/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "3944", diff --git a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_bmc/output.json b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_bmc/output.json index c80d3e22a8..ce83f3f280 100644 --- a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_bmc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_bmc/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "4144", diff --git a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_chc/output.json index e5719640aa..8f28e79615 100644 --- a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_chc/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "3944", diff --git a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_bmc/output.json b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_bmc/output.json index de02d57c90..2162e723f6 100644 --- a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_bmc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_bmc/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "4144", diff --git a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_chc/output.json index 6fb8762e43..97442455c0 100644 --- a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_chc/output.json @@ -1,5 +1,24 @@ { "errors": [ + { + "component": "general", + "errorCode": "9207", + "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. + --> A:11:7: + | +11 | \t\t\t\t\t\ta.transfer(x); + | \t\t\t\t\t\t^^^^^^^^^^ + +", + "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", + "severity": "warning", + "sourceLocation": { + "end": 234, + "file": "A", + "start": 224 + }, + "type": "Warning" + }, { "component": "general", "errorCode": "3944", diff --git a/test/cmdlineTests/viair_abicoder_v1/err b/test/cmdlineTests/viair_abicoder_v1/err index 29fa2acd04..ca7671fe38 100644 --- a/test/cmdlineTests/viair_abicoder_v1/err +++ b/test/cmdlineTests/viair_abicoder_v1/err @@ -1,3 +1,9 @@ +Warning (9511): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. + --> input.sol:3:1: + | +3 | pragma abicoder v1; + | ^^^^^^^^^^^^^^^^^^^ + Warning (2066): Contract requests the ABI coder v1, which is incompatible with the IR. Using ABI coder v2 instead. --> input.sol:4:1: | diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/balance_spend.sol b/test/libsolidity/smtCheckerTests/blockchain_state/balance_spend.sol index 720ab56950..01c0a19001 100644 --- a/test/libsolidity/smtCheckerTests/blockchain_state/balance_spend.sol +++ b/test/libsolidity/smtCheckerTests/blockchain_state/balance_spend.sol @@ -18,5 +18,6 @@ contract C { // SMTEngine: all // SMTIgnoreCex: yes // ---- +// Warning 9207: (175-186): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. // Warning 6328: (280-314): CHC: Assertion violation happens here. // Info 1391: CHC: 3 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/balance_spend_2.sol b/test/libsolidity/smtCheckerTests/blockchain_state/balance_spend_2.sol index 3ceac964f1..94c793df0b 100644 --- a/test/libsolidity/smtCheckerTests/blockchain_state/balance_spend_2.sol +++ b/test/libsolidity/smtCheckerTests/blockchain_state/balance_spend_2.sol @@ -17,6 +17,7 @@ contract C { // SMTIgnoreCex: yes // SMTIgnoreOS: macos // ---- +// Warning 9207: (141-152): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. // Warning 8656: (141-156): CHC: Insufficient funds happens here. // Warning 6328: (193-226): CHC: Assertion violation happens here. // Warning 6328: (245-279): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/decreasing_balance.sol b/test/libsolidity/smtCheckerTests/blockchain_state/decreasing_balance.sol index df35a27858..75a02b7b51 100644 --- a/test/libsolidity/smtCheckerTests/blockchain_state/decreasing_balance.sol +++ b/test/libsolidity/smtCheckerTests/blockchain_state/decreasing_balance.sol @@ -18,4 +18,5 @@ contract C { // ==== // SMTEngine: all // ---- +// Warning 9207: (160-170): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. // Info 1391: CHC: 1 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/free_function_2.sol b/test/libsolidity/smtCheckerTests/blockchain_state/free_function_2.sol index 3b7c0f9dce..64900cbcb1 100644 --- a/test/libsolidity/smtCheckerTests/blockchain_state/free_function_2.sol +++ b/test/libsolidity/smtCheckerTests/blockchain_state/free_function_2.sol @@ -19,5 +19,6 @@ contract C { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 6328: (258-274): CHC: Assertion violation happens here. +// Warning 9207: (33-43): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. +// Warning 6328: (258-274): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\na = 0x7e1e\nb1 = 38\nb2 = 37\n\nTransaction trace:\nC.constructor()\nState: x = 0\nC.f(0x7e1e){ msg.value: 17 }\n l(0x7e1e) -- internal call // Info 1391: CHC: 4 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/library_internal_2.sol b/test/libsolidity/smtCheckerTests/blockchain_state/library_internal_2.sol index 826af0bf86..d6203e3813 100644 --- a/test/libsolidity/smtCheckerTests/blockchain_state/library_internal_2.sol +++ b/test/libsolidity/smtCheckerTests/blockchain_state/library_internal_2.sol @@ -22,5 +22,6 @@ contract C { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 6328: (315-331): CHC: Assertion violation happens here. +// Warning 9207: (87-97): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. +// Warning 6328: (315-331): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\na = 0x7e1e\nb1 = 38\nb2 = 37\n\nTransaction trace:\nC.constructor()\nState: x = 0\nC.f(0x7e1e){ msg.value: 17 }\n L.l(0x7e1e) -- internal call // Info 1391: CHC: 4 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/library_public_2.sol b/test/libsolidity/smtCheckerTests/blockchain_state/library_public_2.sol index 02e29be8de..4689dceba0 100644 --- a/test/libsolidity/smtCheckerTests/blockchain_state/library_public_2.sol +++ b/test/libsolidity/smtCheckerTests/blockchain_state/library_public_2.sol @@ -20,7 +20,8 @@ contract C { // SMTEngine: all // SMTIgnoreCex: yes // ---- +// Warning 9207: (54-64): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. // Warning 4588: (238-243): Assertion checker does not yet implement this type of function call. -// Warning 8656: (54-67): CHC: Insufficient funds happens here. -// Warning 6328: (282-298): CHC: Assertion violation happens here. -// Warning 6328: (317-331): CHC: Assertion violation happens here. +// Warning 8656: (54-67): CHC: Insufficient funds happens here.\nCounterexample:\n\na = 0x0\n\nTransaction trace:\nL.constructor()\nL.l(0x0) +// Warning 6328: (282-298): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\na = 0x0\nb1 = 15923\nb2 = 15924\n\nTransaction trace:\nC.constructor()\nState: x = 0\nC.f(0x0){ msg.value: 15923 } +// Warning 6328: (317-331): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\na = 0x0\nb1 = 15923\nb2 = 15924\n\nTransaction trace:\nC.constructor()\nState: x = 0\nC.f(0x0){ msg.value: 15923 } diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/transfer_1.sol b/test/libsolidity/smtCheckerTests/blockchain_state/transfer_1.sol index 169257edff..086eb25497 100644 --- a/test/libsolidity/smtCheckerTests/blockchain_state/transfer_1.sol +++ b/test/libsolidity/smtCheckerTests/blockchain_state/transfer_1.sol @@ -11,5 +11,6 @@ contract C { // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 6328: (166-201): CHC: Assertion violation happens here. +// Warning 9207: (96-106): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. +// Warning 6328: (166-201): CHC: Assertion violation happens here.\nCounterexample:\n\na = 0x0\n\nTransaction trace:\nC.constructor()\nC.f(0x0) // Info 1391: CHC: 2 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/transfer_2.sol b/test/libsolidity/smtCheckerTests/blockchain_state/transfer_2.sol index ed5b4c90f2..033163516a 100644 --- a/test/libsolidity/smtCheckerTests/blockchain_state/transfer_2.sol +++ b/test/libsolidity/smtCheckerTests/blockchain_state/transfer_2.sol @@ -11,4 +11,6 @@ contract C { // ==== // SMTEngine: chc // ---- +// Warning 9207: (133-151): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. +// Warning 9207: (167-185): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. // Info 1391: CHC: 2 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/transfer_3.sol b/test/libsolidity/smtCheckerTests/blockchain_state/transfer_3.sol index 4b3318b6be..d4f3eec99e 100644 --- a/test/libsolidity/smtCheckerTests/blockchain_state/transfer_3.sol +++ b/test/libsolidity/smtCheckerTests/blockchain_state/transfer_3.sol @@ -8,4 +8,5 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 8656: (76-97): CHC: Insufficient funds happens here. +// Warning 9207: (76-94): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. +// Warning 8656: (76-97): CHC: Insufficient funds happens here.\nCounterexample:\nrecipient = 0x0\n\nTransaction trace:\nC.constructor()\nState: recipient = 0x0\nC.shouldFail() diff --git a/test/libsolidity/smtCheckerTests/blockchain_state/transfer_4.sol b/test/libsolidity/smtCheckerTests/blockchain_state/transfer_4.sol index ab0c4a81ef..838154ec3a 100644 --- a/test/libsolidity/smtCheckerTests/blockchain_state/transfer_4.sol +++ b/test/libsolidity/smtCheckerTests/blockchain_state/transfer_4.sol @@ -9,4 +9,5 @@ contract C { // ==== // SMTEngine: all // ---- +// Warning 9207: (101-119): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. // Info 1391: CHC: 1 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/bmc_coverage/funds.sol b/test/libsolidity/smtCheckerTests/bmc_coverage/funds.sol index 737443e64b..a8f90f161f 100644 --- a/test/libsolidity/smtCheckerTests/bmc_coverage/funds.sol +++ b/test/libsolidity/smtCheckerTests/bmc_coverage/funds.sol @@ -6,4 +6,5 @@ contract C { // ==== // SMTEngine: bmc // ---- +// Warning 9207: (55-65): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. // Warning 1236: (55-70): BMC: Insufficient funds happens here. diff --git a/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_addresses.sol b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_addresses.sol index 01c68790c8..b0b159f473 100644 --- a/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_addresses.sol +++ b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_addresses.sol @@ -16,4 +16,5 @@ contract C { // SMTEngine: all // SMTExtCalls: trusted // ---- +// Warning 9170: (107-115): Comparison of variables of contract type is deprecated and scheduled for removal. Use an explicit cast to address type and compare the addresses instead. // Info 1391: CHC: 3 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/deployment/deploy_untrusted_addresses.sol b/test/libsolidity/smtCheckerTests/deployment/deploy_untrusted_addresses.sol index c934e152b2..4de7eaa49f 100644 --- a/test/libsolidity/smtCheckerTests/deployment/deploy_untrusted_addresses.sol +++ b/test/libsolidity/smtCheckerTests/deployment/deploy_untrusted_addresses.sol @@ -15,8 +15,9 @@ contract C { // ==== // SMTEngine: all // ---- +// Warning 9170: (107-115): Comparison of variables of contract type is deprecated and scheduled for removal. Use an explicit cast to address type and compare the addresses instead. // Warning 8729: (70-77): Contract deployment is only supported in the trusted mode for external calls with the CHC engine. // Warning 8729: (88-95): Contract deployment is only supported in the trusted mode for external calls with the CHC engine. // Warning 6328: (100-116): CHC: Assertion violation happens here.\nCounterexample:\n\nd1 = 0\nd2 = 0\n\nTransaction trace:\nC.constructor()\nC.f() -// Warning 6328: (163-199): CHC: Assertion violation happens here.\nCounterexample:\n\nd1 = 21238\nd2 = 21238\n\nTransaction trace:\nC.constructor()\nC.f() -// Warning 6328: (246-282): CHC: Assertion violation happens here.\nCounterexample:\n\nd1 = 21238\nd2 = 21238\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (163-199): CHC: Assertion violation happens here.\nCounterexample:\n\nd1 = 11797\nd2 = 11797\n\nTransaction trace:\nC.constructor()\nC.f() +// Warning 6328: (246-282): CHC: Assertion violation happens here.\nCounterexample:\n\nd1 = 8855\nd2 = 8855\n\nTransaction trace:\nC.constructor()\nC.f() diff --git a/test/libsolidity/smtCheckerTests/functions/getters/array_of_structs_1.sol b/test/libsolidity/smtCheckerTests/functions/getters/array_of_structs_1.sol index 209b00f5f3..73f1e4aee1 100644 --- a/test/libsolidity/smtCheckerTests/functions/getters/array_of_structs_1.sol +++ b/test/libsolidity/smtCheckerTests/functions/getters/array_of_structs_1.sol @@ -15,4 +15,5 @@ contract D { // ==== // SMTEngine: all // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // Info 1391: CHC: 1 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/functions/getters/array_of_structs_2.sol b/test/libsolidity/smtCheckerTests/functions/getters/array_of_structs_2.sol index b62611b519..52389a4f7f 100644 --- a/test/libsolidity/smtCheckerTests/functions/getters/array_of_structs_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/getters/array_of_structs_2.sol @@ -20,5 +20,6 @@ contract D { // SMTEngine: all // SMTIgnoreCex: no // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // Warning 6328: (267-281): CHC: Assertion violation happens here.\nCounterexample:\nitems = [{x: 42, y: 43}]\na = 42\nb = 43\n\nTransaction trace:\nD.constructor()\nState: items = []\nD.test() // Info 1391: CHC: 2 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/functions/getters/array_of_structs_3.sol b/test/libsolidity/smtCheckerTests/functions/getters/array_of_structs_3.sol index abe50e8a54..2fee43bfb3 100644 --- a/test/libsolidity/smtCheckerTests/functions/getters/array_of_structs_3.sol +++ b/test/libsolidity/smtCheckerTests/functions/getters/array_of_structs_3.sol @@ -21,5 +21,6 @@ contract D { // ==== // SMTEngine: all // ---- -// Warning 6328: (322-336): CHC: Assertion violation happens here. +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. +// Warning 6328: (322-336): CHC: Assertion violation happens here.\nCounterexample:\nitems = [{x: 42, y: 43, arr: [0]}]\ntmp = [0]\na = 42\nb = 43\n\nTransaction trace:\nD.constructor()\nState: items = []\nD.test() // Info 1391: CHC: 2 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/functions/getters/contract.sol b/test/libsolidity/smtCheckerTests/functions/getters/contract.sol index 7da893bd36..ba95fcee14 100644 --- a/test/libsolidity/smtCheckerTests/functions/getters/contract.sol +++ b/test/libsolidity/smtCheckerTests/functions/getters/contract.sol @@ -12,5 +12,6 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 6328: (123-158): CHC: Assertion violation happens here. +// Warning 9170: (97-103): Comparison of variables of contract type is deprecated and scheduled for removal. Use an explicit cast to address type and compare the addresses instead. +// Warning 6328: (123-158): CHC: Assertion violation happens here.\nCounterexample:\nd = 0\ne = 0\n\nTransaction trace:\nC.constructor()\nState: d = 0\nC.f() // Info 1391: CHC: 1 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/functions/getters/struct_4.sol b/test/libsolidity/smtCheckerTests/functions/getters/struct_4.sol index 345959354f..928580207d 100644 --- a/test/libsolidity/smtCheckerTests/functions/getters/struct_4.sol +++ b/test/libsolidity/smtCheckerTests/functions/getters/struct_4.sol @@ -18,7 +18,8 @@ contract C { // ==== // SMTEngine: all // ---- +// Warning 9170: (206-214): Comparison of variables of contract type is deprecated and scheduled for removal. Use an explicit cast to address type and compare the addresses instead. // Warning 2072: (146-183): Unused local variable. // Warning 8364: (187-193): Assertion checker does not yet implement type function () view external returns (contract D,function () external returns (uint256)) -// Warning 6328: (234-269): CHC: Assertion violation happens here. +// Warning 6328: (234-269): CHC: Assertion violation happens here.\nCounterexample:\ns = {d: 0, f: 0}\nd = 0\nf = 0\n\nTransaction trace:\nC.constructor()\nState: s = {d: 0, f: 0}\nC.test() // Info 1391: CHC: 1 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_abstract.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_abstract.sol index 9d541b6299..3ed457a0c9 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_abstract.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_abstract.sol @@ -5,3 +5,4 @@ abstract contract A { // ==== // SMTEngine: all // ---- +// Warning 8429: (51-72): Virtual modifiers are deprecated and scheduled for removal. diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_overriding_1.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_overriding_1.sol index 9165ed1331..9c1a59df94 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_overriding_1.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_overriding_1.sol @@ -20,5 +20,6 @@ contract B is A { // ==== // SMTEngine: all // ---- -// Warning 6328: (209-223): CHC: Assertion violation happens here. +// Warning 8429: (64-93): Virtual modifiers are deprecated and scheduled for removal. +// Warning 6328: (209-223): CHC: Assertion violation happens here.\nCounterexample:\ns = 42\nx = 42\n\nTransaction trace:\nB.constructor()\nState: s = 0\nB.set(42)\nState: s = 42\nA.f() // Info 1391: CHC: 1 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_overriding_2.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_overriding_2.sol index fd9dc90085..28df58db2e 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_overriding_2.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_overriding_2.sol @@ -23,4 +23,6 @@ contract C is B { // ==== // SMTEngine: all // ---- +// Warning 8429: (113-136): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (159-213): Virtual modifiers are deprecated and scheduled for removal. // Warning 6328: (66-75): CHC: Assertion violation happens here.\nCounterexample:\ns = false\n\nTransaction trace:\nB.constructor()\nState: s = false\nA.f() diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_overriding_3.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_overriding_3.sol index f85e1b3bae..a213caee1c 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_overriding_3.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_overriding_3.sol @@ -18,5 +18,7 @@ contract B is A { // ==== // SMTEngine: all // ---- -// Warning 6328: (94-104): CHC: Assertion violation happens here. +// Warning 8429: (125-148): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (171-238): Virtual modifiers are deprecated and scheduled for removal. +// Warning 6328: (94-104): CHC: Assertion violation happens here.\nCounterexample:\ns = true\nx = true\n\nTransaction trace:\nB.constructor()\nState: s = false\nA.f() // Info 1391: CHC: 1 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_overriding_4.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_overriding_4.sol index d0e82665cc..87e5517eef 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_overriding_4.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_overriding_4.sol @@ -36,6 +36,10 @@ contract D is B,C { // ==== // SMTEngine: all // ---- +// Warning 8429: (262-294): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (317-367): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (390-440): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (465-520): Virtual modifiers are deprecated and scheduled for removal. // Warning 6328: (160-174): CHC: Assertion violation happens here.\nCounterexample:\nx = 1\n\nTransaction trace:\nB.constructor()\nState: x = 0\nA.f() // Warning 6328: (193-207): CHC: Assertion violation happens here.\nCounterexample:\nx = 2\n\nTransaction trace:\nC.constructor()\nState: x = 0\nA.f() // Warning 6328: (226-240): CHC: Assertion violation happens here.\nCounterexample:\nx = 3\n\nTransaction trace:\nD.constructor()\nState: x = 0\nA.f() diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_virtual_static_call_1.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_virtual_static_call_1.sol index 40bc6d93eb..998337d04a 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_virtual_static_call_1.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_virtual_static_call_1.sol @@ -10,3 +10,4 @@ contract C is A { // ==== // SMTEngine: all // ---- +// Warning 8429: (17-52): Virtual modifiers are deprecated and scheduled for removal. diff --git a/test/libsolidity/smtCheckerTests/modifiers/modifier_virtual_static_call_2.sol b/test/libsolidity/smtCheckerTests/modifiers/modifier_virtual_static_call_2.sol index b270fb2bb5..0d32bcd649 100644 --- a/test/libsolidity/smtCheckerTests/modifiers/modifier_virtual_static_call_2.sol +++ b/test/libsolidity/smtCheckerTests/modifiers/modifier_virtual_static_call_2.sol @@ -20,5 +20,6 @@ contract C is A { // ==== // SMTEngine: all // ---- -// Warning 6328: (83-98): CHC: Assertion violation happens here. +// Warning 8429: (27-122): Virtual modifiers are deprecated and scheduled for removal. +// Warning 6328: (83-98): CHC: Assertion violation happens here.\nCounterexample:\nx = 0\n = 0\n\nTransaction trace:\nC.constructor()\nState: x = 0\nC.f() // Info 1391: CHC: 1 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/types/address_transfer.sol b/test/libsolidity/smtCheckerTests/types/address_transfer.sol index 1430e15cb8..734a70c803 100644 --- a/test/libsolidity/smtCheckerTests/types/address_transfer.sol +++ b/test/libsolidity/smtCheckerTests/types/address_transfer.sol @@ -11,5 +11,6 @@ contract C // ==== // SMTEngine: all // ---- -// Warning 8656: (98-113): CHC: Insufficient funds happens here. -// Warning 6328: (162-186): CHC: Assertion violation happens here. +// Warning 9207: (98-108): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. +// Warning 8656: (98-113): CHC: Insufficient funds happens here.\nCounterexample:\n\na = 0x51f0\nx = 100\n\nTransaction trace:\nC.constructor()\nC.f(0x51f0) +// Warning 6328: (162-186): CHC: Assertion violation happens here.\nCounterexample:\n\na = 0x0\nx = 100\n\nTransaction trace:\nC.constructor()\nC.f(0x0) diff --git a/test/libsolidity/smtCheckerTests/types/address_transfer_2.sol b/test/libsolidity/smtCheckerTests/types/address_transfer_2.sol index ce6492d9e5..0d558b85ae 100644 --- a/test/libsolidity/smtCheckerTests/types/address_transfer_2.sol +++ b/test/libsolidity/smtCheckerTests/types/address_transfer_2.sol @@ -15,6 +15,8 @@ contract C // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 8656: (184-199): CHC: Insufficient funds happens here. -// Warning 8656: (203-218): CHC: Insufficient funds happens here. -// Warning 6328: (262-291): CHC: Assertion violation happens here. +// Warning 9207: (184-194): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. +// Warning 9207: (203-213): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. +// Warning 8656: (184-199): CHC: Insufficient funds happens here.\nCounterexample:\n\nx = 100\na = 0x6532\nb = 0xffffffffffffffffffffffffffffffffffffed9d\n\nTransaction trace:\nC.constructor()\nC.f(100, 0x6532, 0xffffffffffffffffffffffffffffffffffffed9d) +// Warning 8656: (203-218): CHC: Insufficient funds happens here.\nCounterexample:\n\nx = 100\na = 0x08c0\nb = 0x7992\n\nTransaction trace:\nC.constructor()\nC.f(100, 0x08c0, 0x7992) +// Warning 6328: (262-291): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 100\na = 0x08c1\nb = 0x08c0\n\nTransaction trace:\nC.constructor()\nC.f(100, 0x08c1, 0x08c0) diff --git a/test/libsolidity/smtCheckerTests/types/address_transfer_3.sol b/test/libsolidity/smtCheckerTests/types/address_transfer_3.sol index 3f9cc22c7b..5d0de50210 100644 --- a/test/libsolidity/smtCheckerTests/types/address_transfer_3.sol +++ b/test/libsolidity/smtCheckerTests/types/address_transfer_3.sol @@ -13,4 +13,5 @@ contract C // ==== // SMTEngine: all // ---- +// Warning 9207: (126-136): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. // Info 1391: CHC: 2 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/types/address_transfer_insufficient.sol b/test/libsolidity/smtCheckerTests/types/address_transfer_insufficient.sol index 55a96026ee..5a9f88aae3 100644 --- a/test/libsolidity/smtCheckerTests/types/address_transfer_insufficient.sol +++ b/test/libsolidity/smtCheckerTests/types/address_transfer_insufficient.sol @@ -12,6 +12,8 @@ contract C // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 8656: (101-116): CHC: Insufficient funds happens here. -// Warning 8656: (120-136): CHC: Insufficient funds happens here. -// Warning 6328: (180-204): CHC: Assertion violation happens here. +// Warning 9207: (101-111): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. +// Warning 9207: (120-130): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. +// Warning 8656: (101-116): CHC: Insufficient funds happens here.\nCounterexample:\n\na = 0x0\nb = 0x0\n\nTransaction trace:\nC.constructor()\nC.f(0x0, 0x0) +// Warning 8656: (120-136): CHC: Insufficient funds happens here.\nCounterexample:\n\na = 0x0\nb = 0x0\n\nTransaction trace:\nC.constructor()\nC.f(0x0, 0x0) +// Warning 6328: (180-204): CHC: Assertion violation happens here.\nCounterexample:\n\na = 0x0476\nb = 0x0476\n\nTransaction trace:\nC.constructor()\nC.f(0x0476, 0x0476) diff --git a/test/libsolidity/smtCheckerTests/types/contract.sol b/test/libsolidity/smtCheckerTests/types/contract.sol index 0ddc3e6503..a631fe1c8c 100644 --- a/test/libsolidity/smtCheckerTests/types/contract.sol +++ b/test/libsolidity/smtCheckerTests/types/contract.sol @@ -8,4 +8,5 @@ contract C // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 6328: (51-65): CHC: Assertion violation happens here. +// Warning 9170: (58-64): Comparison of variables of contract type is deprecated and scheduled for removal. Use an explicit cast to address type and compare the addresses instead. +// Warning 6328: (51-65): CHC: Assertion violation happens here.\nCounterexample:\n\nc = 0\nd = 1\n\nTransaction trace:\nC.constructor()\nC.f(0, 1) diff --git a/test/libsolidity/smtCheckerTests/types/contract_2.sol b/test/libsolidity/smtCheckerTests/types/contract_2.sol index 2357852d56..262c157b51 100644 --- a/test/libsolidity/smtCheckerTests/types/contract_2.sol +++ b/test/libsolidity/smtCheckerTests/types/contract_2.sol @@ -13,4 +13,5 @@ contract C // SMTEngine: all // SMTIgnoreCex: yes // ---- -// Warning 6328: (76-90): CHC: Assertion violation happens here. +// Warning 9170: (83-89): Comparison of variables of contract type is deprecated and scheduled for removal. Use an explicit cast to address type and compare the addresses instead. +// Warning 6328: (76-90): CHC: Assertion violation happens here.\nCounterexample:\n\nc = 0\nd = 1\n\nTransaction trace:\nC.constructor()\nC.f(0, 1) diff --git a/test/libsolidity/smtCheckerTests/types/contract_3.sol b/test/libsolidity/smtCheckerTests/types/contract_3.sol index 657999c0b7..0ddf195ba7 100644 --- a/test/libsolidity/smtCheckerTests/types/contract_3.sol +++ b/test/libsolidity/smtCheckerTests/types/contract_3.sol @@ -9,4 +9,7 @@ contract C // ==== // SMTEngine: all // ---- +// Warning 9170: (64-70): Comparison of variables of contract type is deprecated and scheduled for removal. Use an explicit cast to address type and compare the addresses instead. +// Warning 9170: (83-89): Comparison of variables of contract type is deprecated and scheduled for removal. Use an explicit cast to address type and compare the addresses instead. +// Warning 9170: (101-107): Comparison of variables of contract type is deprecated and scheduled for removal. Use an explicit cast to address type and compare the addresses instead. // Info 1391: CHC: 1 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/types/contract_address_conversion_2.sol b/test/libsolidity/smtCheckerTests/types/contract_address_conversion_2.sol index 0b395c1877..f79878993b 100644 --- a/test/libsolidity/smtCheckerTests/types/contract_address_conversion_2.sol +++ b/test/libsolidity/smtCheckerTests/types/contract_address_conversion_2.sol @@ -10,4 +10,5 @@ contract C // ==== // SMTEngine: all // ---- +// Warning 9170: (121-127): Comparison of variables of contract type is deprecated and scheduled for removal. Use an explicit cast to address type and compare the addresses instead. // Info 1391: CHC: 2 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings.sol b/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings.sol index da81a3bc66..f7e41bd61d 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings.sol @@ -2,3 +2,4 @@ pragma abicoder v2; pragma abicoder v1; // ---- // SyntaxError 3845: (34-53): ABI coder has already been selected for this source unit. +// Warning 9511: (34-53): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. diff --git a/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse.sol b/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse.sol index f06b2458c3..feacbbeb31 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse.sol @@ -1,4 +1,5 @@ pragma abicoder v1; pragma abicoder v2; // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // SyntaxError 3845: (20-39): ABI coder has already been selected for this source unit. diff --git a/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse_experimental.sol b/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse_experimental.sol index 855378e9ef..884d03132e 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse_experimental.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse_experimental.sol @@ -1,4 +1,5 @@ pragma abicoder v1; pragma experimental ABIEncoderV2; // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // SyntaxError 8273: (20-53): ABI coder v1 has already been selected through "pragma abicoder v1". diff --git a/test/libsolidity/syntaxTests/abiEncoder/select_v1.sol b/test/libsolidity/syntaxTests/abiEncoder/select_v1.sol index 1beb5f0ec7..ab430610cc 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/select_v1.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/select_v1.sol @@ -1,2 +1,3 @@ pragma abicoder v1; // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. diff --git a/test/libsolidity/syntaxTests/abiEncoder/select_v1_quoted_string.sol b/test/libsolidity/syntaxTests/abiEncoder/select_v1_quoted_string.sol index a6c26efdc7..b8083cd354 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/select_v1_quoted_string.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/select_v1_quoted_string.sol @@ -1,2 +1,3 @@ pragma abicoder "v1"; // ---- +// Warning 9511: (0-21): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. diff --git a/test/libsolidity/syntaxTests/abiEncoder/selected_twice.sol b/test/libsolidity/syntaxTests/abiEncoder/selected_twice.sol index 424c2c7f2b..172ee0b57d 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/selected_twice.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/selected_twice.sol @@ -1,4 +1,6 @@ pragma abicoder v1; pragma abicoder v1; // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // SyntaxError 3845: (20-39): ABI coder has already been selected for this source unit. +// Warning 9511: (20-39): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_accessing_public_state_variable_via_v1_type.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_accessing_public_state_variable_via_v1_type.sol index 036a259761..f6bc52a1e1 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_accessing_public_state_variable_via_v1_type.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_accessing_public_state_variable_via_v1_type.sol @@ -16,3 +16,4 @@ contract D { // ==== // bytecodeFormat: legacy // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_accessing_public_state_variable_via_v2_type.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_accessing_public_state_variable_via_v2_type.sol index feaf895e17..7a5fbc5719 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_accessing_public_state_variable_via_v2_type.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_accessing_public_state_variable_via_v2_type.sol @@ -14,5 +14,6 @@ contract D { } } // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 7364: (222-260): Different number of components on the left hand side (1) than on the right hand side (2). // TypeError 9574: (222-260): Type uint256 is not implicitly convertible to expected type struct Item memory. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v1_library_function_accepting_storage_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v1_library_function_accepting_storage_struct.sol index 03fb9bae67..61763429b1 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v1_library_function_accepting_storage_struct.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v1_library_function_accepting_storage_struct.sol @@ -22,3 +22,4 @@ contract Test { // ==== // bytecodeFormat: legacy // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_constructor_accepting_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_constructor_accepting_struct.sol index 3ee6f75a17..d114c7192d 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_constructor_accepting_struct.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_constructor_accepting_struct.sol @@ -18,4 +18,5 @@ contract Test { } } // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 2443: (B:91-100): The type of this parameter, struct C.Item memory, is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_accepting_struct_via_named_argument.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_accepting_struct_via_named_argument.sol index c02faba189..078e977e91 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_accepting_struct_via_named_argument.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_accepting_struct_via_named_argument.sol @@ -18,4 +18,5 @@ contract Test { } } // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 2443: (B:119-129): The type of this parameter, struct C.Item memory, is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_pointer_accepting_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_pointer_accepting_struct.sol index 674ce4e32a..8c67e60881 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_pointer_accepting_struct.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_pointer_accepting_struct.sol @@ -20,4 +20,5 @@ contract Test { } } // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 2443: (B:166-175): The type of this parameter, struct C.Item memory, is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_dynamic_string_array.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_dynamic_string_array.sol index 5379bdb503..1f7c8d7545 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_dynamic_string_array.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_dynamic_string_array.sol @@ -14,4 +14,5 @@ contract D { } } // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 2428: (B:85-105): The type of return parameter 1, string[] memory, is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_struct.sol index d2b0e1ffe2..c5dbe401be 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_struct.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_struct.sol @@ -18,4 +18,5 @@ contract Test { } } // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 2428: (B:90-112): The type of return parameter 1, struct C.Item memory, is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_struct_with_dynamic_array.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_struct_with_dynamic_array.sol index 0aa9973aa7..6117b79ccf 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_struct_with_dynamic_array.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_contract_function_returning_struct_with_dynamic_array.sol @@ -18,4 +18,5 @@ contract Test { } } // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 2428: (B:90-112): The type of return parameter 1, struct C.Item memory, is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_event_accepting_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_event_accepting_struct.sol index e868418683..e78a5a1933 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_event_accepting_struct.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_event_accepting_struct.sol @@ -17,4 +17,5 @@ contract Test { } } // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 2443: (B:94-104): The type of this parameter, struct L.Item memory, is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_attached_function_returning_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_attached_function_returning_struct.sol index bb3651d4f8..bb394cc7a2 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_attached_function_returning_struct.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_attached_function_returning_struct.sol @@ -20,4 +20,5 @@ contract D { } } // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 2428: (B:106-117): The type of return parameter 1, struct L.Item memory, is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_function_accepting_storage_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_function_accepting_storage_struct.sol index 01d9658ce2..0a6c80b7c2 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_function_accepting_storage_struct.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_function_accepting_storage_struct.sol @@ -22,3 +22,4 @@ contract Test { // ==== // bytecodeFormat: legacy // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_function_returning_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_function_returning_struct.sol index c6ba752259..4a32c474ab 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_function_returning_struct.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_library_function_returning_struct.sol @@ -18,4 +18,5 @@ contract Test { } } // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 2428: (B:90-97): The type of return parameter 1, struct L.Item memory, is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_modifier.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_modifier.sol index ef066593c4..63b082834c 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_modifier.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_call_to_v2_modifier.sol @@ -28,3 +28,4 @@ contract C is B { // ==== // bytecodeFormat: legacy // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_constructor_with_v2_modifier.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_constructor_with_v2_modifier.sol index b937d1e2d1..84a58a812e 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_constructor_with_v2_modifier.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_constructor_with_v2_modifier.sol @@ -35,3 +35,5 @@ contract D is C { // ==== // bytecodeFormat: legacy // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. +// Warning 9511: (C:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_calling_v2_function.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_calling_v2_function.sol index 9743d5318c..bae6d83c8c 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_calling_v2_function.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_calling_v2_function.sol @@ -26,3 +26,4 @@ contract C is B {} // ==== // bytecodeFormat: legacy // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_event.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_event.sol index e4492a4ef7..accaa7d6bd 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_event.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_event.sol @@ -16,3 +16,4 @@ contract D is C {} // ==== // bytecodeFormat: legacy // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_function_accepting_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_function_accepting_struct.sol index c37bad41c2..9773036816 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_function_accepting_struct.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_function_accepting_struct.sol @@ -14,4 +14,5 @@ import "A"; contract D is C {} // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 6594: (B:33-51): Contract "D" does not use ABI coder v2 but wants to inherit from a contract which uses types that require it. Use "pragma abicoder v2;" for the inheriting contract as well to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_function_returning_struct.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_function_returning_struct.sol index f257542469..2a3e41a406 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_function_returning_struct.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_defining_v2_function_returning_struct.sol @@ -14,4 +14,5 @@ import "A"; contract D is C {} // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 6594: (B:33-51): Contract "D" does not use ABI coder v2 but wants to inherit from a contract which uses types that require it. Use "pragma abicoder v2;" for the inheriting contract as well to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_emitting_v2_event.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_emitting_v2_event.sol index 5ae4cb0ed8..791163be5b 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_emitting_v2_event.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_inheritance_from_contract_emitting_v2_event.sol @@ -22,3 +22,4 @@ contract D is C {} // ==== // bytecodeFormat: legacy // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_modifier_overriding_v2_modifier.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_modifier_overriding_v2_modifier.sol index 9a809b50a2..bae4a1b1d3 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_modifier_overriding_v2_modifier.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_modifier_overriding_v2_modifier.sol @@ -30,3 +30,5 @@ contract C is B { // ==== // bytecodeFormat: legacy // ---- +// Warning 8429: (A:156-234): Virtual modifiers are deprecated and scheduled for removal. +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v1_v2_v1_modifier_mix.sol b/test/libsolidity/syntaxTests/abiEncoder/v1_v2_v1_modifier_mix.sol index 6665c42efd..75e3ae94cf 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v1_v2_v1_modifier_mix.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v1_v2_v1_modifier_mix.sol @@ -55,3 +55,6 @@ contract X { // ==== // bytecodeFormat: legacy // ---- +// Warning 9511: (V1A:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. +// Warning 9511: (V1B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. +// Warning 9511: (C:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v2_v1_v1_modifier_sandwich.sol b/test/libsolidity/syntaxTests/abiEncoder/v2_v1_v1_modifier_sandwich.sol index 45fed616c0..9946c538c0 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v2_v1_v1_modifier_sandwich.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v2_v1_v1_modifier_sandwich.sol @@ -29,4 +29,6 @@ contract C is B { {} } // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. +// Warning 9511: (C:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 2428: (B:80-102): The type of return parameter 1, struct Data memory, is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/abiEncoder/v2_v1_v2_modifier_sandwich.sol b/test/libsolidity/syntaxTests/abiEncoder/v2_v1_v2_modifier_sandwich.sol index 5bc10f53ed..980a93248e 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/v2_v1_v2_modifier_sandwich.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/v2_v1_v2_modifier_sandwich.sol @@ -30,4 +30,5 @@ contract C is B { {} } // ---- +// Warning 9511: (B:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 2428: (B:80-102): The type of return parameter 1, struct Data memory, is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/array/calldata_multi_dynamic_V1.sol b/test/libsolidity/syntaxTests/array/calldata_multi_dynamic_V1.sol index 754270ecf4..54fbda5673 100644 --- a/test/libsolidity/syntaxTests/array/calldata_multi_dynamic_V1.sol +++ b/test/libsolidity/syntaxTests/array/calldata_multi_dynamic_V1.sol @@ -4,5 +4,6 @@ contract Test { function g(uint[][1] calldata) external { } } // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 4957: (51-68): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. // TypeError 4957: (98-116): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/constructor/nonabiv2_type.sol b/test/libsolidity/syntaxTests/constructor/nonabiv2_type.sol index 8e0c323b8c..580261afe0 100644 --- a/test/libsolidity/syntaxTests/constructor/nonabiv2_type.sol +++ b/test/libsolidity/syntaxTests/constructor/nonabiv2_type.sol @@ -3,4 +3,5 @@ contract C { constructor(uint[][][] memory t) {} } // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 4957: (46-65): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. Alternatively, make the contract abstract and supply the constructor arguments from a derived contract. diff --git a/test/libsolidity/syntaxTests/constructor/nonabiv2_type_abstract.sol b/test/libsolidity/syntaxTests/constructor/nonabiv2_type_abstract.sol index ca52859ff9..712322f1f4 100644 --- a/test/libsolidity/syntaxTests/constructor/nonabiv2_type_abstract.sol +++ b/test/libsolidity/syntaxTests/constructor/nonabiv2_type_abstract.sol @@ -5,3 +5,4 @@ abstract contract C { // ==== // bytecodeFormat: legacy // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. diff --git a/test/libsolidity/syntaxTests/controlFlow/modifiers/implemented_without_placeholder.sol b/test/libsolidity/syntaxTests/controlFlow/modifiers/implemented_without_placeholder.sol index 08b4ef12ef..a2a6bf661e 100644 --- a/test/libsolidity/syntaxTests/controlFlow/modifiers/implemented_without_placeholder.sol +++ b/test/libsolidity/syntaxTests/controlFlow/modifiers/implemented_without_placeholder.sol @@ -6,3 +6,4 @@ abstract contract A { } // ---- // SyntaxError 2883: (129-132): Modifier body does not contain '_'. +// Warning 8429: (106-132): Virtual modifiers are deprecated and scheduled for removal. diff --git a/test/libsolidity/syntaxTests/controlFlow/modifiers/modifier_different_functions.sol b/test/libsolidity/syntaxTests/controlFlow/modifiers/modifier_different_functions.sol index 63d9153692..81ff63e3d8 100644 --- a/test/libsolidity/syntaxTests/controlFlow/modifiers/modifier_different_functions.sol +++ b/test/libsolidity/syntaxTests/controlFlow/modifiers/modifier_different_functions.sol @@ -9,4 +9,5 @@ contract A { } } // ---- +// Warning 8429: (140-172): Virtual modifiers are deprecated and scheduled for removal. // TypeError 3464: (118-132): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/modifiers/modifier_override.sol b/test/libsolidity/syntaxTests/controlFlow/modifiers/modifier_override.sol index 3c5eb80ee4..7a9ea5e467 100644 --- a/test/libsolidity/syntaxTests/controlFlow/modifiers/modifier_override.sol +++ b/test/libsolidity/syntaxTests/controlFlow/modifiers/modifier_override.sol @@ -13,5 +13,6 @@ contract B is A { } } // ---- +// Warning 8429: (71-115): Virtual modifiers are deprecated and scheduled for removal. // Warning 5740: (65-69): Unreachable code. // TypeError 3464: (49-63): This variable is of storage pointer type and can be returned without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/modifiers/non_implemented_modifier.sol b/test/libsolidity/syntaxTests/controlFlow/modifiers/non_implemented_modifier.sol index 61aede05eb..670639cc95 100644 --- a/test/libsolidity/syntaxTests/controlFlow/modifiers/non_implemented_modifier.sol +++ b/test/libsolidity/syntaxTests/controlFlow/modifiers/non_implemented_modifier.sol @@ -4,3 +4,5 @@ abstract contract A { } modifier mod() virtual; } +// ---- +// Warning 8429: (106-129): Virtual modifiers are deprecated and scheduled for removal. diff --git a/test/libsolidity/syntaxTests/conversion/implicit_conversion_of_super_in_comparison.sol b/test/libsolidity/syntaxTests/conversion/implicit_conversion_of_super_in_comparison.sol index 708713b6b0..1d9e488ce4 100644 --- a/test/libsolidity/syntaxTests/conversion/implicit_conversion_of_super_in_comparison.sol +++ b/test/libsolidity/syntaxTests/conversion/implicit_conversion_of_super_in_comparison.sol @@ -21,7 +21,10 @@ contract C { // ---- // TypeError 2271: (144-157): Built-in binary operator != cannot be applied to types type(contract super C) and contract C. // TypeError 2271: (167-180): Built-in binary operator != cannot be applied to types contract C and type(contract super C). +// Warning 9170: (167-180): Comparison of variables of contract type is deprecated and scheduled for removal. Use an explicit cast to address type and compare the addresses instead. // TypeError 2271: (254-264): Built-in binary operator != cannot be applied to types type(contract super C) and contract C. // TypeError 2271: (274-284): Built-in binary operator != cannot be applied to types contract C and type(contract super C). +// Warning 9170: (274-284): Comparison of variables of contract type is deprecated and scheduled for removal. Use an explicit cast to address type and compare the addresses instead. // TypeError 2271: (349-359): Built-in binary operator != cannot be applied to types type(contract super C) and contract D. // TypeError 2271: (369-379): Built-in binary operator != cannot be applied to types contract D and type(contract super C). +// Warning 9170: (369-379): Comparison of variables of contract type is deprecated and scheduled for removal. Use an explicit cast to address type and compare the addresses instead. diff --git a/test/libsolidity/syntaxTests/errors/no_structs_in_abiv1.sol b/test/libsolidity/syntaxTests/errors/no_structs_in_abiv1.sol index 8727a28529..7c3ba51379 100644 --- a/test/libsolidity/syntaxTests/errors/no_structs_in_abiv1.sol +++ b/test/libsolidity/syntaxTests/errors/no_structs_in_abiv1.sol @@ -4,4 +4,5 @@ contract C { error MyError(S); } // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 3061: (70-71): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/events/event_nested_array.sol b/test/libsolidity/syntaxTests/events/event_nested_array.sol index 2491dae6e8..a518aa9364 100644 --- a/test/libsolidity/syntaxTests/events/event_nested_array.sol +++ b/test/libsolidity/syntaxTests/events/event_nested_array.sol @@ -3,4 +3,5 @@ contract c { event E(uint[][]); } // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 3061: (45-53): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/events/event_nested_array_in_struct.sol b/test/libsolidity/syntaxTests/events/event_nested_array_in_struct.sol index ee868a7152..dee2710214 100644 --- a/test/libsolidity/syntaxTests/events/event_nested_array_in_struct.sol +++ b/test/libsolidity/syntaxTests/events/event_nested_array_in_struct.sol @@ -4,4 +4,5 @@ contract c { event E(S); } // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 3061: (81-82): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/events/event_struct.sol b/test/libsolidity/syntaxTests/events/event_struct.sol index ac6355fe07..062fe4ba07 100644 --- a/test/libsolidity/syntaxTests/events/event_struct.sol +++ b/test/libsolidity/syntaxTests/events/event_struct.sol @@ -4,4 +4,5 @@ contract c { event E(S); } // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 3061: (71-72): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/events/event_struct_indexed.sol b/test/libsolidity/syntaxTests/events/event_struct_indexed.sol index 7e3393400c..5171bf0174 100644 --- a/test/libsolidity/syntaxTests/events/event_struct_indexed.sol +++ b/test/libsolidity/syntaxTests/events/event_struct_indexed.sol @@ -4,4 +4,5 @@ contract c { event E(S indexed); } // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 3061: (71-80): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/getter/nested_structs.sol b/test/libsolidity/syntaxTests/getter/nested_structs.sol index 36886bacff..ecde4773b3 100644 --- a/test/libsolidity/syntaxTests/getter/nested_structs.sol +++ b/test/libsolidity/syntaxTests/getter/nested_structs.sol @@ -9,4 +9,5 @@ contract C { mapping(uint256 => X) public m; } // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 2763: (108-138): The following types are only supported for getters in ABI coder v2: struct C.Y memory. Either remove "public" or use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_virtual_modifiers.sol b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_modifiers.sol index 31f510dccc..1250d86d2b 100644 --- a/test/libsolidity/syntaxTests/immutable/inheritance_virtual_modifiers.sol +++ b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_modifiers.sol @@ -17,3 +17,5 @@ contract C is B { _; f(x); } } +// ---- +// Warning 8429: (88-137): Virtual modifiers are deprecated and scheduled for removal. diff --git a/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_mismatch_1.sol b/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_mismatch_1.sol index 891f57e8d3..2b1b920c53 100644 --- a/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_mismatch_1.sol +++ b/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_mismatch_1.sol @@ -17,4 +17,5 @@ pragma abicoder v1; import "./B.sol"; contract C is B { } // ---- +// Warning 9511: (C.sol:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 6594: (C.sol:38-57): Contract "C" does not use ABI coder v2 but wants to inherit from a contract which uses types that require it. Use "pragma abicoder v2;" for the inheriting contract as well to enable the feature. diff --git a/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_mismatch_2.sol b/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_mismatch_2.sol index c92221f3ac..d4b6996e7d 100644 --- a/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_mismatch_2.sol +++ b/test/libsolidity/syntaxTests/imports/inheritance_abi_encoder_mismatch_2.sol @@ -16,4 +16,6 @@ pragma abicoder v1; import "./B.sol"; contract C is B { } // ---- +// Warning 9511: (B.sol:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. +// Warning 9511: (C.sol:0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 6594: (B.sol:38-57): Contract "B" does not use ABI coder v2 but wants to inherit from a contract which uses types that require it. Use "pragma abicoder v2;" for the inheriting contract as well to enable the feature. diff --git a/test/libsolidity/syntaxTests/inheritance/dataLocation/modifier_parameter_data_location_change_illegal_internal.sol b/test/libsolidity/syntaxTests/inheritance/dataLocation/modifier_parameter_data_location_change_illegal_internal.sol index a4856d9f03..bfceb04896 100644 --- a/test/libsolidity/syntaxTests/inheritance/dataLocation/modifier_parameter_data_location_change_illegal_internal.sol +++ b/test/libsolidity/syntaxTests/inheritance/dataLocation/modifier_parameter_data_location_change_illegal_internal.sol @@ -10,4 +10,5 @@ contract B is A { } } // ---- +// Warning 8429: (26-66): Virtual modifiers are deprecated and scheduled for removal. // TypeError 1078: (153-214): Override changes modifier signature. diff --git a/test/libsolidity/syntaxTests/inheritance/override/modifier_ambiguous.sol b/test/libsolidity/syntaxTests/inheritance/override/modifier_ambiguous.sol index 00aeed0cda..4f87fc779e 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/modifier_ambiguous.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/modifier_ambiguous.sol @@ -8,3 +8,5 @@ contract C is A, B { modifier f() override(A,B) { _; } } // ---- +// Warning 8429: (17-44): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (64-91): Virtual modifiers are deprecated and scheduled for removal. diff --git a/test/libsolidity/syntaxTests/inheritance/override/modifier_ambiguous_fail.sol b/test/libsolidity/syntaxTests/inheritance/override/modifier_ambiguous_fail.sol index 7332f0817c..fe663c6bfa 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/modifier_ambiguous_fail.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/modifier_ambiguous_fail.sol @@ -7,4 +7,6 @@ contract B { contract C is A, B { } // ---- +// Warning 8429: (17-44): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (64-91): Virtual modifiers are deprecated and scheduled for removal. // TypeError 6480: (94-116): Derived contract must override modifier "f". Two or more base classes define modifier with same name. diff --git a/test/libsolidity/syntaxTests/inheritance/override/modifier_inherited_different_signature.sol b/test/libsolidity/syntaxTests/inheritance/override/modifier_inherited_different_signature.sol index 428f38e62b..5c4356741c 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/modifier_inherited_different_signature.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/modifier_inherited_different_signature.sol @@ -7,4 +7,6 @@ contract B { contract C is A, B { } // ---- +// Warning 8429: (17-50): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (70-97): Virtual modifiers are deprecated and scheduled for removal. // TypeError 6480: (100-122): Derived contract must override modifier "f". Two or more base classes define modifier with same name. diff --git a/test/libsolidity/syntaxTests/inheritance/override/modifier_inherited_different_signature_override.sol b/test/libsolidity/syntaxTests/inheritance/override/modifier_inherited_different_signature_override.sol index 7791ea7250..9669a02d0a 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/modifier_inherited_different_signature_override.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/modifier_inherited_different_signature_override.sol @@ -8,4 +8,7 @@ contract C is A, B { modifier f() virtual override(A, B) { _; } } // ---- +// Warning 8429: (17-50): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (70-97): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (125-167): Virtual modifiers are deprecated and scheduled for removal. // TypeError 1078: (125-167): Override changes modifier signature. diff --git a/test/libsolidity/syntaxTests/inheritance/override/nonintermediate_common_base_and_unique_implementation_modifier.sol b/test/libsolidity/syntaxTests/inheritance/override/nonintermediate_common_base_and_unique_implementation_modifier.sol index deda228fa0..0c582f3c51 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/nonintermediate_common_base_and_unique_implementation_modifier.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/nonintermediate_common_base_and_unique_implementation_modifier.sol @@ -16,4 +16,7 @@ contract B is IJ } contract C is A, B {} // ---- +// Warning 8429: (14-41): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (58-85): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (111-154): Virtual modifiers are deprecated and scheduled for removal. // TypeError 6480: (229-250): Derived contract must override modifier "f". Two or more base classes define modifier with same name. diff --git a/test/libsolidity/syntaxTests/inheritance/virtual/modifier_virtual_err.sol b/test/libsolidity/syntaxTests/inheritance/virtual/modifier_virtual_err.sol index 85824c86b7..52625e72cb 100644 --- a/test/libsolidity/syntaxTests/inheritance/virtual/modifier_virtual_err.sol +++ b/test/libsolidity/syntaxTests/inheritance/virtual/modifier_virtual_err.sol @@ -4,4 +4,5 @@ library test { } } // ---- +// Warning 8429: (19-38): Virtual modifiers are deprecated and scheduled for removal. // TypeError 3275: (19-38): Modifiers in a library cannot be virtual. diff --git a/test/libsolidity/syntaxTests/inheritance/virtual/simple.sol b/test/libsolidity/syntaxTests/inheritance/virtual/simple.sol index 892118a13d..e74f5a3e0c 100644 --- a/test/libsolidity/syntaxTests/inheritance/virtual/simple.sol +++ b/test/libsolidity/syntaxTests/inheritance/virtual/simple.sol @@ -5,3 +5,4 @@ contract C modifier modi() virtual {_;} } // ---- +// Warning 8429: (83-111): Virtual modifiers are deprecated and scheduled for removal. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/memory_safe_dialect_string_and_comment.sol b/test/libsolidity/syntaxTests/inlineAssembly/memory_safe_dialect_string_and_comment.sol index 9d0fd3e1c7..12ccf94ffe 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/memory_safe_dialect_string_and_comment.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/memory_safe_dialect_string_and_comment.sol @@ -4,4 +4,5 @@ function f() pure { } } // ---- -// Warning 8544: (63-104): Inline assembly marked as memory safe using both a NatSpec tag and an assembly flag. If you are not concerned with backwards compatibility, only use the assembly flag, otherwise only use the NatSpec tag. +// Warning 8544: (63-104): Inline assembly marked as memory safe using both a NatSpec tag and an assembly block annotation. If you are not concerned with backwards compatibility, only use the assembly block annotation, otherwise only use the NatSpec tag. +// Warning 2424: (63-104): Natspec memory-safe-assembly special comment for inline assembly is deprecated and scheduled for removal. Use the memory-safe block annotation instead. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/natspec_multi.sol b/test/libsolidity/syntaxTests/inlineAssembly/natspec_multi.sol index 945d62b069..48b9ba79c5 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/natspec_multi.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/natspec_multi.sol @@ -15,9 +15,11 @@ function f() pure { // Warning 6269: (189-200): Unexpected NatSpec tag "after" with value "bogus-value" in inline assembly. // Warning 6269: (189-200): Unexpected NatSpec tag "before" with value "bogus-value" in inline assembly. // Warning 8787: (189-200): Unexpected value for @solidity tag in inline assembly: a +// Warning 2424: (189-200): Natspec memory-safe-assembly special comment for inline assembly is deprecated and scheduled for removal. Use the memory-safe block annotation instead. // Warning 8787: (189-200): Unexpected value for @solidity tag in inline assembly: b // Warning 8787: (189-200): Unexpected value for @solidity tag in inline assembly: c // Warning 8787: (189-200): Unexpected value for @solidity tag in inline assembly: d +// Warning 2424: (289-300): Natspec memory-safe-assembly special comment for inline assembly is deprecated and scheduled for removal. Use the memory-safe block annotation instead. // Warning 8787: (289-300): Unexpected value for @solidity tag in inline assembly: a // Warning 4377: (289-300): Value for @solidity tag in inline assembly specified multiple times: a // Warning 4377: (289-300): Value for @solidity tag in inline assembly specified multiple times: memory-safe-assembly diff --git a/test/libsolidity/syntaxTests/inlineAssembly/natspec_multi_swallowed.sol b/test/libsolidity/syntaxTests/inlineAssembly/natspec_multi_swallowed.sol index 74be1232dd..9d148b0b3f 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/natspec_multi_swallowed.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/natspec_multi_swallowed.sol @@ -14,6 +14,7 @@ function f() pure { // ---- // Warning 6269: (177-188): Unexpected NatSpec tag "after" with value "bogus-value" in inline assembly. // Warning 6269: (177-188): Unexpected NatSpec tag "before" with value "@solidity a memory-safe-assembly b c d" in inline assembly. +// Warning 2424: (277-288): Natspec memory-safe-assembly special comment for inline assembly is deprecated and scheduled for removal. Use the memory-safe block annotation instead. // Warning 8787: (277-288): Unexpected value for @solidity tag in inline assembly: a // Warning 4377: (277-288): Value for @solidity tag in inline assembly specified multiple times: a // Warning 4377: (277-288): Value for @solidity tag in inline assembly specified multiple times: memory-safe-assembly diff --git a/test/libsolidity/syntaxTests/modifiers/definition_in_contract.sol b/test/libsolidity/syntaxTests/modifiers/definition_in_contract.sol index 9b0a805adf..ff3b924c66 100644 --- a/test/libsolidity/syntaxTests/modifiers/definition_in_contract.sol +++ b/test/libsolidity/syntaxTests/modifiers/definition_in_contract.sol @@ -9,3 +9,6 @@ abstract contract A { modifier muv virtual; } // ---- +// Warning 8429: (39-65): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (117-143): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (148-169): Virtual modifiers are deprecated and scheduled for removal. diff --git a/test/libsolidity/syntaxTests/modifiers/definition_in_contract_unimplemented.sol b/test/libsolidity/syntaxTests/modifiers/definition_in_contract_unimplemented.sol index 2018ae4eab..8b60105aee 100644 --- a/test/libsolidity/syntaxTests/modifiers/definition_in_contract_unimplemented.sol +++ b/test/libsolidity/syntaxTests/modifiers/definition_in_contract_unimplemented.sol @@ -3,5 +3,6 @@ contract C { modifier muv virtual; } // ---- +// Warning 8429: (34-55): Virtual modifiers are deprecated and scheduled for removal. // TypeError 3656: (0-57): Contract "C" should be marked as abstract. // TypeError 8063: (17-29): Modifiers without implementation must be marked virtual. diff --git a/test/libsolidity/syntaxTests/modifiers/definition_in_interface.sol b/test/libsolidity/syntaxTests/modifiers/definition_in_interface.sol index 2f8281bdeb..56e5bb02f3 100644 --- a/test/libsolidity/syntaxTests/modifiers/definition_in_interface.sol +++ b/test/libsolidity/syntaxTests/modifiers/definition_in_interface.sol @@ -5,6 +5,8 @@ interface I { modifier muv virtual; } // ---- +// Warning 8429: (57-83): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (88-109): Virtual modifiers are deprecated and scheduled for removal. // TypeError 6408: (18-35): Modifiers cannot be defined or declared in interfaces. // TypeError 6408: (40-52): Modifiers cannot be defined or declared in interfaces. // TypeError 8063: (40-52): Modifiers without implementation must be marked virtual. diff --git a/test/libsolidity/syntaxTests/modifiers/definition_in_library.sol b/test/libsolidity/syntaxTests/modifiers/definition_in_library.sol index 6acbf3a0f6..34503d7278 100644 --- a/test/libsolidity/syntaxTests/modifiers/definition_in_library.sol +++ b/test/libsolidity/syntaxTests/modifiers/definition_in_library.sol @@ -2,4 +2,5 @@ library L { modifier mv virtual { _; } } // ---- +// Warning 8429: (16-42): Virtual modifiers are deprecated and scheduled for removal. // TypeError 3275: (16-42): Modifiers in a library cannot be virtual. diff --git a/test/libsolidity/syntaxTests/modifiers/definition_in_library_unimplemented.sol b/test/libsolidity/syntaxTests/modifiers/definition_in_library_unimplemented.sol index 5fbc356822..a239d86cef 100644 --- a/test/libsolidity/syntaxTests/modifiers/definition_in_library_unimplemented.sol +++ b/test/libsolidity/syntaxTests/modifiers/definition_in_library_unimplemented.sol @@ -3,5 +3,6 @@ library L { modifier muv virtual; } // ---- +// Warning 8429: (33-54): Virtual modifiers are deprecated and scheduled for removal. // TypeError 8063: (16-28): Modifiers without implementation must be marked virtual. // TypeError 3275: (33-54): Modifiers in a library cannot be virtual. diff --git a/test/libsolidity/syntaxTests/modifiers/empty_modifier_body.sol b/test/libsolidity/syntaxTests/modifiers/empty_modifier_body.sol index 0c0e821f56..d9abfbbde7 100644 --- a/test/libsolidity/syntaxTests/modifiers/empty_modifier_body.sol +++ b/test/libsolidity/syntaxTests/modifiers/empty_modifier_body.sol @@ -13,3 +13,5 @@ contract D is C { } } // ---- +// Warning 8429: (22-51): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (134-153): Virtual modifiers are deprecated and scheduled for removal. diff --git a/test/libsolidity/syntaxTests/modifiers/empty_modifier_err.sol b/test/libsolidity/syntaxTests/modifiers/empty_modifier_err.sol index e9e03f3042..bc466e471d 100644 --- a/test/libsolidity/syntaxTests/modifiers/empty_modifier_err.sol +++ b/test/libsolidity/syntaxTests/modifiers/empty_modifier_err.sol @@ -5,6 +5,8 @@ contract C is B { } abstract contract D {modifier m;} // ---- +// Warning 8429: (12-31): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (55-74): Virtual modifiers are deprecated and scheduled for removal. // TypeError 3656: (0-32): Contract "A" should be marked as abstract. // TypeError 3656: (76-95): Contract "C" should be marked as abstract. // TypeError 8063: (118-129): Modifiers without implementation must be marked virtual. diff --git a/test/libsolidity/syntaxTests/modifiers/legal_modifier_override.sol b/test/libsolidity/syntaxTests/modifiers/legal_modifier_override.sol index 62356abb13..2fff2d427f 100644 --- a/test/libsolidity/syntaxTests/modifiers/legal_modifier_override.sol +++ b/test/libsolidity/syntaxTests/modifiers/legal_modifier_override.sol @@ -1,3 +1,4 @@ contract A { modifier mod(uint a) virtual { _; } } contract B is A { modifier mod(uint a) override { _; } } // ---- +// Warning 8429: (13-48): Virtual modifiers are deprecated and scheduled for removal. diff --git a/test/libsolidity/syntaxTests/modifiers/modifier_abstract_override.sol b/test/libsolidity/syntaxTests/modifiers/modifier_abstract_override.sol index dd3ef4499f..e4c2a5cd2c 100644 --- a/test/libsolidity/syntaxTests/modifiers/modifier_abstract_override.sol +++ b/test/libsolidity/syntaxTests/modifiers/modifier_abstract_override.sol @@ -8,4 +8,6 @@ contract C is B { function f() m public {} } // ---- +// Warning 8429: (17-44): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (78-108): Virtual modifiers are deprecated and scheduled for removal. // TypeError 4593: (78-108): Overriding an implemented modifier with an unimplemented modifier is not allowed. diff --git a/test/libsolidity/syntaxTests/modifiers/multiple_inheritance_unimplemented_override.sol b/test/libsolidity/syntaxTests/modifiers/multiple_inheritance_unimplemented_override.sol index 81e1c3565b..f78b0b9fd2 100644 --- a/test/libsolidity/syntaxTests/modifiers/multiple_inheritance_unimplemented_override.sol +++ b/test/libsolidity/syntaxTests/modifiers/multiple_inheritance_unimplemented_override.sol @@ -9,3 +9,5 @@ contract C is A, B { function f() m public {} } // ---- +// Warning 8429: (17-44): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (73-94): Virtual modifiers are deprecated and scheduled for removal. diff --git a/test/libsolidity/syntaxTests/modifiers/unimplemented_function_and_modifier.sol b/test/libsolidity/syntaxTests/modifiers/unimplemented_function_and_modifier.sol index bf823748c8..702ac5ba70 100644 --- a/test/libsolidity/syntaxTests/modifiers/unimplemented_function_and_modifier.sol +++ b/test/libsolidity/syntaxTests/modifiers/unimplemented_function_and_modifier.sol @@ -26,6 +26,7 @@ contract E is A { modifier mod() override { _;} } // ---- +// Warning 8429: (110-133): Virtual modifiers are deprecated and scheduled for removal. // TypeError 3656: (137-254): Contract "B" should be marked as abstract. // TypeError 3656: (256-344): Contract "C" should be marked as abstract. // TypeError 3656: (346-466): Contract "D" should be marked as abstract. diff --git a/test/libsolidity/syntaxTests/modifiers/unimplemented_override_unimplemented.sol b/test/libsolidity/syntaxTests/modifiers/unimplemented_override_unimplemented.sol index ae041ebfc6..dbc6b0657c 100644 --- a/test/libsolidity/syntaxTests/modifiers/unimplemented_override_unimplemented.sol +++ b/test/libsolidity/syntaxTests/modifiers/unimplemented_override_unimplemented.sol @@ -9,3 +9,6 @@ abstract contract C is B { function f() m public {} } // ---- +// Warning 8429: (26-47): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (81-111): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (145-175): Virtual modifiers are deprecated and scheduled for removal. diff --git a/test/libsolidity/syntaxTests/modifiers/use_unimplemented_from_base.sol b/test/libsolidity/syntaxTests/modifiers/use_unimplemented_from_base.sol index 6d6423e7e1..77d06eca3f 100644 --- a/test/libsolidity/syntaxTests/modifiers/use_unimplemented_from_base.sol +++ b/test/libsolidity/syntaxTests/modifiers/use_unimplemented_from_base.sol @@ -6,3 +6,5 @@ contract B is A { modifier m() virtual override { _; } } // ---- +// Warning 8429: (26-47): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (101-137): Virtual modifiers are deprecated and scheduled for removal. diff --git a/test/libsolidity/syntaxTests/modifiers/use_unimplemented_on_overridden_func.sol b/test/libsolidity/syntaxTests/modifiers/use_unimplemented_on_overridden_func.sol index 5f6de4ce83..91aeb91f8d 100644 --- a/test/libsolidity/syntaxTests/modifiers/use_unimplemented_on_overridden_func.sol +++ b/test/libsolidity/syntaxTests/modifiers/use_unimplemented_on_overridden_func.sol @@ -6,3 +6,4 @@ abstract contract B is A { function f() public override {} } // ---- +// Warning 8429: (26-47): Virtual modifiers are deprecated and scheduled for removal. diff --git a/test/libsolidity/syntaxTests/modifiers/use_unimplemented_static.sol b/test/libsolidity/syntaxTests/modifiers/use_unimplemented_static.sol index a6fcd7eafa..26f4ebee6b 100644 --- a/test/libsolidity/syntaxTests/modifiers/use_unimplemented_static.sol +++ b/test/libsolidity/syntaxTests/modifiers/use_unimplemented_static.sol @@ -9,4 +9,6 @@ contract C is A, B { function f() B.m public {} } // ---- +// Warning 8429: (17-44): Virtual modifiers are deprecated and scheduled for removal. +// Warning 8429: (73-94): Virtual modifiers are deprecated and scheduled for removal. // TypeError 1835: (174-177): Cannot call unimplemented modifier. The modifier has no implementation in the referenced contract. Refer to it by its unqualified name if you want to call the implementation from the most derived contract. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/045_returning_multi_dimensional_arrays.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/045_returning_multi_dimensional_arrays.sol index c9075a4cd3..6490d00677 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/045_returning_multi_dimensional_arrays.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/045_returning_multi_dimensional_arrays.sol @@ -3,4 +3,5 @@ contract C { function f() public pure returns (string[][] memory) {} } // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 4957: (71-88): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/046_returning_multi_dimensional_static_arrays.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/046_returning_multi_dimensional_static_arrays.sol index ee8c7f18d3..17829bcdd5 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/046_returning_multi_dimensional_static_arrays.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/046_returning_multi_dimensional_static_arrays.sol @@ -3,4 +3,5 @@ contract C { function f() public pure returns (uint[][2] memory) {} } // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 4957: (71-87): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/048_returning_arrays_in_structs_arrays.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/048_returning_arrays_in_structs_arrays.sol index 77c77831e0..77a11a6d91 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/048_returning_arrays_in_structs_arrays.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/048_returning_arrays_in_structs_arrays.sol @@ -4,4 +4,5 @@ contract C { function f() public pure returns (S memory x) {} } // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 4957: (100-110): This type is only supported in ABI coder v2. Use "pragma abicoder v2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/346_unused_return_value_send.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/346_unused_return_value_send.sol index 80787c10c4..88f172cd7c 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/346_unused_return_value_send.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/346_unused_return_value_send.sol @@ -4,4 +4,5 @@ contract test { } } // ---- +// Warning 9207: (50-77): 'send' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. // Warning 5878: (50-80): Failure condition of 'send' ignored. Consider using 'transfer' instead. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/405_address_checksum_type_deduction.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/405_address_checksum_type_deduction.sol index 25acbfd42a..07f268f024 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/405_address_checksum_type_deduction.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/405_address_checksum_type_deduction.sol @@ -4,3 +4,4 @@ contract C { } } // ---- +// Warning 9207: (47-107): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/413_address_methods.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/413_address_methods.sol index ad57224cde..b8678efa6b 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/413_address_methods.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/413_address_methods.sol @@ -10,3 +10,5 @@ contract C { } } // ---- +// Warning 9207: (227-236): 'send' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. +// Warning 9207: (249-262): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/466_does_not_error_transfer_payable_fallback.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/466_does_not_error_transfer_payable_fallback.sol index 642ae83605..d897d8171d 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/466_does_not_error_transfer_payable_fallback.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/466_does_not_error_transfer_payable_fallback.sol @@ -13,3 +13,4 @@ contract B { } } // ---- +// Warning 9207: (227-246): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/535_address_overload_resolution.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/535_address_overload_resolution.sol index a36aeed863..6736329407 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/535_address_overload_resolution.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/535_address_overload_resolution.sol @@ -17,4 +17,5 @@ contract D { } } // ---- +// Warning 9207: (187-209): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. // Warning 2018: (17-134): Function state mutability can be restricted to view diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/585_abi_decode_with_unsupported_types.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/585_abi_decode_with_unsupported_types.sol index abbf76182f..d7127f6180 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/585_abi_decode_with_unsupported_types.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/585_abi_decode_with_unsupported_types.sol @@ -6,4 +6,5 @@ contract C { } } // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 9611: (118-119): Decoding type struct C.s memory not supported. diff --git a/test/libsolidity/syntaxTests/sizeLimits/bytecode_too_large_abiencoder_v1.sol b/test/libsolidity/syntaxTests/sizeLimits/bytecode_too_large_abiencoder_v1.sol index f3091ebf53..599267274a 100644 --- a/test/libsolidity/syntaxTests/sizeLimits/bytecode_too_large_abiencoder_v1.sol +++ b/test/libsolidity/syntaxTests/sizeLimits/bytecode_too_large_abiencoder_v1.sol @@ -10,4 +10,5 @@ contract test { // EVMVersion: >=shanghai // bytecodeFormat: legacy // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // Warning 5574: (21-27154): Contract code size is 27205 bytes and exceeds 24576 bytes (a limit introduced in Spurious Dragon). This contract may not be deployable on Mainnet. Consider enabling the optimizer (with a low "runs" value!), turning off revert strings, or using libraries. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abi_encodePacked_nested_dynamic_array.sol b/test/libsolidity/syntaxTests/specialFunctions/abi_encodePacked_nested_dynamic_array.sol index c10dc10f25..4b1f5905cb 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/abi_encodePacked_nested_dynamic_array.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/abi_encodePacked_nested_dynamic_array.sol @@ -5,4 +5,5 @@ contract C { } } // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 9578: (89-119): Type not supported in packed mode. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abi_encode_nested_dynamic_array.sol b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_nested_dynamic_array.sol index 8c679258c5..f46c468454 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/abi_encode_nested_dynamic_array.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_nested_dynamic_array.sol @@ -5,4 +5,5 @@ contract C { } } // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 2056: (86-116): This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol index a7d0e3dbe0..8b194ff6a2 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol @@ -12,6 +12,7 @@ contract C { } } // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 2056: (151-152): This type cannot be encoded. // TypeError 2056: (154-155): This type cannot be encoded. // TypeError 9578: (220-221): Type not supported in packed mode. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_nested_dynamic_array.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_nested_dynamic_array.sol index 1981b04bf3..256728a7e1 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_nested_dynamic_array.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_nested_dynamic_array.sol @@ -5,4 +5,5 @@ contract C { } } // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 9611: (92-101): Decoding type uint256[][3] memory not supported. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_struct.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_struct.sol index dd981b54c9..04ec0f78ba 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_struct.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_struct.sol @@ -9,4 +9,5 @@ contract C { } } // ---- +// Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // TypeError 9611: (118-119): Decoding type struct S memory not supported. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions.sol b/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions.sol index 8f27532138..4d54097d58 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions.sol @@ -21,4 +21,6 @@ contract C { // ==== // bytecodeFormat: legacy // ---- +// Warning 9207: (47-69): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. +// Warning 9207: (90-108): 'send' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. // Warning 5159: (122-134): "selfdestruct" has been deprecated. Note that, starting from the Cancun hard fork, the underlying opcode no longer deletes the code and data associated with an account and only transfers its Ether to the beneficiary, unless executed in the same transaction in which the contract was created (see EIP-6780). Any use in newly deployed contracts is strongly discouraged even if the new behavior is taken into account. Future changes to the EVM might further reduce the functionality of the opcode. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions_view_fail.sol b/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions_view_fail.sol index 4f9efcdb5d..347fd3fde1 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions_view_fail.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions_view_fail.sol @@ -20,6 +20,8 @@ contract C { } } // ---- +// Warning 9207: (52-74): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. +// Warning 9207: (132-150): 'send' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. // Warning 5159: (201-213): "selfdestruct" has been deprecated. Note that, starting from the Cancun hard fork, the underlying opcode no longer deletes the code and data associated with an account and only transfers its Ether to the beneficiary, unless executed in the same transaction in which the contract was created (see EIP-6780). Any use in newly deployed contracts is strongly discouraged even if the new behavior is taken into account. Future changes to the EVM might further reduce the functionality of the opcode. // TypeError 8961: (52-77): Function cannot be declared as view because this expression (potentially) modifies the state. // TypeError 8961: (132-153): Function cannot be declared as view because this expression (potentially) modifies the state. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/eof/builtin_functions.sol b/test/libsolidity/syntaxTests/viewPureChecker/eof/builtin_functions.sol index 91ea709fe2..a974e26e77 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/eof/builtin_functions.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/eof/builtin_functions.sol @@ -20,3 +20,5 @@ contract C { // ==== // bytecodeFormat: >=EOFv1 // ---- +// Warning 9207: (47-69): 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. +// Warning 9207: (90-108): 'send' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. From dbeb8762388e32df51585092c7905b7039b6bd8f Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Tue, 2 Sep 2025 16:51:44 -0300 Subject: [PATCH 084/243] Update docs --- docs/assembly.rst | 5 +- docs/cheatsheet.rst | 4 +- docs/common-patterns.rst | 12 +- docs/contracts/functions.rst | 6 +- docs/contracts/inheritance.rst | 9 +- docs/control-structures.rst | 15 +-- docs/examples/blind-auction.rst | 21 ++-- docs/examples/micropayment.rst | 15 ++- docs/examples/safe-remote.rst | 15 ++- docs/layout-of-source-files.rst | 8 +- docs/security-considerations.rst | 14 ++- docs/types/reference-types.rst | 6 +- docs/types/value-types.rst | 178 ++++++++++++++++------------ docs/units-and-global-variables.rst | 14 ++- 14 files changed, 198 insertions(+), 124 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index 3f00ab6a8a..0458e3196f 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -381,8 +381,9 @@ of Solidity, you can use a special comment to annotate an assembly block as memo ... } -Note that we will disallow the annotation via comment in a future breaking release; so, if you are not concerned with -backward-compatibility with older compiler versions, prefer using the dialect string. +.. warning:: + The ``memory-safe-assembly`` special comment is deprecated and scheduled for removal. + In new code targeting recent compilers, use the assembly block annotation. Advanced Safe Use of Memory --------------------------- diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index 79aa6e65d5..1a3085f129 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -53,8 +53,8 @@ Members of ``address`` - ``
.staticcall(bytes memory) returns (bool, bytes memory)``: issue low-level ``STATICCALL`` with the given payload, returns success condition and return data - ``
.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, - returns ``false`` on failure -- ``
.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure + returns ``false`` on failure (deprecated) +- ``
.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure (deprecated) .. index:: blockhash, blobhash, block, block;basefee, block;blobbasefee, block;chainid, block;coinbase, block;difficulty, block;gaslimit, block;number, block;prevrandao, block;timestamp .. index:: gasleft, msg;data, msg;sender, msg;sig, msg;value, tx;gasprice, tx;origin diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index cbc219b53b..4051f0f306 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -57,7 +57,8 @@ you receive the funds of the person who is now the richest. // Remember to zero the pending refund before // sending to prevent reentrancy attacks pendingWithdrawals[msg.sender] = 0; - payable(msg.sender).transfer(amount); + (bool success, ) = payable(msg.sender).call{value: amount}(""); + require(success); } } @@ -84,7 +85,8 @@ This is as opposed to the more intuitive sending pattern: function becomeRichest() public payable { if (msg.value <= mostSent) revert NotEnoughEther(); // This line can cause problems (explained below). - richest.transfer(msg.value); + (bool success, ) = richest.call{value: msg.value}(""); + require(success); richest = payable(msg.sender); mostSent = msg.value; } @@ -210,8 +212,10 @@ restrictions highly readable. revert NotEnoughEther(); _; - if (msg.value > amount) - payable(msg.sender).transfer(msg.value - amount); + if (msg.value > amount) { + (bool success, ) = payable(msg.sender).call{value: msg.value - amount}(""); + require(success); + } } function forceOwnerChange(address newOwner) diff --git a/docs/contracts/functions.rst b/docs/contracts/functions.rst index a3a2755388..163a891f12 100644 --- a/docs/contracts/functions.rst +++ b/docs/contracts/functions.rst @@ -310,6 +310,10 @@ will consume more gas than the 2300 gas stipend: - Calling an external function which consumes a large amount of gas - Sending Ether +.. warning:: + ``send()`` and ``transfer()`` are deprecated and scheduled for removal. + See the section on :ref:`send ` and :ref:`transfer ` for more information. + .. warning:: When Ether is sent directly to a contract (without a function call, i.e. sender uses ``send`` or ``transfer``) but the receiving contract does not define a receive Ether function or a payable fallback function, @@ -319,7 +323,6 @@ will consume more gas than the 2300 gas stipend: not recommended, since the fallback is invoked and would not fail for interface confusions on the part of the sender). - .. warning:: A contract without a receive Ether function can receive Ether as a recipient of a *coinbase transaction* (aka *miner block reward*) @@ -440,6 +443,7 @@ operations as long as there is enough gas passed on to it. // If someone sends Ether to that contract, // the transfer will fail, i.e. this returns false here. + // This will report a warning (deprecation) return testPayable.send(2 ether); } diff --git a/docs/contracts/inheritance.rst b/docs/contracts/inheritance.rst index 8a2d12afd8..778cd1c26e 100644 --- a/docs/contracts/inheritance.rst +++ b/docs/contracts/inheritance.rst @@ -377,8 +377,8 @@ of the variable: .. _modifier-overriding: -Modifier Overriding -=================== +Modifier Overriding (deprecated) +================================ Function modifiers can override each other. This works in the same way as :ref:`function overriding ` (except that there is no overloading for modifiers). The @@ -392,6 +392,7 @@ and the ``override`` keyword must be used in the overriding modifier: contract Base { + // This will report a warning (deprecation) modifier foo() virtual {_;} } @@ -411,11 +412,13 @@ explicitly: contract Base1 { + // This will report a warning (deprecation) modifier foo() virtual {_;} } contract Base2 { + // This will report a warning (deprecation) modifier foo() virtual {_;} } @@ -424,6 +427,8 @@ explicitly: modifier foo() override(Base1, Base2) {_;} } +.. warning:: + ``virtual`` modifiers are deprecated and scheduled for removal. .. index:: ! constructor diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 8a815e10ef..c567ddc527 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -692,16 +692,16 @@ and ``assert`` for internal error checking. :force: // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.5.0 <0.9.0; + pragma solidity >=0.6.2 <0.9.0; contract Sharer { function sendHalf(address payable addr) public payable returns (uint balance) { require(msg.value % 2 == 0, "Even value required."); uint balanceBeforeTransfer = address(this).balance; - addr.transfer(msg.value / 2); - // Since transfer throws an exception on failure and - // cannot call back here, there should be no way for us to - // still have half of the Ether. + (bool success, ) = addr.call{value: msg.value / 2}(""); + require(success); + // Since require will stop execution and revert if success is false, + // there should be no way for us to still have half of the Ether. assert(address(this).balance == balanceBeforeTransfer - msg.value / 2); return address(this).balance; } @@ -775,7 +775,8 @@ together with ``revert`` and the equivalent ``require``: if (msg.sender != owner) revert Unauthorized(); - payable(msg.sender).transfer(address(this).balance); + (bool success, ) = payable(msg.sender).call{value: address(this).balance}(""); + require(success); } } @@ -914,4 +915,4 @@ in scope in the block that follows. out-of-gas situation and not a deliberate error condition: The caller always retains at least 1/64th of the gas in a call and thus even if the called contract goes out of gas, the caller still - has some gas left. \ No newline at end of file + has some gas left. diff --git a/docs/examples/blind-auction.rst b/docs/examples/blind-auction.rst index 47e19033f9..e5e3184a86 100644 --- a/docs/examples/blind-auction.rst +++ b/docs/examples/blind-auction.rst @@ -118,13 +118,14 @@ to receive their Ether - contracts cannot activate themselves. if (amount > 0) { // It is important to set this to zero because the recipient // can call this function again as part of the receiving call - // before `send` returns. + // before `call` returns. pendingReturns[msg.sender] = 0; // msg.sender is not of type `address payable` and must be // explicitly converted using `payable(msg.sender)` in order - // use the member function `send()`. - if (!payable(msg.sender).send(amount)) { + // use the member function `call()`. + (bool success, ) = payable(msg.sender).call{value: amount}(""); + if (!success) { // No need to call throw here, just reset the amount owing pendingReturns[msg.sender] = amount; return false; @@ -160,7 +161,8 @@ to receive their Ether - contracts cannot activate themselves. emit AuctionEnded(highestBidder, highestBid); // 3. Interaction - beneficiary.transfer(highestBid); + (bool success, ) = beneficiary.call{value: highestBid}(""); + require(success); } } @@ -310,7 +312,8 @@ invalid bids. // the same deposit. bidToCheck.blindedBid = bytes32(0); } - payable(msg.sender).transfer(refund); + (bool success, ) = payable(msg.sender).call{value: refund}(""); + require(success); } /// Withdraw a bid that was overbid. @@ -319,11 +322,12 @@ invalid bids. if (amount > 0) { // It is important to set this to zero because the recipient // can call this function again as part of the receiving call - // before `transfer` returns (see the remark above about + // before `call` returns (see the remark above about // conditions -> effects -> interaction). pendingReturns[msg.sender] = 0; - payable(msg.sender).transfer(amount); + (bool success, ) = payable(msg.sender).call{value: amount}(""); + require(success); } } @@ -336,7 +340,8 @@ invalid bids. if (ended) revert AuctionEndAlreadyCalled(); emit AuctionEnded(highestBidder, highestBid); ended = true; - beneficiary.transfer(highestBid); + (bool success, ) = beneficiary.call{value: highestBid}(""); + require(success); } // This is an "internal" function which means that it diff --git a/docs/examples/micropayment.rst b/docs/examples/micropayment.rst index a7b26bb51b..373627c3d9 100644 --- a/docs/examples/micropayment.rst +++ b/docs/examples/micropayment.rst @@ -185,7 +185,8 @@ The full contract // this recreates the message that was signed on the client bytes32 message = prefixed(keccak256(abi.encodePacked(msg.sender, amount, nonce, this))); require(recoverSigner(message, signature) == owner); - payable(msg.sender).transfer(amount); + (bool success, ) = payable(msg.sender).call{value: amount}(""); + require(success); } /// freeze the contract and reclaim the leftover funds. @@ -195,7 +196,8 @@ The full contract { require(msg.sender == owner); freeze(); - payable(msg.sender).transfer(address(this).balance); + (bool success, ) = payable(msg.sender).call{value: address(this).balance}(""); + require(success); } /// signature methods. @@ -406,9 +408,11 @@ The full contract require(msg.sender == recipient); require(isValidSignature(amount, signature)); - recipient.transfer(amount); + (bool success, ) = recipient.call{value: amount}(""); + require(success); freeze(); - sender.transfer(address(this).balance); + (success, ) = sender.call{value: address(this).balance}(""); + require(success); } /// the sender can extend the expiration at any time @@ -430,7 +434,8 @@ The full contract { require(block.timestamp >= expiration); freeze(); - sender.transfer(address(this).balance); + (bool success, ) = sender.call{value: address(this).balance}(""); + require(success); } function isValidSignature(uint256 amount, bytes memory signature) diff --git a/docs/examples/safe-remote.rst b/docs/examples/safe-remote.rst index a2651af238..dd2a86b142 100644 --- a/docs/examples/safe-remote.rst +++ b/docs/examples/safe-remote.rst @@ -93,11 +93,12 @@ you can use state machine-like constructs inside a contract. { emit Aborted(); state = State.Inactive; - // We use transfer here directly. It is + // We use call here directly. It is // reentrancy-safe, because it is the // last call in this function and we // already changed the state. - seller.transfer(address(this).balance); + (bool success, ) = seller.call{value: address(this).balance}(""); + require(success); } /// Confirm the purchase as buyer. @@ -124,11 +125,12 @@ you can use state machine-like constructs inside a contract. { emit ItemReceived(); // It is important to change the state first because - // otherwise, the contracts called using `send` below + // otherwise, the contracts called using `call` below // can call in again here. state = State.Release; - buyer.transfer(value); + (bool success, ) = buyer.call{value: value}(""); + require(success); } /// This function refunds the seller, i.e. @@ -140,10 +142,11 @@ you can use state machine-like constructs inside a contract. { emit SellerRefunded(); // It is important to change the state first because - // otherwise, the contracts called using `send` below + // otherwise, the contracts called using `call` below // can call in again here. state = State.Inactive; - seller.transfer(3 * value); + (bool success, ) = seller.call{value: 3 * value}(""); + require(success); } } diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index e2c7a859a8..94ef435259 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -103,10 +103,14 @@ select between the two implementations of the ABI encoder and decoder. The new ABI coder (v2) is able to encode and decode arbitrarily nested arrays and structs. Apart from supporting more types, it involves more extensive validation and safety checks, which may result in higher gas costs, but also heightened -security. It is considered -non-experimental as of Solidity 0.6.0 and it is enabled by default starting +security. +It is considered non-experimental as of Solidity 0.6.0 and it is enabled by default starting with Solidity 0.8.0. The old ABI coder can still be selected using ``pragma abicoder v1;``. +.. warning:: + The ABI coder v1 is deprecated and scheduled for removal. + Use ABI coder v2 instead. + The set of types supported by the new encoder is a strict superset of the ones supported by the old one. Contracts that use it can interact with ones that do not without limitations. The reverse is possible only as long as the diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index eb42993e67..af0e4c318c 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -65,6 +65,7 @@ To give an example, the following code contains a bug (it is just a snippet and mapping(address => uint) shares; /// Withdraw your share. function withdraw() public { + // This will report a warning (deprecation) if (payable(msg.sender).send(shares[msg.sender])) shares[msg.sender] = 0; } @@ -76,7 +77,7 @@ Ether transfer can always include code execution, so the recipient could be a contract that calls back into ``withdraw``. This would let it get multiple refunds and, basically, retrieve all the Ether in the contract. In particular, the following contract will allow an attacker to refund multiple times -as it uses ``call`` which forwards all remaining gas by default: +as it uses ``call`` which does not limit the amount of gas that is forwarded by default: .. code-block:: solidity @@ -100,7 +101,7 @@ To avoid reentrancy, you can use the Checks-Effects-Interactions pattern as demo .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.9.0; + pragma solidity >=0.6.2 <0.9.0; contract Fund { /// @dev Mapping of ether shares of the contract. @@ -109,7 +110,8 @@ To avoid reentrancy, you can use the Checks-Effects-Interactions pattern as demo function withdraw() public { uint share = shares[msg.sender]; shares[msg.sender] = 0; - payable(msg.sender).transfer(share); + (bool success, ) = payable(msg.sender).call{value: share}(""); + require(success); } } @@ -158,8 +160,9 @@ Sending and Receiving Ether (for example in the "details" section in Remix). - There is a way to forward more gas to the receiving contract using ``addr.call{value: x}("")``. - This is essentially the same as ``addr.transfer(x)``, only that it forwards all remaining gas - and opens up the ability for the recipient to perform more expensive actions + This is essentially the same as ``addr.transfer(x)``, only that it forwards all remaining gas, + subject to additional limits imposed by some EVM versions (such as the `63/64th rule `_ + introduced by ``tangerineWhistle``), and opens up the ability for the recipient to perform more expensive actions (and it returns a failure code instead of automatically propagating the error). This might include calling back into the sending contract or other state changes you might not have thought of. So it allows for great flexibility for honest users but also for malicious actors. @@ -255,6 +258,7 @@ Let's say you have a wallet contract like this: function transferTo(address payable dest, uint amount) public { // THE BUG IS RIGHT HERE, you must use msg.sender instead of tx.origin require(tx.origin == owner); + // This will report a warning (deprecation) dest.transfer(amount); } } diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index 79ba8da6e5..b07a54f45f 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -680,7 +680,7 @@ shown in the following example: .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 - pragma solidity >=0.6.0 <0.9.0; + pragma solidity >=0.6.2 <0.9.0; // Defines a new type with two fields. // Declaring a struct outside of a contract allows @@ -729,8 +729,8 @@ shown in the following example: return false; uint amount = c.amount; c.amount = 0; - c.beneficiary.transfer(amount); - return true; + (bool success, ) = c.beneficiary.call{value: amount}(""); + return success; } } diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index 57fa7b3dab..035857d7ac 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -242,118 +242,148 @@ Members of Addresses For a quick reference of all members of address, see :ref:`address_related`. +.. _balance-transfer-address-members: + * ``balance`` and ``transfer`` -It is possible to query the balance of an address using the property ``balance`` -and to send Ether (in units of wei) to a payable address using the ``transfer`` function: + It is possible to query the balance of an address using the property ``balance`` + and to send Ether (in units of wei) to a payable address using the ``transfer`` function: -.. code-block:: solidity - :force: + .. code-block:: solidity + :force: - address payable x = payable(0x123); - address myAddress = address(this); - if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10); + address payable x = payable(0x123); + address myAddress = address(this); + if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10); -The ``transfer`` function fails if the balance of the current contract is not large enough -or if the Ether transfer is rejected by the receiving account. The ``transfer`` function -reverts on failure. + The ``transfer`` function fails if the balance of the current contract is not large enough + or if the Ether transfer is rejected by the receiving account. The ``transfer`` function + reverts on failure. -.. note:: - If ``x`` is a contract address, its code (more specifically: its :ref:`receive-ether-function`, if present, or otherwise its :ref:`fallback-function`, if present) will be executed together with the ``transfer`` call (this is a feature of the EVM and cannot be prevented). If that execution runs out of gas or fails in any way, the Ether transfer will be reverted and the current contract will stop with an exception. + .. note:: + If ``x`` is a contract address, its code (more specifically: its :ref:`receive-ether-function`, if present, or otherwise its :ref:`fallback-function`, if present) will be executed together with the ``transfer`` call (this is a feature of the EVM and cannot be prevented). If that execution runs out of gas or fails in any way, the Ether transfer will be reverted and the current contract will stop with an exception. + + .. warning:: + ``transfer`` is deprecated and scheduled for removal. + Simple ether transfers can still be performed using the :ref:`call function ` + with with an optionally provided maximum amount of gas and empty payload, i.e., ``call{value: }("")``. + By default this forwards all the remaining gas, subject to additional limits imposed by some EVM versions + (such as the `63/64th rule `_ introduced by ``tangerineWhistle``). + As with any external call, the ``gas`` call option can be used to set a lower limit. + + While it is possible to recreate the functionality by explicitly setting the limit to the value of the stipend (2300 gas), + this value no longer holds its original meaning due to changing opcode costs. + It is recommended to use different means to protect against reentrancy. + +.. _send-address-member: * ``send`` -``send`` is the low-level counterpart of ``transfer``. If the execution fails, the current contract will not stop with an exception, but ``send`` will return ``false``. + ``send`` is the low-level counterpart of ``transfer``. If the execution fails, the current contract will not stop with an exception, but ``send`` will return ``false``. -.. warning:: - There are some dangers in using ``send``: The transfer fails if the call stack depth is at 1024 - (this can always be forced by the caller) and it also fails if the recipient runs out of gas. So in order - to make safe Ether transfers, always check the return value of ``send``, use ``transfer`` or even better: - use a pattern where the recipient withdraws the Ether. + .. warning:: + There are some dangers in using ``send``: The transfer fails if the call stack depth is at 1024 + (this can always be forced by the caller) and it also fails if the recipient runs out of gas. So in order + to make safe Ether transfers, always check the return value of ``send``, use ``transfer`` or even better: + use a pattern where the recipient withdraws the Ether. + + .. warning:: + ``send`` is deprecated and scheduled for removal. + Simple ether transfers can still be performed using the :ref:`call function ` + with with an optionally provided maximum amount of gas and empty payload, i.e., ``call{value: }("")``. + By default this forwards all the remaining gas, subject to additional limits imposed by some EVM versions + (such as the `63/64th rule `_ introduced by ``tangerineWhistle``). + As with any external call, the ``gas`` call option can be used to set a lower limit. + + While it is possible to recreate the functionality by explicitly setting the limit to the value of the stipend (2300 gas), + this value no longer holds its original meaning due to changing opcode costs. + It is recommended to use different means to protect against reentrancy. + +.. _address_call_functions: * ``call``, ``delegatecall`` and ``staticcall`` -In order to interface with contracts that do not adhere to the ABI, -or to get more direct control over the encoding, -the functions ``call``, ``delegatecall`` and ``staticcall`` are provided. -They all take a single ``bytes memory`` parameter and -return the success condition (as a ``bool``) and the returned data -(``bytes memory``). -The functions ``abi.encode``, ``abi.encodePacked``, ``abi.encodeWithSelector`` -and ``abi.encodeWithSignature`` can be used to encode structured data. + In order to interface with contracts that do not adhere to the ABI, + or to get more direct control over the encoding, + the functions ``call``, ``delegatecall`` and ``staticcall`` are provided. + They all take a single ``bytes memory`` parameter and + return the success condition (as a ``bool``) and the returned data + (``bytes memory``). + The functions ``abi.encode``, ``abi.encodePacked``, ``abi.encodeWithSelector`` + and ``abi.encodeWithSignature`` can be used to encode structured data. -Example: + Example: -.. code-block:: solidity + .. code-block:: solidity - bytes memory payload = abi.encodeWithSignature("register(string)", "MyName"); - (bool success, bytes memory returnData) = address(nameReg).call(payload); - require(success); + bytes memory payload = abi.encodeWithSignature("register(string)", "MyName"); + (bool success, bytes memory returnData) = address(nameReg).call(payload); + require(success); -.. warning:: - All these functions are low-level functions and should be used with care. - Specifically, any unknown contract might be malicious and if you call it, you - hand over control to that contract which could in turn call back into - your contract, so be prepared for changes to your state variables - when the call returns. The regular way to interact with other contracts - is to call a function on a contract object (``x.f()``). + .. warning:: + All these functions are low-level functions and should be used with care. + Specifically, any unknown contract might be malicious and if you call it, you + hand over control to that contract which could in turn call back into + your contract, so be prepared for changes to your state variables + when the call returns. The regular way to interact with other contracts + is to call a function on a contract object (``x.f()``). -.. note:: - Previous versions of Solidity allowed these functions to receive - arbitrary arguments and would also handle a first argument of type - ``bytes4`` differently. These edge cases were removed in version 0.5.0. + .. note:: + Previous versions of Solidity allowed these functions to receive + arbitrary arguments and would also handle a first argument of type + ``bytes4`` differently. These edge cases were removed in version 0.5.0. -It is possible to adjust the supplied gas with the ``gas`` modifier: + It is possible to adjust the supplied gas with the ``gas`` modifier: -.. code-block:: solidity + .. code-block:: solidity - address(nameReg).call{gas: 1000000}(abi.encodeWithSignature("register(string)", "MyName")); + address(nameReg).call{gas: 1000000}(abi.encodeWithSignature("register(string)", "MyName")); -Similarly, the supplied Ether value can be controlled too: + Similarly, the supplied Ether value can be controlled too: -.. code-block:: solidity + .. code-block:: solidity - address(nameReg).call{value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName")); + address(nameReg).call{value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName")); -Lastly, these modifiers can be combined. Their order does not matter: + Lastly, these modifiers can be combined. Their order does not matter: -.. code-block:: solidity + .. code-block:: solidity - address(nameReg).call{gas: 1000000, value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName")); + address(nameReg).call{gas: 1000000, value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName")); -In a similar way, the function ``delegatecall`` can be used: the difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of ``delegatecall`` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used. + In a similar way, the function ``delegatecall`` can be used: the difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of ``delegatecall`` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used. -.. note:: - Prior to homestead, only a limited variant called ``callcode`` was available that did not provide access to the original ``msg.sender`` and ``msg.value`` values. This function was removed in version 0.5.0. + .. note:: + Prior to homestead, only a limited variant called ``callcode`` was available that did not provide access to the original ``msg.sender`` and ``msg.value`` values. This function was removed in version 0.5.0. -Since byzantium ``staticcall`` can be used as well. This is basically the same as ``call``, but will revert if the called function modifies the state in any way. + Since byzantium ``staticcall`` can be used as well. This is basically the same as ``call``, but will revert if the called function modifies the state in any way. -All three functions ``call``, ``delegatecall`` and ``staticcall`` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity. + All three functions ``call``, ``delegatecall`` and ``staticcall`` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity. -The ``gas`` option is available on all three methods, while the ``value`` option is only available -on ``call``. + The ``gas`` option is available on all three methods, while the ``value`` option is only available + on ``call``. -.. note:: - It is best to avoid relying on hardcoded gas values in your smart contract code, - regardless of whether state is read from or written to, as this can have many pitfalls. - Also, access to gas might change in the future. + .. note:: + It is best to avoid relying on hardcoded gas values in your smart contract code, + regardless of whether state is read from or written to, as this can have many pitfalls. + Also, access to gas might change in the future. * ``code`` and ``codehash`` -You can query the deployed code for any smart contract. Use ``.code`` to get the EVM bytecode as a -``bytes memory``, which might be empty. Use ``.codehash`` to get the Keccak-256 hash of that code -(as a ``bytes32``). Note that ``addr.codehash`` is cheaper than using ``keccak256(addr.code)``. + You can query the deployed code for any smart contract. Use ``.code`` to get the EVM bytecode as a + ``bytes memory``, which might be empty. Use ``.codehash`` to get the Keccak-256 hash of that code + (as a ``bytes32``). Note that ``addr.codehash`` is cheaper than using ``keccak256(addr.code)``. -.. warning:: - The output of ``addr.codehash`` may be ``0`` if the account associated with ``addr`` is empty or non-existent - (i.e., it has no code, zero balance, and zero nonce as defined by `EIP-161 `_). - If the account has no code but a non-zero balance or nonce, then ``addr.codehash`` will output the Keccak-256 hash of empty data - (i.e., ``keccak256("")`` which is equal to ``c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470``), as defined by - `EIP-1052 `_. + .. warning:: + The output of ``addr.codehash`` may be ``0`` if the account associated with ``addr`` is empty or non-existent + (i.e., it has no code, zero balance, and zero nonce as defined by `EIP-161 `_). + If the account has no code but a non-zero balance or nonce, then ``addr.codehash`` will output the Keccak-256 hash of empty data + (i.e., ``keccak256("")`` which is equal to ``c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470``), as defined by + `EIP-1052 `_. -.. note:: - All contracts can be converted to ``address`` type, so it is possible to query the balance of the - current contract using ``address(this).balance``. + .. note:: + All contracts can be converted to ``address`` type, so it is possible to query the balance of the + current contract using ``address(this).balance``. .. index:: ! contract type, ! type; contract diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index fce4cf96d9..4d8fed283f 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -252,6 +252,7 @@ Mathematical and Cryptographic Functions Members of Address Types ------------------------ +These members are explained in more detail in the section on :ref:`members of address `. ``
.balance`` (``uint256``) balance of the :ref:`address` in Wei @@ -268,14 +269,21 @@ Members of Address Types ``
.send(uint256 amount) returns (bool)`` send given amount of Wei to :ref:`address`, returns ``false`` on failure, forwards 2300 gas stipend, not adjustable +.. warning:: + ``send()`` and ``transfer()`` are deprecated and scheduled for removal. + See the section on :ref:`send ` and :ref:`transfer ` for more information. + ``
.call(bytes memory) returns (bool, bytes memory)`` - issue low-level ``CALL`` with the given payload, returns success condition and return data, forwards all available gas, adjustable + issue low-level ``CALL`` with the given payload, returns success condition and return data, + forwards all available gas (subject to additional limits imposed by some EVM versions), adjustable ``
.delegatecall(bytes memory) returns (bool, bytes memory)`` - issue low-level ``DELEGATECALL`` with the given payload, returns success condition and return data, forwards all available gas, adjustable + issue low-level ``DELEGATECALL`` with the given payload, returns success condition and return data, + forwards all available gas (subject to additional limits imposed by some EVM versions), adjustable ``
.staticcall(bytes memory) returns (bool, bytes memory)`` - issue low-level ``STATICCALL`` with the given payload, returns success condition and return data, forwards all available gas, adjustable + issue low-level ``STATICCALL`` with the given payload, returns success condition and return data, + forwards all available gas (subject to additional limits imposed by some EVM versions), adjustable For more information, see the section on :ref:`address`. From b60f11076417e5540d3f82a5d2bec4b67f124b42 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Fri, 22 Aug 2025 15:38:12 -0300 Subject: [PATCH 085/243] zeppelin ext test workaround --- test/externalTests/zeppelin.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/externalTests/zeppelin.sh b/test/externalTests/zeppelin.sh index 99df125bba..aca8311ac9 100755 --- a/test/externalTests/zeppelin.sh +++ b/test/externalTests/zeppelin.sh @@ -108,6 +108,25 @@ function zeppelin_test # Fails under evmVersion=osaka, likely due to transaction gas limits introduced by EIP-7825 or by the modexp upper bound EIP-7823. sed -i "s|describe(\('RSA'\)|describe.skip(\1|g" test/utils/cryptography/RSA.test.js + cat <<-EOF >> "$config_file" + const { TASK_COMPILE_SOLIDITY_COMPILE } = require("hardhat/builtin-tasks/task-names"); + + task(TASK_COMPILE_SOLIDITY_COMPILE) + .setAction(async (args, hre, runSuper) => { + const result = await runSuper(args); + + result.output.errors = (result.output.errors || []).filter(err => { + if (err.severity === "warning" && err.message.includes("deprecated")) { + return false; // suppress this warning + } + return true; + }); + + return result; + }); + +EOF + neutralize_package_json_hooks force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH" force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" From 809c719ff5fbfd156b72a69eaf136f3fe39e16a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 16 Oct 2025 18:55:14 +0200 Subject: [PATCH 086/243] Exclude docs of dependencies from the source tarball --- scripts/create_source_tarball.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/create_source_tarball.sh b/scripts/create_source_tarball.sh index 9ccbec2223..dd21612f9d 100755 --- a/scripts/create_source_tarball.sh +++ b/scripts/create_source_tarball.sh @@ -43,6 +43,11 @@ git checkout-index --all --prefix="$SOLDIR" # shellcheck disable=SC2016 SOLDIR="$SOLDIR" git submodule foreach 'git checkout-index --all --prefix="${SOLDIR}/${sm_path}/"' +# Documentation is pretty heavy and not necessary to build the compiler. +# Especially nlohmann-json has several huge images, which blow up the size of the compressed tarball. +# shellcheck disable=SC2016 +SOLDIR="$SOLDIR" git submodule foreach 'rm -rf "${SOLDIR}/${sm_path}/"doc/; rm -rf "${SOLDIR}/${sm_path}/"docs/' + # Include the commit hash and prerelease suffix in the tarball echo "$commit_hash" > "${SOLDIR}/commit_hash.txt" [[ -e prerelease.txt ]] && cp prerelease.txt "${SOLDIR}/" From 11083858f8b51f3774dbd8296eab5dfd35c79b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 3 Dec 2025 02:50:28 +0100 Subject: [PATCH 087/243] ReleaseChecklist: Update the requirements for docker releases --- ReleaseChecklist.md | 3 ++- scripts/docker_deploy_manual.sh | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 4eada5f65e..0449dce26c 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -3,7 +3,8 @@ ## Requirements - [ ] GitHub account with access to [solidity](https://github.com/argotorg/solidity), [solc-js](https://github.com/argotorg/solc-js), [solc-bin](https://github.com/argotorg/solc-bin), [solidity-website](https://github.com/argotorg/solidity-website). -- [ ] DockerHub account with push rights to the [`solc` image](https://hub.docker.com/r/ethereum/solc). +- [ ] Personal Access Token (PAT) with `write:packages` scope to access Github's container registry. + You can generate one by visiting https://github.com/settings/tokens/new?scopes=write:packages. - [ ] Ubuntu/Debian dependencies of the Docker script: `docker-buildx`. - [ ] [npm Registry](https://www.npmjs.com) account added as a collaborator for the [`solc` package](https://www.npmjs.com/package/solc). - [ ] Access to the [solidity_lang Twitter account](https://twitter.com/solidity_lang). diff --git a/scripts/docker_deploy_manual.sh b/scripts/docker_deploy_manual.sh index c17119ce08..715147765c 100755 --- a/scripts/docker_deploy_manual.sh +++ b/scripts/docker_deploy_manual.sh @@ -25,7 +25,7 @@ fi # NOTE: Login to GHCR before running this script with a PAT: # echo $GHCR_TOKEN | docker login ghcr.io -u USERNAME --password-stdin # -# Create a classic PAT with write:packages scope only visiting the following url: +# Create a classic PAT with write:packages scope by visiting the following URL: # https://github.com/settings/tokens/new?scopes=write:packages DIR=$(mktemp -d) From 032d0bfecd105a4ef0f6109cf1b6e88342850f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 3 Dec 2025 02:51:26 +0100 Subject: [PATCH 088/243] ReleaseChecklist: Remove checkboxes from requirements - This is not something we go through during a release so we never check them anyway. --- ReleaseChecklist.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 0449dce26c..b78edc2de2 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -1,14 +1,14 @@ # Checklist for making a Solidity release ## Requirements -- [ ] GitHub account with access to [solidity](https://github.com/argotorg/solidity), [solc-js](https://github.com/argotorg/solc-js), +- GitHub account with access to [solidity](https://github.com/argotorg/solidity), [solc-js](https://github.com/argotorg/solc-js), [solc-bin](https://github.com/argotorg/solc-bin), [solidity-website](https://github.com/argotorg/solidity-website). -- [ ] Personal Access Token (PAT) with `write:packages` scope to access Github's container registry. +- Personal Access Token (PAT) with `write:packages` scope to access Github's container registry. You can generate one by visiting https://github.com/settings/tokens/new?scopes=write:packages. -- [ ] Ubuntu/Debian dependencies of the Docker script: `docker-buildx`. -- [ ] [npm Registry](https://www.npmjs.com) account added as a collaborator for the [`solc` package](https://www.npmjs.com/package/solc). -- [ ] Access to the [solidity_lang Twitter account](https://twitter.com/solidity_lang). -- [ ] [Reddit](https://www.reddit.com) account that is at least 10 days old with a minimum of 20 comment karma (`/r/ethereum` requirements). +- Ubuntu/Debian dependencies of the Docker script: `docker-buildx`. +- [npm Registry](https://www.npmjs.com) account added as a collaborator for the [`solc` package](https://www.npmjs.com/package/solc). +- Access to the [solidity_lang Twitter account](https://twitter.com/solidity_lang). +- [Reddit](https://www.reddit.com) account that is at least 10 days old with a minimum of 20 comment karma (`/r/ethereum` requirements). ## Full release From 28ca8e62a0673a5f6ee36065e3dca7d01c80c6fb Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Sat, 15 Mar 2025 12:09:55 -0300 Subject: [PATCH 089/243] Update and add tests --- .../storageLayoutSpecifier/abi_decode.sol | 2 +- .../storageLayoutSpecifier/address.sol | 2 +- .../storageLayoutSpecifier/address_constant.sol | 6 ++++++ .../storageLayoutSpecifier/array_literal.sol | 2 +- .../bitwise_negation_after_cast.sol | 2 +- .../storageLayoutSpecifier/bool_constant.sol | 4 ++++ .../storageLayoutSpecifier/boolean.sol | 2 +- .../storageLayoutSpecifier/bytes_constant.sol | 6 ++++++ .../constant_divided_by_its_negation.sol | 2 +- .../constant_divided_by_zero.sol | 2 +- .../constant_from_base_contract.sol | 2 +- .../constant_from_module.sol | 8 ++++++++ .../constant_initialized_from_builtin.sol | 7 +++++++ .../constant_initialized_from_cast.sol | 4 ++++ ...constant_initialized_from_other_constant.sol | 4 ++++ ...tant_initialized_with_literal_expression.sol | 3 +++ ...zed_with_unlimited_arithmetic_expression.sol | 7 +++++++ .../syntaxTests/storageLayoutSpecifier/enum.sol | 2 +- .../function_defined_in_other_contract.sol | 2 +- .../function_selector.sol | 2 +- .../storageLayoutSpecifier/hex_address.sol | 2 +- .../storageLayoutSpecifier/hex_string.sol | 2 +- .../storageLayoutSpecifier/hex_string_cast.sol | 2 +- .../storageLayoutSpecifier/immutables.sol | 17 +++++++++++++++++ .../storageLayoutSpecifier/int_constant.sol | 7 +++++++ .../layout_specification_bytes.sol | 2 +- ...out_specification_constant_in_expression.sol | 1 - .../layout_specified_by_interface_id.sol | 2 +- .../layout_specified_by_module.sol | 2 +- .../layout_specified_by_other_contract.sol | 2 +- .../layout_specified_by_type.sol | 2 +- .../storageLayoutSpecifier/literal_cast.sol | 2 +- .../storageLayoutSpecifier/precompiles.sol | 4 ++-- ...rational_number_without_fractional_part.sol} | 1 + .../storageLayoutSpecifier/string.sol | 2 +- .../storageLayoutSpecifier/ternary_operator.sol | 4 ++-- .../storageLayoutSpecifier/tuple.sol | 2 +- .../storageLayoutSpecifier/type_uint_max.sol | 2 +- .../user_defined_value_type.sol | 2 +- .../user_defined_value_type_unwrap.sol | 2 +- .../user_defined_value_type_wrap.sol | 2 +- .../value_from_array_literal.sol | 4 ++-- .../storageLayoutSpecifier/value_from_bytes.sol | 2 +- 43 files changed, 107 insertions(+), 34 deletions(-) create mode 100644 test/libsolidity/syntaxTests/storageLayoutSpecifier/address_constant.sol create mode 100644 test/libsolidity/syntaxTests/storageLayoutSpecifier/bool_constant.sol create mode 100644 test/libsolidity/syntaxTests/storageLayoutSpecifier/bytes_constant.sol create mode 100644 test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_from_module.sol create mode 100644 test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_from_builtin.sol create mode 100644 test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_from_cast.sol create mode 100644 test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_from_other_constant.sol create mode 100644 test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_with_literal_expression.sol create mode 100644 test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_with_unlimited_arithmetic_expression.sol create mode 100644 test/libsolidity/syntaxTests/storageLayoutSpecifier/immutables.sol create mode 100644 test/libsolidity/syntaxTests/storageLayoutSpecifier/int_constant.sol rename test/libsolidity/syntaxTests/storageLayoutSpecifier/{rational_number_zero_fractional_part.sol => rational_number_without_fractional_part.sol} (70%) diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/abi_decode.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/abi_decode.sol index 920e8d252c..498e39abf0 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/abi_decode.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/abi_decode.sol @@ -1,3 +1,3 @@ contract C layout at abi.decode(abi.encode(42), (uint)) {} // ---- -// TypeError 6396: (21-55): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1505: (21-55): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/address.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/address.sol index fded26fadd..b351ab0094 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/address.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/address.sol @@ -1,3 +1,3 @@ contract C layout at address(0x1234) {} // ---- -// TypeError 6396: (21-36): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1763: (21-36): The base slot of the storage layout must evaluate to an integer (the type is 'address' instead). diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/address_constant.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/address_constant.sol new file mode 100644 index 0000000000..3eda177fc3 --- /dev/null +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/address_constant.sol @@ -0,0 +1,6 @@ +address constant x = 0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF; +contract C layout at x { + +} +// ---- +// TypeError 1763: (86-87): The base slot of the storage layout must evaluate to an integer (the type is 'address' instead). diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/array_literal.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/array_literal.sol index af6b9424f6..56b5f09713 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/array_literal.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/array_literal.sol @@ -1,3 +1,3 @@ contract C layout at [1, 2, 3] {} // ---- -// TypeError 6396: (21-30): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1763: (21-30): The base slot of the storage layout must evaluate to an integer. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/bitwise_negation_after_cast.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/bitwise_negation_after_cast.sol index 6ccb2ff7a1..6c7cd0efbd 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/bitwise_negation_after_cast.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/bitwise_negation_after_cast.sol @@ -1,3 +1,3 @@ contract C layout at ~uint(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {} // ---- -// TypeError 6396: (21-94): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1505: (21-94): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/bool_constant.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/bool_constant.sol new file mode 100644 index 0000000000..e4835ca77e --- /dev/null +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/bool_constant.sol @@ -0,0 +1,4 @@ +bool constant x = false; +contract C layout at x {} +// ---- +// TypeError 1763: (46-47): The base slot of the storage layout must evaluate to an integer. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/boolean.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/boolean.sol index 083977c7ae..5391cee68c 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/boolean.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/boolean.sol @@ -1,3 +1,3 @@ contract C layout at true {} // ---- -// TypeError 6396: (21-25): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1763: (21-25): The base slot of the storage layout must evaluate to an integer. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/bytes_constant.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/bytes_constant.sol new file mode 100644 index 0000000000..ce4e252a0f --- /dev/null +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/bytes_constant.sol @@ -0,0 +1,6 @@ +bytes32 constant CONST1 = "12345"; +contract A layout at CONST1 {} +contract C layout at CONST1[1] {} +// ---- +// TypeError 1763: (56-62): The base slot of the storage layout must evaluate to an integer (the type is 'bytes32' instead). +// TypeError 1763: (87-96): The base slot of the storage layout must evaluate to an integer (the type is 'bytes1' instead). diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_divided_by_its_negation.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_divided_by_its_negation.sol index b9cdd9dfaa..9f8acbeb6a 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_divided_by_its_negation.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_divided_by_its_negation.sol @@ -1,4 +1,4 @@ uint constant N = 100; contract C layout at N / ~N {} // ---- -// TypeError 6396: (44-50): The base slot of the storage layout must evaluate to a rational number. +// TypeError 3667: (48-50): Arithmetic error when computing constant value. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_divided_by_zero.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_divided_by_zero.sol index bb97172d99..e3ba099a1d 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_divided_by_zero.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_divided_by_zero.sol @@ -1,4 +1,4 @@ uint constant N = 100; contract C layout at N / 0 {} // ---- -// TypeError 6396: (44-49): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1505: (44-49): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_from_base_contract.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_from_base_contract.sol index e06deeaaf2..1a0bd451bd 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_from_base_contract.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_from_base_contract.sol @@ -4,4 +4,4 @@ contract A { contract C is A layout at A.x { } // ---- -// TypeError 6396: (68-71): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1505: (68-71): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_from_module.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_from_module.sol new file mode 100644 index 0000000000..8be06de6c8 --- /dev/null +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_from_module.sol @@ -0,0 +1,8 @@ +==== Source: A ==== +uint constant x = 77; + +==== Source: B ==== +import "A" as M; +contract C layout at M.x{ } +// ---- +// TypeError 1505: (B:38-41): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_from_builtin.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_from_builtin.sol new file mode 100644 index 0000000000..a95cb5609d --- /dev/null +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_from_builtin.sol @@ -0,0 +1,7 @@ +uint constant x = addmod(10, 2, 8); +uint constant y = mulmod(10, 2, 8); +contract C layout at x {} +contract D layout at y {} +// ---- +// TypeError 1505: (93-94): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. +// TypeError 1505: (119-120): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_from_cast.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_from_cast.sol new file mode 100644 index 0000000000..fd75c94a44 --- /dev/null +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_from_cast.sol @@ -0,0 +1,4 @@ +uint constant x = uint(42); +contract C layout at x {} +// ---- +// TypeError 1505: (49-50): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_from_other_constant.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_from_other_constant.sol new file mode 100644 index 0000000000..1b135b32b4 --- /dev/null +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_from_other_constant.sol @@ -0,0 +1,4 @@ +uint constant x = 42; +uint constant y = x * 2; +contract C layout at y {} +// ---- diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_with_literal_expression.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_with_literal_expression.sol new file mode 100644 index 0000000000..1a06c0198f --- /dev/null +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_with_literal_expression.sol @@ -0,0 +1,3 @@ +uint constant x = ((2**5 + 2**5) * (2 ** 10 + 1 << 1)) % 2**256 - 1; +contract C layout at x {} +// ---- diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_with_unlimited_arithmetic_expression.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_with_unlimited_arithmetic_expression.sol new file mode 100644 index 0000000000..98c133e166 --- /dev/null +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_with_unlimited_arithmetic_expression.sol @@ -0,0 +1,7 @@ +uint constant CONST1 = 1.23e100 / 2e50; +contract C layout at CONST1 {} +uint constant CONST2 = 2**256 * (500e-3); +contract D layout at CONST2 {} +uint constant CONST3 = (2**255 * 2) - (2**256 + 1) + 1; +contract E layout at CONST3 {} +// ---- \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/enum.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/enum.sol index 9a264247c8..8c995bec75 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/enum.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/enum.sol @@ -2,4 +2,4 @@ enum Color {Red, Green, Blue} contract C layout at Color.Red {} // ---- -// TypeError 6396: (52-61): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1763: (52-61): The base slot of the storage layout must evaluate to an integer. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/function_defined_in_other_contract.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/function_defined_in_other_contract.sol index 68b2e24604..e7a6f1178c 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/function_defined_in_other_contract.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/function_defined_in_other_contract.sol @@ -4,4 +4,4 @@ contract A { contract C layout at A.f { } // ---- -// TypeError 6396: (71-74): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1763: (71-74): The base slot of the storage layout must evaluate to an integer. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/function_selector.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/function_selector.sol index 98c7b6057a..98ebfb775d 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/function_selector.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/function_selector.sol @@ -3,4 +3,4 @@ contract A { } contract C is A layout at uint32(this.f.selector) {} // ---- -// TypeError 6396: (68-91): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1505: (68-91): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/hex_address.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/hex_address.sol index 9e63740d89..f403c91be5 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/hex_address.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/hex_address.sol @@ -1,3 +1,3 @@ contract C layout at 0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF {} // ---- -// TypeError 6396: (21-63): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1763: (21-63): The base slot of the storage layout must evaluate to an integer (the type is 'address' instead). diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/hex_string.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/hex_string.sol index 194f0df0ea..e3bca428ae 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/hex_string.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/hex_string.sol @@ -1,3 +1,3 @@ contract C layout at hex"616263" {} // ---- -// TypeError 6396: (21-32): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1763: (21-32): The base slot of the storage layout must evaluate to an integer. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/hex_string_cast.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/hex_string_cast.sol index a803d005f7..b807f28298 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/hex_string_cast.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/hex_string_cast.sol @@ -1,3 +1,3 @@ contract at layout at uint40(bytes5(hex"0011223344")) { } // ---- -// TypeError 6396: (22-53): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1505: (22-53): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/immutables.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/immutables.sol new file mode 100644 index 0000000000..065d6290c5 --- /dev/null +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/immutables.sol @@ -0,0 +1,17 @@ +function f() returns (uint) { + return 2; +} + +contract A { + address immutable a = 0x0000000000000000000000000000000000000001; + uint immutable x = 1; // considered pure by the compiler (initialized with a literal) + uint immutable y = f(); // considered not pure by the compiler (initialized with a function) +} + +contract B is A layout at A.a { } +contract C is A layout at A.x { } +contract D is A layout at A.y { } +// ---- +// TypeError 1139: (346-349): The base slot of the storage layout must be a compile-time constant expression. +// TypeError 1139: (380-383): The base slot of the storage layout must be a compile-time constant expression. +// TypeError 1139: (414-417): The base slot of the storage layout must be a compile-time constant expression. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/int_constant.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/int_constant.sol new file mode 100644 index 0000000000..1299f1347d --- /dev/null +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/int_constant.sol @@ -0,0 +1,7 @@ +int constant x = -42; +int constant y = 64; +contract C layout at x {} +contract D layout at y {} +// ---- +// TypeError 6753: (64-65): The base slot of the storage layout evaluates to -42, which is outside the range of type uint256. +// TypeError 1481: (90-91): Base slot expression of type 'int256' is not convertible to uint256. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specification_bytes.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specification_bytes.sol index 6f9b39703a..b386d5e516 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specification_bytes.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specification_bytes.sol @@ -1,4 +1,4 @@ bytes32 constant b = "bytes"; contract A layout at b[1] {} // ---- -// TypeError 6396: (51-55): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1763: (51-55): The base slot of the storage layout must evaluate to an integer (the type is 'bytes1' instead). diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specification_constant_in_expression.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specification_constant_in_expression.sol index 8e6d37ad6e..2f0891e868 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specification_constant_in_expression.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specification_constant_in_expression.sol @@ -1,4 +1,3 @@ uint constant X = 42; contract C layout at 0xffff * (50 - X) { } // ---- -// TypeError 6396: (43-60): The base slot of the storage layout must evaluate to a rational number. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_interface_id.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_interface_id.sol index 12304ba8a0..a48172d40b 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_interface_id.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_interface_id.sol @@ -2,4 +2,4 @@ interface I {} contract C layout at uint(bytes32(type(I).interfaceId)) { } // ---- -// TypeError 6396: (37-71): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1505: (37-71): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_module.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_module.sol index ee48073043..639527500d 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_module.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_module.sol @@ -6,4 +6,4 @@ import "A" as MyModule; contract C layout at MyModule {} // ---- -// TypeError 6396: (B:46-54): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1763: (B:46-54): The base slot of the storage layout must evaluate to an integer. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_other_contract.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_other_contract.sol index 035e43d24a..12589208f1 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_other_contract.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_other_contract.sol @@ -1,4 +1,4 @@ contract A {} contract C layout at A(address(0x1234)) {} // ---- -// TypeError 6396: (35-53): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1763: (35-53): The base slot of the storage layout must evaluate to an integer. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_type.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_type.sol index 4d48e410f8..225336b67a 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_type.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_type.sol @@ -1,3 +1,3 @@ contract A layout at uint {} // ---- -// TypeError 6396: (21-25): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1763: (21-25): The base slot of the storage layout must evaluate to an integer. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/literal_cast.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/literal_cast.sol index 0f682d779b..067e23cf4b 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/literal_cast.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/literal_cast.sol @@ -1,3 +1,3 @@ contract at layout at uint(42) { } // ---- -// TypeError 6396: (22-30): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1505: (22-30): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/precompiles.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/precompiles.sol index c87a98eaa9..f8f8762073 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/precompiles.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/precompiles.sol @@ -1,5 +1,5 @@ contract A layout at addmod(1, 2, 3) {} contract B layout at mulmod(3, 2, 1) {} // ---- -// TypeError 6396: (21-36): The base slot of the storage layout must evaluate to a rational number. -// TypeError 6396: (61-76): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1505: (21-36): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. +// TypeError 1505: (61-76): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/rational_number_zero_fractional_part.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/rational_number_without_fractional_part.sol similarity index 70% rename from test/libsolidity/syntaxTests/storageLayoutSpecifier/rational_number_zero_fractional_part.sol rename to test/libsolidity/syntaxTests/storageLayoutSpecifier/rational_number_without_fractional_part.sol index e21521e39a..216a9dc1e2 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/rational_number_zero_fractional_part.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/rational_number_without_fractional_part.sol @@ -1,3 +1,4 @@ contract A layout at 42.0 {} contract B layout at 2.5e10 {} +contract C layout at 12/3 {} // ---- diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/string.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/string.sol index 3b8292fe8c..a6686af39f 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/string.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/string.sol @@ -1,3 +1,3 @@ contract C layout at "MyLayoutBase" {} // ---- -// TypeError 6396: (21-35): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1763: (21-35): The base slot of the storage layout must evaluate to an integer. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/ternary_operator.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/ternary_operator.sol index f426104cc2..d73bd28903 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/ternary_operator.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/ternary_operator.sol @@ -1,5 +1,5 @@ contract A layout at true ? 42 : 94 {} contract B layout at 255 + (true ? 1 : 0) {} // ---- -// TypeError 6396: (21-35): The base slot of the storage layout must evaluate to a rational number. -// TypeError 6396: (60-80): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1505: (21-35): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. +// TypeError 1505: (60-80): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/tuple.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/tuple.sol index eb4693e488..73d1fec74d 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/tuple.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/tuple.sol @@ -1,3 +1,3 @@ contract C layout at (1, 2, 3) {} // ---- -// TypeError 6396: (21-30): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1763: (21-30): The base slot of the storage layout must evaluate to an integer. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/type_uint_max.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/type_uint_max.sol index dd8ee6f9cc..6f53097c50 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/type_uint_max.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/type_uint_max.sol @@ -1,3 +1,3 @@ contract at layout at type(uint).max { } // ---- -// TypeError 6396: (22-36): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1505: (22-36): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/user_defined_value_type.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/user_defined_value_type.sol index 9803641688..3e721a3998 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/user_defined_value_type.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/user_defined_value_type.sol @@ -2,4 +2,4 @@ type MyUint is uint128; MyUint constant x = MyUint.wrap(42); contract C layout at x {} // ---- -// TypeError 6396: (82-83): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1763: (82-83): The base slot of the storage layout must evaluate to an integer (the type is 'MyUint' instead). diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/user_defined_value_type_unwrap.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/user_defined_value_type_unwrap.sol index b45ee88dd9..a4ba0a8022 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/user_defined_value_type_unwrap.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/user_defined_value_type_unwrap.sol @@ -2,4 +2,4 @@ type MyUint is uint128; MyUint constant x = MyUint.wrap(42); contract C layout at MyUint.unwrap(x) {} // ---- -// TypeError 6396: (82-98): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1505: (82-98): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/user_defined_value_type_wrap.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/user_defined_value_type_wrap.sol index 172fb58ab1..4f3d8f8833 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/user_defined_value_type_wrap.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/user_defined_value_type_wrap.sol @@ -1,4 +1,4 @@ type MyUint is uint128; contract C layout at MyUint.wrap(42) {} // ---- -// TypeError 6396: (45-60): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1763: (45-60): The base slot of the storage layout must evaluate to an integer (the type is 'MyUint' instead). diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/value_from_array_literal.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/value_from_array_literal.sol index bb56de7289..929b9d088b 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/value_from_array_literal.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/value_from_array_literal.sol @@ -1,5 +1,5 @@ contract A layout at [1, 2, 3][0] {} contract B layout at 255 + [1, 2, 3][0] {} // ---- -// TypeError 6396: (21-33): The base slot of the storage layout must evaluate to a rational number. -// TypeError 6396: (58-76): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1505: (21-33): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. +// TypeError 1505: (58-76): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/value_from_bytes.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/value_from_bytes.sol index 1f08fa0ec9..8c6dfea772 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/value_from_bytes.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/value_from_bytes.sol @@ -1,4 +1,4 @@ bytes32 constant b = "Solidity"; contract C layout at uint8(b[1]) {} // ---- -// TypeError 6396: (54-65): The base slot of the storage layout must evaluate to a rational number. +// TypeError 1505: (54-65): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. From 6300a440cf1ecc2407bb456be3700a36060b2325 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Sat, 15 Mar 2025 12:10:44 -0300 Subject: [PATCH 090/243] Allow constants in custom storage layout expression --- Changelog.md | 2 + docs/contracts/custom-storage-layout.rst | 1 + .../analysis/PostTypeContractLevelChecker.cpp | 80 ++++++++++++++++--- .../address_constant.sol | 4 +- ...d_with_unlimited_arithmetic_expression.sol | 2 +- 5 files changed, 72 insertions(+), 17 deletions(-) diff --git a/Changelog.md b/Changelog.md index ba3e76659b..0a35b2a87e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Language Features: * Yul: Introduce builtin `clz(x)` for counting the number of leading zero bits in a 256-bit word. Compiler Features: +* Custom Storage Layout: Allow using `constant` state variables in the base slot expression. * ethdebug: Experimental support for instructions and source locations under EOF. * EVM: Set default EVM Version to `osaka`. * DocString Parser: Warn about deprecation of inline assembly special comment `memory-safe-assembly`. @@ -12,6 +13,7 @@ Compiler Features: * Type Checker: Warn about deprecation of `send` and `transfer` functions on instances of `address`. * Type Checker: Warn about deprecation of comparisons between variables of contract types. + Bugfixes: * Assembler: Fix not using a fixed-width type for IDs being assigned to subassemblies nested more than one level away, resulting in inconsistent `--asm-json` output between target architectures. * Yul Optimizer: Fix edge case in which invalid Yul code is produced by ExpressionSimplifier due to expressions being substituted that contain out-of-scope variables. diff --git a/docs/contracts/custom-storage-layout.rst b/docs/contracts/custom-storage-layout.rst index 224e774250..72f281d7f9 100644 --- a/docs/contracts/custom-storage-layout.rst +++ b/docs/contracts/custom-storage-layout.rst @@ -25,6 +25,7 @@ and is located in the header of a contract definition. The layout specifier can be placed either before or after the inheritance specifier, and can appear at most once. The ``base-slot-expression`` must be an :ref:`integer literal` expression that can be evaluated at compilation time and yields a value in the range of ``uint256``. +The use of constants initialized using such expressions is also allowed. A custom layout cannot make contract's storage "wrap around". If the selected base slot would push the static variables past the end of storage, diff --git a/libsolidity/analysis/PostTypeContractLevelChecker.cpp b/libsolidity/analysis/PostTypeContractLevelChecker.cpp index c7b7b09616..f8f4b6ef69 100644 --- a/libsolidity/analysis/PostTypeContractLevelChecker.cpp +++ b/libsolidity/analysis/PostTypeContractLevelChecker.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -101,29 +102,71 @@ void PostTypeContractLevelChecker::checkStorageLayoutSpecifier(ContractDefinitio } auto const* baseSlotExpressionType = type(baseSlotExpression); + auto const* integerType = dynamic_cast(baseSlotExpressionType); auto const* rationalType = dynamic_cast(baseSlotExpressionType); - if (!rationalType) + if ( + !integerType && + !rationalType + ) { + std::string errorMsg = "The base slot of the storage layout must evaluate to an integer"; + if (dynamic_cast(baseSlotExpressionType)) + errorMsg += " (the type is 'address' instead)"; + else if (auto const* fixedBytesType = dynamic_cast(baseSlotExpressionType)) + errorMsg += fmt::format( + " (the type is 'bytes{}' instead)", + fixedBytesType->numBytes() + ) + ; + else if (auto const* userDefinedType = dynamic_cast(baseSlotExpressionType)) + errorMsg += fmt::format( + " (the type is '{}' instead)", + userDefinedType->canonicalName() + ) + ; + errorMsg += "."; + m_errorReporter.typeError( - 6396_error, + 1763_error, baseSlotExpression.location(), - "The base slot of the storage layout must evaluate to a rational number." + errorMsg ); return; } - if (rationalType->isFractional()) + rational baseSlotRationalValue; + if (integerType) { - m_errorReporter.typeError( - 1763_error, - baseSlotExpression.location(), - "The base slot of the storage layout must evaluate to an integer." - ); - return; + std::optional typedRational = ConstantEvaluator::evaluate(m_errorReporter, baseSlotExpression); + if (!typedRational) + { + m_errorReporter.typeError( + 1505_error, + baseSlotExpression.location(), + "The base slot expression contains elements that are not yet supported " + "by the internal constant evaluator and therefore cannot be evaluated at compilation time." + ); + return; + } + baseSlotRationalValue = typedRational->value; + } + else + { + solAssert(rationalType); + if (rationalType->isFractional()) + { + m_errorReporter.typeError( + ErrorId{1763}, + baseSlotExpression.location(), + "The base slot of the storage layout must evaluate to an integer." + ); + return; + } + baseSlotRationalValue = rationalType->value(); } - solAssert(rationalType->value().denominator() == 1); - bigint baseSlot = rationalType->value().numerator(); + solAssert(baseSlotRationalValue.denominator() == 1); + bigint baseSlot = baseSlotRationalValue.numerator(); if (!(0 <= baseSlot && baseSlot <= std::numeric_limits::max())) { m_errorReporter.typeError( @@ -137,7 +180,18 @@ void PostTypeContractLevelChecker::checkStorageLayoutSpecifier(ContractDefinitio return; } - solAssert(baseSlotExpressionType->isImplicitlyConvertibleTo(*TypeProvider::uint256())); + if (!baseSlotExpressionType->isImplicitlyConvertibleTo(*TypeProvider::uint256())) + { + m_errorReporter.typeError( + 1481_error, + baseSlotExpression.location(), + fmt::format( + "Base slot expression of type '{}' is not convertible to uint256.", + baseSlotExpressionType->humanReadableName() + ) + ); + return; + } storageLayoutSpecifier->annotation().baseSlot = u256(baseSlot); bigint size = contractStorageSizeUpperBound(_contract, VariableDeclaration::Location::Unspecified); diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/address_constant.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/address_constant.sol index 3eda177fc3..da9449b67c 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/address_constant.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/address_constant.sol @@ -1,6 +1,4 @@ address constant x = 0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF; -contract C layout at x { - -} +contract C layout at x {} // ---- // TypeError 1763: (86-87): The base slot of the storage layout must evaluate to an integer (the type is 'address' instead). diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_with_unlimited_arithmetic_expression.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_with_unlimited_arithmetic_expression.sol index 98c133e166..926bff5544 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_with_unlimited_arithmetic_expression.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_with_unlimited_arithmetic_expression.sol @@ -4,4 +4,4 @@ uint constant CONST2 = 2**256 * (500e-3); contract D layout at CONST2 {} uint constant CONST3 = (2**255 * 2) - (2**256 + 1) + 1; contract E layout at CONST3 {} -// ---- \ No newline at end of file +// ---- From 67abe75d44735b3d6b852c790e49e01d94797cb4 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Tue, 2 Dec 2025 22:56:11 -0300 Subject: [PATCH 091/243] sort changelog v.0.8.31 --- Changelog.md | 7 +++++-- docs/bugs_by_version.json | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 0a35b2a87e..a75193adb2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,13 +1,14 @@ -### 0.8.31 (unreleased) +### 0.8.31 (2025-12-03) Language Features: * Yul: Introduce builtin `clz(x)` for counting the number of leading zero bits in a 256-bit word. + Compiler Features: * Custom Storage Layout: Allow using `constant` state variables in the base slot expression. +* DocString Parser: Warn about deprecation of inline assembly special comment `memory-safe-assembly`. * ethdebug: Experimental support for instructions and source locations under EOF. * EVM: Set default EVM Version to `osaka`. -* DocString Parser: Warn about deprecation of inline assembly special comment `memory-safe-assembly`. * Syntax Checker: Warn about deprecation of ABI coder v1. * Syntax Checker: Warn about deprecation of virtual modifiers. * Type Checker: Warn about deprecation of `send` and `transfer` functions on instances of `address`. @@ -18,11 +19,13 @@ Bugfixes: * Assembler: Fix not using a fixed-width type for IDs being assigned to subassemblies nested more than one level away, resulting in inconsistent `--asm-json` output between target architectures. * Yul Optimizer: Fix edge case in which invalid Yul code is produced by ExpressionSimplifier due to expressions being substituted that contain out-of-scope variables. + Build System: * Enable Linux arm64 binaries for testing and releases. * Ubuntu PPA Packages: Discontinue the PPA as a binary distribution channel. * Update minimum version requirements of Boost to 1.83.0 for non-windows builds and of GCC and Clang to 13.3 and 18.1.3, respectively. Fixes infinite recursion on `boost::rational` comparison affecting compiler binaries built with GCC<14.0 and Boost<1.75. + ### 0.8.30 (2025-05-07) Compiler Features: diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 1a210b885c..881f5eabee 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1927,6 +1927,10 @@ "bugs": [], "released": "2025-05-07" }, + "0.8.31": { + "bugs": [], + "released": "2025-12-03" + }, "0.8.4": { "bugs": [ "FullInlinerNonExpressionSplitArgumentEvaluationOrder", From 886bf48051632d01b745aa8f542b965276454e57 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Tue, 2 Dec 2025 16:11:56 -0300 Subject: [PATCH 092/243] Set version to 0.8.32 --- CMakeLists.txt | 2 +- Changelog.md | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e477cf3a8..f4d67e243b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.8.31") +set(PROJECT_VERSION "0.8.32") # OSX target needed in order to support std::visit set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) diff --git a/Changelog.md b/Changelog.md index a75193adb2..5cd6133f2c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,12 @@ +### 0.8.32 (unreleased) + +Language Features: + +Compiler Features: + +Bugfixes: + + ### 0.8.31 (2025-12-03) Language Features: From b7ce492bb651f1f80d5d5a4d675d97972387e70b Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Wed, 3 Dec 2025 16:35:56 -0300 Subject: [PATCH 093/243] Move changelog entries to correct section --- Changelog.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Changelog.md b/Changelog.md index 5cd6133f2c..1aa6bc64b2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,18 +10,18 @@ Bugfixes: ### 0.8.31 (2025-12-03) Language Features: -* Yul: Introduce builtin `clz(x)` for counting the number of leading zero bits in a 256-bit word. - - -Compiler Features: * Custom Storage Layout: Allow using `constant` state variables in the base slot expression. * DocString Parser: Warn about deprecation of inline assembly special comment `memory-safe-assembly`. -* ethdebug: Experimental support for instructions and source locations under EOF. -* EVM: Set default EVM Version to `osaka`. * Syntax Checker: Warn about deprecation of ABI coder v1. * Syntax Checker: Warn about deprecation of virtual modifiers. * Type Checker: Warn about deprecation of `send` and `transfer` functions on instances of `address`. * Type Checker: Warn about deprecation of comparisons between variables of contract types. +* Yul: Introduce builtin `clz(x)` for counting the number of leading zero bits in a 256-bit word. + + +Compiler Features: +* ethdebug: Experimental support for instructions and source locations under EOF. +* EVM: Set default EVM Version to `osaka`. Bugfixes: From ec00d77b89dae6444ef689d2c6c7b21a1c19f638 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Wed, 3 Dec 2025 13:53:06 -0300 Subject: [PATCH 094/243] Update release checklist with docker login info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kamil Śliwak --- ReleaseChecklist.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index b78edc2de2..9c3b2f8968 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -42,6 +42,7 @@ At least a day before the release: - [ ] Get the posts reviewed and approved **before the release starts**. ### Changelog +- [ ] Ensure that all changelog entries are correctly classified as language or compiler features. - [ ] Sort the changelog entries alphabetically and correct any errors you notice. Commit it. - [ ] Update the changelog to include a release date. - [ ] Run `scripts/update_bugs_by_version.py` to regenerate `bugs_by_version.json` from the changelog and `bugs.json`. @@ -76,6 +77,7 @@ At least a day before the release: ### Docker - [ ] Make sure `docker-buildx` is installed. +- [ ] Run `echo $GHCR_TOKEN | docker login ghcr.io --username $GH_USERNAME --password-stdin` where `$GH_USERNAME` is your GitHub username and `$GHCR_TOKEN` is a PAT with `write:packages` scope. - [ ] Run `./scripts/docker_deploy_manual.sh v$VERSION`. ### Release solc-js From 7b39f83127382f9fb057567cc04035cd838e5627 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Thu, 4 Dec 2025 15:09:49 -0300 Subject: [PATCH 095/243] Copy libevmone to Linux ARM machine --- scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.arm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.arm b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.arm index a24549d4b8..663928220a 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.arm +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.arm @@ -22,7 +22,7 @@ # (c) 2016-2025 solidity contributors. #------------------------------------------------------------------------------ FROM --platform=linux/arm64 buildpack-deps:noble AS base -LABEL version="1" +LABEL version="2" ARG DEBIAN_FRONTEND=noninteractive @@ -104,6 +104,7 @@ RUN <<-EOF EOF FROM base +COPY --from=libraries /usr/local/lib/libevmone* /usr/lib COPY --from=libraries /usr/lib /usr/lib COPY --from=libraries /usr/bin /usr/bin COPY --from=libraries /usr/include /usr/include From 6ac7d73b23d9e7b1c6fbb02fcd1cde77537bd2e3 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Thu, 4 Dec 2025 18:33:31 -0300 Subject: [PATCH 096/243] Disable partial clone and fetch all objects on alpine --- .circleci/config.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6255d2834e..6109d05612 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1931,6 +1931,12 @@ jobs: # This job is only meant to run nightly so build time is not an issue. docker_layer_caching: false - checkout + - run: + name: Disable partial clone and fetch all objects + command: | + git config remote.origin.promisor false + git config --unset remote.origin.partialclonefilter + git fetch --refetch origin - run: name: Build and tag the container command: scripts/docker_deploy_manual.sh develop "file://$PWD" --no-push From 37653a0f95d4c3330e3d2564cd77946b963043dd Mon Sep 17 00:00:00 2001 From: blishko Date: Fri, 5 Dec 2025 11:24:57 +0100 Subject: [PATCH 097/243] SMTChecker: Use Eldarica in more tests Eldarica is more reliable on Z3 and results in fewer differences of behaviour across different platforms. The disadvantage is that Eldarica often takes longer to solve the problems than Z3. --- .../external_calls/external_call_indirect_2.sol | 2 +- .../external_calls/external_call_indirect_5.sol | 1 + .../smtCheckerTests/functions/functions_external_2.sol | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_2.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_2.sol index 16779bb89a..d6575fdc22 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_2.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_2.sol @@ -47,7 +47,7 @@ contract C { // ==== // SMTEngine: chc // SMTExtCalls: trusted -// SMTIgnoreOS: macos +// SMTSolvers: eld // ---- // Warning 6328: (434-455): CHC: Assertion violation happens here. // Warning 6328: (1270-1291): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_5.sol b/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_5.sol index 26d39838ef..da3077c84a 100644 --- a/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_5.sol +++ b/test/libsolidity/smtCheckerTests/external_calls/external_call_indirect_5.sol @@ -46,6 +46,7 @@ contract C { // SMTContract: C // SMTEngine: chc // SMTExtCalls: trusted +// SMTSolvers: eld // ---- // Warning 6328: (601-622): CHC: Assertion violation happens here. // Info 1391: CHC: 1 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/functions/functions_external_2.sol b/test/libsolidity/smtCheckerTests/functions/functions_external_2.sol index d3373b62d6..cbac9fbbba 100644 --- a/test/libsolidity/smtCheckerTests/functions/functions_external_2.sol +++ b/test/libsolidity/smtCheckerTests/functions/functions_external_2.sol @@ -20,9 +20,9 @@ contract C } } // ==== -// SMTEngine: all +// SMTEngine: chc // SMTIgnoreCex: yes -// SMTIgnoreOS: macos +// SMTSolvers: eld // ---- // Warning 6328: (234-253): CHC: Assertion violation happens here. // Info 1391: CHC: 2 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. From bdf3c50ea0229d086e6b38a1c520f45204a36ca7 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Fri, 5 Dec 2025 19:08:54 -0300 Subject: [PATCH 098/243] Update fmtlib to 12.1.0 --- deps/fmtlib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/fmtlib b/deps/fmtlib index 40626af88b..407c905e45 160000 --- a/deps/fmtlib +++ b/deps/fmtlib @@ -1 +1 @@ -Subproject commit 40626af88bd7df9a5fb80be7b25ac85b122d6c21 +Subproject commit 407c905e45ad75fc29bf0f9bb7c5c2fd3475976f From e0773b9fadc5cd6fcf718314ce511500501f8a56 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Tue, 2 Dec 2025 23:21:47 -0300 Subject: [PATCH 099/243] run soltest and cli tests on arm --- .circleci/config.yml | 49 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6109d05612..8c7a560183 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -701,6 +701,11 @@ defaults: requires: - b_ubu_static + - requires_b_ubu_static_arm: &requires_b_ubu_static_arm + <<: *on_all_tags_and_branches + requires: + - b_ubu_static_arm + - requires_b_archlinux: &requires_b_archlinux <<: *on_all_tags_and_branches requires: @@ -1104,7 +1109,7 @@ jobs: - solc/solc-static-linux - matrix_notify_failure_unless_pr - b_ubu_static_arm: + b_ubu_static_arm: &b_ubu_static_arm <<: *base_ubuntu2404_arm_large environment: <<: *base_ubuntu2404_arm_large_env @@ -1293,6 +1298,28 @@ jobs: steps: - soltest_all + t_ubu_arm_soltest: &t_ubu_arm_soltest + <<: *base_ubuntu2404_arm_medium + parallelism: 20 + environment: + <<: *base_ubuntu2404_arm_medium_env + EVM: << pipeline.parameters.evm-version >> + EOF_VERSION: 0 + OPTIMIZE: 0 + steps: + - checkout + - attach_workspace: + at: build + - run: + name: Create symbolic link with expected soltest binary name + command: | + ln --symbolic --relative build/test/soltest-linux-arm build/test/soltest + - run_soltest + - store_test_results: + path: test_results/ + - store_artifacts_test_results + - matrix_notify_failure_unless_pr + t_ubu_soltest_deprecated_evm_versions: &t_ubu_soltest_deprecated_evm_versions <<: *base_ubuntu2404_large parallelism: 50 @@ -1355,6 +1382,24 @@ jobs: steps: - cmdline_tests + t_ubu_arm_cli: &t_ubu_arm_cli + <<: *base_ubuntu2404_arm_medium + parallelism: 8 # Should match number of tests in .circleci/parallel_cli_tests.py + steps: + - checkout + - attach_workspace: + at: build + - run: + name: Create symbolic link with expected solc and solfuzzer binaries name + command: | + ln --symbolic --relative build/solc/solc-static-linux-arm build/solc/solc + ln --symbolic --relative build/test/tools/solfuzzer-linux-arm build/test/tools/solfuzzer + - run_cmdline_tests + - store_test_results: + path: test_results/ + - store_artifacts_test_results + - matrix_notify_failure_unless_pr + t_ubu_force_release_cli: &t_ubu_force_release_cli <<: *t_ubu_cli @@ -1991,8 +2036,10 @@ workflows: # Ubuntu build and tests - b_ubu: *requires_nothing - t_ubu_cli: *requires_b_ubu + - t_ubu_arm_cli: *requires_b_ubu_static_arm - t_ubu_locale: *requires_b_ubu - t_ubu_soltest_all: *requires_b_ubu + - t_ubu_arm_soltest: *requires_b_ubu_static_arm - b_ubu_clang: *requires_nothing - t_ubu_clang_soltest: *requires_b_ubu_clang - t_ubu_lsp: *requires_b_ubu From 9763e9f5d0e95f4f3d2920c7bec7e821e24edeea Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Thu, 4 Dec 2025 18:25:04 -0300 Subject: [PATCH 100/243] Use new Linux ARM image --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8c7a560183..69ae1999fc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -17,8 +17,8 @@ parameters: default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:c0412c53e59ce0c96bde4c08e7332ea12e4cadba9bbac829621947897fa21272" ubuntu-2404-arm-docker-image: type: string - # ghcr.io/argotorg/solidity-buildpack-deps:ubuntu2404.arm-1 - default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:6cdb928fa8743d0b5d515c2c489b8d545c541f2370af28d5d3a71532056a0f22" + # ghcr.io/argotorg/solidity-buildpack-deps:ubuntu2404.arm-2 + default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:27b03c1c4688e5d69b10e0539460346a3dee3b10b0e04fb406b9707c6f0dd95e" ubuntu-2404-clang-docker-image: type: string # ghcr.io/argotorg/solidity-buildpack-deps:ubuntu2404.clang-7 From 8d563631f2e9a04555b9149513fd4eae283b0ee1 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Sat, 6 Dec 2025 16:09:16 -0300 Subject: [PATCH 101/243] Update nlohmann-json to v3.12.0 --- deps/nlohmann-json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/nlohmann-json b/deps/nlohmann-json index 9cca280a4d..55f93686c0 160000 --- a/deps/nlohmann-json +++ b/deps/nlohmann-json @@ -1 +1 @@ -Subproject commit 9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03 +Subproject commit 55f93686c01528224f448c19128836e7df245f72 From 37ce297ec64d1e32cad82c1fc370a9702a44447b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 10 Dec 2025 11:50:42 +0100 Subject: [PATCH 102/243] contributing.rst: Add a link to the public call agenda --- docs/contributing.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/contributing.rst b/docs/contributing.rst index 45b15e579d..b5eb3c08ed 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -34,6 +34,9 @@ the team and contributors are working on, you can join our public team call: The call takes place on `Jitsi `_. +New topics can be freely added to the `agenda `_ +and will be scheduled for discussion on the nearest call. + How to Report Issues ==================== From 51e91352c0b08cf813fe4458a0ea1be3470e14fb Mon Sep 17 00:00:00 2001 From: rodiazet Date: Wed, 3 Dec 2025 17:00:30 +0100 Subject: [PATCH 103/243] Fix constantness of event and error selectors. Reported by https://github.com/argotorg/solidity/issues/13137 --- libsolidity/analysis/TypeChecker.cpp | 27 ++++++++++++++----- .../event_error_selector_pure_assignment.sol | 20 ++++++++++++++ 2 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 test/libsolidity/syntaxTests/types/functionTypes/selector/event_error_selector_pure_assignment.sol diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 9e29983bad..f5c093dbd4 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -3280,18 +3280,31 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) auto const* functionType = dynamic_cast(exprType); functionType && functionType->hasDeclaration() && - dynamic_cast(&functionType->declaration()) && memberName == "selector" ) - if (auto const* parentAccess = dynamic_cast(&_memberAccess.expression())) + { + if (dynamic_cast(&functionType->declaration())) { - bool isPure = *parentAccess->expression().annotation().isPure; - if (auto const* exprInt = dynamic_cast(&parentAccess->expression())) - if (exprInt->name() == "this" || exprInt->name() == "super") - isPure = true; + if (auto const* parentAccess = dynamic_cast(&_memberAccess.expression())) + { + bool isPure = *parentAccess->expression().annotation().isPure; + // Accessing a function selector using `super|this.f.selector`. + if (auto const* exprInt = dynamic_cast(&parentAccess->expression())) + if (exprInt->name() == "this" || exprInt->name() == "super") + isPure = true; - annotation.isPure = isPure; + annotation.isPure = isPure; + } } + // In case of event or error definition the selector is always compile-time constant, as it can be + // a keccak256 hash of the event signature or a function selector in case of an error. + else if ( + dynamic_cast(&functionType->declaration()) || + dynamic_cast(&functionType->declaration()) + ) + annotation.isPure = true; + } + if ( auto const* varDecl = dynamic_cast(annotation.referencedDeclaration); !annotation.isPure.set() && diff --git a/test/libsolidity/syntaxTests/types/functionTypes/selector/event_error_selector_pure_assignment.sol b/test/libsolidity/syntaxTests/types/functionTypes/selector/event_error_selector_pure_assignment.sol new file mode 100644 index 0000000000..263e8f8a1a --- /dev/null +++ b/test/libsolidity/syntaxTests/types/functionTypes/selector/event_error_selector_pure_assignment.sol @@ -0,0 +1,20 @@ +event EvExt(); +error ErExt(); + +bytes32 constant eventExtSelectorGlobal = EvExt.selector; +bytes4 constant errorExtSelectorGlobal = ErExt.selector; + +contract C { + event Ev(); + error Er(); + + bytes4 constant errorExtSelector = ErExt.selector; + bytes32 constant eventExtSelector = EvExt.selector; + + bytes4 constant errorSelector = Er.selector; + bytes32 constant eventSelector = Ev.selector; + + bytes4 constant errorSelectorC = C.Er.selector; + bytes32 constant eventSelectorC = C.Ev.selector; +} +// ---- From 4ec10c9343225595cb1d2655f4c66b29349f59c9 Mon Sep 17 00:00:00 2001 From: rodiazet Date: Mon, 8 Dec 2025 09:56:06 +0100 Subject: [PATCH 104/243] Add the changelog entry. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update Changelog.md Co-authored-by: Nikola Matić --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 1aa6bc64b2..42d679caa3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Language Features: Compiler Features: Bugfixes: +* TypeChecker: Fix error and event selectors not being considered compile-time constant. ### 0.8.31 (2025-12-03) From befaeda28009ce126bfa3c4b26f2877433e30fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 15 Dec 2025 10:27:20 +0100 Subject: [PATCH 105/243] Always update stale gas expectations, even if below threshold --- test/libsolidity/SemanticTest.cpp | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index 1b66dd60ff..890a51596a 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -579,22 +579,16 @@ bool SemanticTest::checkGasCostExpectation(TestFunctionCall& io_test, bool _comp io_test.call().expectations.gasUsedForCodeDeposit.count(setting) ); - // We don't check gas if enforce gas cost is not active - // or test is run with abi encoder v1 only - // or gas used less than threshold for enforcing feature - // or the test has used up all available gas (test will fail anyway) - // or setting is "ir" and it's not included in expectations - // or if the called function is an isoltest builtin e.g. `smokeTest` or `storageEmpty` - if ( - !m_enforceGasCost || - m_gasUsed < m_enforceGasCostMinValue || - m_gasUsed >= InitialGas || - (setting == "ir" && io_test.call().expectations.gasUsedExcludingCode.count(setting) == 0) || - io_test.call().kind == FunctionCall::Kind::Builtin - ) + bool uninteresting = + m_gasUsed < m_enforceGasCostMinValue || // gas used less than threshold for enforcing feature + m_gasUsed >= InitialGas || // test has used up all available gas (test will fail anyway) + setting == "ir" || + io_test.call().kind == FunctionCall::Kind::Builtin; // isoltest builtin e.g. `smokeTest` or `storageEmpty` + bool gasValueMissing = !io_test.call().expectations.gasUsedExcludingCode.contains(setting); + if (!m_enforceGasCost || (gasValueMissing && uninteresting)) return true; - solAssert(!m_runWithABIEncoderV1Only, ""); + solAssert(!m_runWithABIEncoderV1Only); // NOTE: Cost excluding code is unlikely to be negative but it may still be possible due to refunds. // We'll deal with it when we actually have a test case like that. From 16ab579bdb49afd7d5d10af011c6348df0d90028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 15 Dec 2025 11:30:41 +0100 Subject: [PATCH 106/243] Update stale gas expectations that dropped below the default 100k threshold --- ...ode_v2_in_modifier_used_in_v1_contract.sol | 3 +- .../abiEncoderV2/calldata_array.sol | 4 +- .../array/array_storage_length_access.sol | 2 +- ...rray_storage_push_empty_length_address.sol | 6 +- .../array/array_storage_push_pop.sol | 6 +- .../array/byte_array_storage_layout.sol | 10 ++-- .../copying/array_copy_cleanup_uint128.sol | 6 +- .../array_copy_clear_storage_packed.sol | 12 ++-- ...ray_copy_storage_storage_static_simple.sol | 4 +- .../copying/array_copy_target_leftover2.sol | 6 +- ...o_storage_dynamic_different_mutability.sol | 2 +- .../copying/arrays_from_and_to_storage.sol | 2 +- .../copying/bytes_storage_to_storage.sol | 4 +- ...sted_array_element_calldata_to_storage.sol | 4 +- ...nested_array_element_memory_to_storage.sol | 4 +- .../delete/delete_storage_array_packed.sol | 6 +- .../array/dynamic_array_cleanup.sol | 6 +- .../array/fixed_array_cleanup.sol | 6 +- .../array/pop/byte_array_pop_copy_long.sol | 6 +- .../array/push/array_push_packed_array.sol | 6 +- .../array/push/push_no_args_2d.sol | 2 +- ...ing_explicit_downcast_during_emissions.sol | 3 +- .../events/event_indexed_mixed.sol | 6 +- .../events/event_signature_in_library.sol | 3 +- .../externalContracts/FixedFeeRegistrar.sol | 6 +- .../externalContracts/prbmath_signed.sol | 58 +++++++++--------- .../externalContracts/prbmath_unsigned.sol | 60 +++++++++---------- .../externalContracts/ramanujan_pi.sol | 4 +- .../externalContracts/strings.sol | 24 ++++---- .../functionCall/send_zero_ether.sol | 6 +- .../functionTypes/store_function.sol | 3 +- .../address_overload_resolution.sol | 6 +- ...d_function_calldata_calldata_interface.sol | 3 +- ...ted_function_calldata_memory_interface.sol | 3 +- .../interface_inheritance_conversions.sol | 10 ++-- .../keccak_yul_optimization.sol | 12 ++-- .../libraries/using_library_structs.sol | 2 +- .../operator_making_pure_external_call.sol | 6 +- .../operator_making_view_external_call.sol | 6 +- .../struct_delete_storage_nested_small.sol | 2 +- ...truct_delete_storage_with_arrays_small.sol | 2 +- .../calldata_to_storage.sol | 18 +++--- .../userDefinedValueType/erc20.sol | 36 +++++------ .../memory_to_storage.sol | 18 +++--- ...cost_abstraction_comparison_elementary.sol | 24 ++++---- ...ost_abstraction_comparison_userdefined.sol | 24 ++++---- .../various/codebalance_assembly.sol | 3 +- .../various/contract_binary_dependencies.sol | 3 +- .../semanticTests/various/erc20.sol | 34 +++++------ 49 files changed, 254 insertions(+), 238 deletions(-) diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_modifier_used_in_v1_contract.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_modifier_used_in_v1_contract.sol index 8df4090c36..78da59648a 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_modifier_used_in_v1_contract.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_modifier_used_in_v1_contract.sol @@ -38,6 +38,7 @@ contract C is B { } // ---- // test() -> 5, 10 -// gas irOptimized: 87337 +// gas irOptimized: 65040 +// gas irOptimized code: 26200 // gas legacy: 66250 // gas legacy code: 36400 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array.sol index cdce1b95a5..739c4c8407 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array.sol @@ -21,5 +21,5 @@ contract C { // f(uint256[][1]): 32, 32, 1, 42 -> true // f(uint256[][1]): 32, 32, 8, 421, 422, 423, 424, 425, 426, 427, 428 -> true // gas irOptimized: 120978 -// gas legacy: 101568 -// gas legacyOptimized: 119092 +// gas legacy: 98324 +// gas legacyOptimized: 94348 diff --git a/test/libsolidity/semanticTests/array/array_storage_length_access.sol b/test/libsolidity/semanticTests/array/array_storage_length_access.sol index 47fddc5547..4f7308c244 100644 --- a/test/libsolidity/semanticTests/array/array_storage_length_access.sol +++ b/test/libsolidity/semanticTests/array/array_storage_length_access.sol @@ -12,7 +12,7 @@ contract C { // set_get_length(uint256): 10 -> 10 // set_get_length(uint256): 20 -> 20 // set_get_length(uint256): 0xFF -> 0xFF -// gas irOptimized: 96690 +// gas irOptimized: 98764 // gas legacy: 128571 // gas legacyOptimized: 110143 // set_get_length(uint256): 0xFFF -> 0xFFF diff --git a/test/libsolidity/semanticTests/array/array_storage_push_empty_length_address.sol b/test/libsolidity/semanticTests/array/array_storage_push_empty_length_address.sol index 607e1ecd35..bf06393a63 100644 --- a/test/libsolidity/semanticTests/array/array_storage_push_empty_length_address.sol +++ b/test/libsolidity/semanticTests/array/array_storage_push_empty_length_address.sol @@ -17,9 +17,9 @@ contract C { // set_get_length(uint256): 10 -> 10 // set_get_length(uint256): 20 -> 20 // set_get_length(uint256): 0 -> 0 -// gas irOptimized: 77628 -// gas legacy: 77730 -// gas legacyOptimized: 77162 +// gas irOptimized: 76123 +// gas legacy: 75618 +// gas legacyOptimized: 75076 // set_get_length(uint256): 0xFF -> 0xFF // gas irOptimized: 168565 // gas legacy: 696850 diff --git a/test/libsolidity/semanticTests/array/array_storage_push_pop.sol b/test/libsolidity/semanticTests/array/array_storage_push_pop.sol index a4c7d34d7f..609944ddc0 100644 --- a/test/libsolidity/semanticTests/array/array_storage_push_pop.sol +++ b/test/libsolidity/semanticTests/array/array_storage_push_pop.sol @@ -13,9 +13,9 @@ contract C { // set_get_length(uint256): 1 -> 0 // set_get_length(uint256): 10 -> 0 // set_get_length(uint256): 20 -> 0 -// gas irOptimized: 106222 -// gas legacy: 105722 -// gas legacyOptimized: 103508 +// gas irOptimized: 87226 +// gas legacy: 85609 +// gas legacyOptimized: 83462 // set_get_length(uint256): 0xFF -> 0 // gas irOptimized: 833586 // gas legacy: 807764 diff --git a/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol b/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol index 060cafe39a..68e4b5d62c 100644 --- a/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol +++ b/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol @@ -41,15 +41,15 @@ contract c { // ---- // storageEmpty -> 1 // test_short() -> 1780731860627700044960722568376587075150542249149356309979516913770823710 -// gas legacy: 59838 -// gas legacyOptimized: 58606 +// gas legacy: 57661 +// gas legacyOptimized: 57073 // storageEmpty -> 0 // test_long() -> 67 -// gas irOptimized: 89148 +// gas irOptimized: 89482 // gas legacy: 101607 // gas legacyOptimized: 100479 // storageEmpty -> 0 // test_pop() -> 1780731860627700044960722568376592200742329637303199754547598369979433020 -// gas legacy: 61930 -// gas legacyOptimized: 59404 +// gas legacy: 58079 +// gas legacyOptimized: 56932 // storageEmpty -> 0 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint128.sol b/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint128.sol index 761feebec9..e1cfa7ce7e 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint128.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint128.sol @@ -21,6 +21,6 @@ contract C { } // ---- // f() -> true -// gas irOptimized: 92740 -// gas legacy: 93035 -// gas legacyOptimized: 92257 +// gas irOptimized: 73992 +// gas legacy: 74395 +// gas legacyOptimized: 73794 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage_packed.sol b/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage_packed.sol index 13cb9c28fd..43827773c1 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage_packed.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage_packed.sol @@ -40,11 +40,11 @@ contract C { } // ---- // f() -> 0 -// gas irOptimized: 92800 -// gas legacy: 93006 -// gas legacyOptimized: 92261 +// gas irOptimized: 74056 +// gas legacy: 74372 +// gas legacyOptimized: 73796 // g() -> 0 // h() -> 0 -// gas irOptimized: 92862 -// gas legacy: 93028 -// gas legacyOptimized: 92303 +// gas irOptimized: 74126 +// gas legacy: 74398 +// gas legacyOptimized: 73833 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_simple.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_simple.sol index 2fccff774e..84642c1439 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_simple.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_simple.sol @@ -11,5 +11,5 @@ contract C { } // ---- // test() -> left(0x01), left(0x02) -// gas legacy: 90001 -// gas legacyOptimized: 89085 +// gas legacy: 89698 +// gas legacyOptimized: 88928 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover2.sol b/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover2.sol index 0a2bcffce2..dd49c7a97b 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover2.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover2.sol @@ -19,6 +19,6 @@ contract c { } // ---- // test() -> 0x04000000000000000000000000000000000000000000000000, 0x0, 0x0 -// gas irOptimized: 93858 -// gas legacy: 97451 -// gas legacyOptimized: 94200 +// gas irOptimized: 91348 +// gas legacy: 96648 +// gas legacyOptimized: 93841 diff --git a/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic_different_mutability.sol b/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic_different_mutability.sol index 2bb3bcf333..d121b22d49 100644 --- a/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic_different_mutability.sol +++ b/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic_different_mutability.sol @@ -52,4 +52,4 @@ contract C { // gas legacy: 108295 // gas legacyOptimized: 102162 // copyInternalArrayOfFunctionType() -> true -// gas legacy: 104178 +// gas legacy: 36192 diff --git a/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol b/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol index af2983b066..93b541620a 100644 --- a/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol @@ -10,7 +10,7 @@ contract Test { } // ---- // set(uint24[]): 0x20, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 -> 18 -// gas irOptimized: 99616 +// gas irOptimized: 95505 // gas legacy: 103509 // gas legacyOptimized: 101266 // data(uint256): 7 -> 8 diff --git a/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol b/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol index fe758fe8fd..d00f5573a0 100644 --- a/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol @@ -33,8 +33,8 @@ contract c { // gas legacy: 148692 // gas legacyOptimized: 148699 // f(uint256): 12 -> 0x20, 0x0c, 0x0102030405060708090a0b0000000000000000000000000000000000000000 -// gas legacy: 59345 -// gas legacyOptimized: 57279 +// gas legacy: 59751 +// gas legacyOptimized: 57208 // f(uint256): 129 -> 0x20, 0x81, 1780731860627700044960722568376592200742329637303199754547598369979440671, 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f, 29063324697304692433803953038474361308315562010425523193971352996434451193439, 0x606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f, -57896044618658097711785492504343953926634992332820282019728792003956564819968 // gas irOptimized: 416918 // gas legacy: 458997 diff --git a/test/libsolidity/semanticTests/array/copying/nested_array_element_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/nested_array_element_calldata_to_storage.sol index 5040a72cad..89eeb27dce 100644 --- a/test/libsolidity/semanticTests/array/copying/nested_array_element_calldata_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/nested_array_element_calldata_to_storage.sol @@ -25,5 +25,5 @@ contract C { // ---- // test(uint8[2][2][2]): 1, 2, 3, 4, 5, 6, 7, 8 // test2(uint8[2][2]): 1, 2, 3, 4 -// gas irOptimized: 119939 -// gas legacyOptimized: 120228 +// gas irOptimized: 72076 +// gas legacyOptimized: 95103 diff --git a/test/libsolidity/semanticTests/array/copying/nested_array_element_memory_to_storage.sol b/test/libsolidity/semanticTests/array/copying/nested_array_element_memory_to_storage.sol index 2f4b4ca7c9..bd74ae0158 100644 --- a/test/libsolidity/semanticTests/array/copying/nested_array_element_memory_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/nested_array_element_memory_to_storage.sol @@ -25,5 +25,5 @@ contract C { // ---- // test(uint8[2][2][2]): 1, 2, 3, 4, 5, 6, 7, 8 // test2(uint8[2][2]): 1, 2, 3, 4 -// gas irOptimized: 119939 -// gas legacyOptimized: 120228 +// gas irOptimized: 73031 +// gas legacyOptimized: 96288 diff --git a/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol b/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol index 9665201c98..f486a50cfa 100644 --- a/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol +++ b/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol @@ -14,6 +14,6 @@ contract C { } // ---- // f() -> 0, 0, 0 -// gas irOptimized: 90992 -// gas legacy: 111037 -// gas legacyOptimized: 109633 +// gas irOptimized: 88026 +// gas legacy: 88800 +// gas legacyOptimized: 87700 diff --git a/test/libsolidity/semanticTests/array/dynamic_array_cleanup.sol b/test/libsolidity/semanticTests/array/dynamic_array_cleanup.sol index b2be566a9d..285bc5f6f8 100644 --- a/test/libsolidity/semanticTests/array/dynamic_array_cleanup.sol +++ b/test/libsolidity/semanticTests/array/dynamic_array_cleanup.sol @@ -19,9 +19,9 @@ contract c { // gas legacyOptimized: 515555 // storageEmpty -> 0 // halfClear() -> -// gas irOptimized: 113961 -// gas legacy: 113257 -// gas legacyOptimized: 113120 +// gas irOptimized: 91472 +// gas legacy: 90567 +// gas legacyOptimized: 90457 // storageEmpty -> 0 // fullClear() -> // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol b/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol index ebe2186fd6..b4ba8b47fd 100644 --- a/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol +++ b/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol @@ -15,7 +15,7 @@ contract c { // gas legacyOptimized: 466238 // storageEmpty -> 0 // clear() -> -// gas irOptimized: 122148 -// gas legacy: 122440 -// gas legacyOptimized: 122259 +// gas irOptimized: 97705 +// gas legacy: 97919 +// gas legacyOptimized: 97790 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/pop/byte_array_pop_copy_long.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_copy_long.sol index c64d93c120..01d2d83f9a 100644 --- a/test/libsolidity/semanticTests/array/pop/byte_array_pop_copy_long.sol +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop_copy_long.sol @@ -9,6 +9,6 @@ contract c { } // ---- // test() -> 0x20, 29, 0x0303030303030303030303030303030303030303030303030303030303000000 -// gas irOptimized: 109310 -// gas legacy: 101220 -// gas legacyOptimized: 123422 +// gas irOptimized: 85778 +// gas legacy: 97666 +// gas legacyOptimized: 96874 diff --git a/test/libsolidity/semanticTests/array/push/array_push_packed_array.sol b/test/libsolidity/semanticTests/array/push/array_push_packed_array.sol index e7a3bdbd1f..e6c0102308 100644 --- a/test/libsolidity/semanticTests/array/push/array_push_packed_array.sol +++ b/test/libsolidity/semanticTests/array/push/array_push_packed_array.sol @@ -13,6 +13,6 @@ contract c { } // ---- // test() -> 1, 2, 3, 4 -// gas irOptimized: 93017 -// gas legacy: 92798 -// gas legacyOptimized: 92062 +// gas irOptimized: 92545 +// gas legacy: 92756 +// gas legacyOptimized: 92045 diff --git a/test/libsolidity/semanticTests/array/push/push_no_args_2d.sol b/test/libsolidity/semanticTests/array/push/push_no_args_2d.sol index 3b35719dbb..626c65871f 100644 --- a/test/libsolidity/semanticTests/array/push/push_no_args_2d.sol +++ b/test/libsolidity/semanticTests/array/push/push_no_args_2d.sol @@ -36,7 +36,7 @@ contract C { // f(uint256,uint256): 84, 128 -> // gas irOptimized: 118708 // gas legacy: 109977 -// gas legacyOptimized: 96331 +// gas legacyOptimized: 98446 // l() -> 2 // ll(uint256): 1 -> 85 // a(uint256,uint256): 0, 42 -> 64 diff --git a/test/libsolidity/semanticTests/cleanup/indexed_log_topic_during_explicit_downcast_during_emissions.sol b/test/libsolidity/semanticTests/cleanup/indexed_log_topic_during_explicit_downcast_during_emissions.sol index 742c02693d..08bfac8c27 100644 --- a/test/libsolidity/semanticTests/cleanup/indexed_log_topic_during_explicit_downcast_during_emissions.sol +++ b/test/libsolidity/semanticTests/cleanup/indexed_log_topic_during_explicit_downcast_during_emissions.sol @@ -12,6 +12,7 @@ contract C { // ---- // constructor() -> // ~ emit ev0(bytes1): #"1" -// gas legacy: 168735 +// gas legacy: 58919 +// gas legacy code: 33600 // j() -> // ~ emit ev0(bytes1): #"1" diff --git a/test/libsolidity/semanticTests/events/event_indexed_mixed.sol b/test/libsolidity/semanticTests/events/event_indexed_mixed.sol index ad753ef16f..66c7f8b5fa 100644 --- a/test/libsolidity/semanticTests/events/event_indexed_mixed.sol +++ b/test/libsolidity/semanticTests/events/event_indexed_mixed.sol @@ -11,6 +11,6 @@ contract C { // ---- // deposit() -> // ~ emit E(uint256,uint256,uint256,bytes): #0x02, 0x01, 0x03, 0x60, 0x03, "def" -// gas irOptimized: 23709 -// gas legacy: 24342 -// gas legacyOptimized: 23753 +// gas irOptimized: 23695 +// gas legacy: 24328 +// gas legacyOptimized: 23752 diff --git a/test/libsolidity/semanticTests/events/event_signature_in_library.sol b/test/libsolidity/semanticTests/events/event_signature_in_library.sol index d8dc0f1580..0991cd28a3 100644 --- a/test/libsolidity/semanticTests/events/event_signature_in_library.sol +++ b/test/libsolidity/semanticTests/events/event_signature_in_library.sol @@ -18,4 +18,5 @@ contract C { // ---- // constructor() // ~ emit E((uint8,int16),(uint8,int16)): #0xad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5, 0x00, 0x00 -// gas legacy: 150602 +// gas legacy: 62552 +// gas legacy code: 1600 diff --git a/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol b/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol index 47feaf5258..e89eeba986 100644 --- a/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol +++ b/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol @@ -82,9 +82,9 @@ contract FixedFeeRegistrar is Registrar { // gas legacyOptimized code: 388000 // reserve(string), 69 ether: 0x20, 3, "abc" -> // ~ emit Changed(string): #0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 -// gas irOptimized: 45967 -// gas legacy: 46842 -// gas legacyOptimized: 46091 +// gas irOptimized: 45741 +// gas legacy: 46698 +// gas legacyOptimized: 45948 // owner(string): 0x20, 3, "abc" -> 0x1212121212121212121212121212120000000012 // reserve(string), 70 ether: 0x20, 3, "def" -> // ~ emit Changed(string): #0x34607c9bbfeb9c23509680f04363f298fdb0b5f9abe327304ecd1daca08cda9c diff --git a/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol b/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol index cb345757f8..35e083d5c2 100644 --- a/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol +++ b/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol @@ -55,42 +55,42 @@ contract test { // gas legacyOptimized: 178012 // gas legacyOptimized code: 1669600 // div(int256,int256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 -// gas irOptimized: 22137 -// gas legacy: 22767 -// gas legacyOptimized: 22282 +// gas irOptimized: 22045 +// gas legacy: 22736 +// gas legacyOptimized: 22264 // exp(int256): 3141592653589793238 -> 23140692632779268978 -// gas irOptimized: 24545 -// gas legacy: 25203 -// gas legacyOptimized: 24357 +// gas irOptimized: 24452 +// gas legacy: 25124 +// gas legacyOptimized: 24351 // exp2(int256): 3141592653589793238 -> 8824977827076287620 -// gas irOptimized: 24257 -// gas legacy: 24864 -// gas legacyOptimized: 24110 +// gas irOptimized: 24162 +// gas legacy: 24787 +// gas legacyOptimized: 24105 // gm(int256,int256): 3141592653589793238, 88714123 -> 16694419339601 -// gas irOptimized: 22970 -// gas legacy: 23228 -// gas legacyOptimized: 22683 +// gas irOptimized: 22805 +// gas legacy: 23202 +// gas legacyOptimized: 22685 // log10(int256): 3141592653589793238 -> 4971498726941338506 -// gas irOptimized: 30609 -// gas legacy: 32934 -// gas legacyOptimized: 30323 +// gas irOptimized: 30300 +// gas legacy: 32841 +// gas legacyOptimized: 30249 // log2(int256): 3141592653589793238 -> 1651496129472318782 -// gas irOptimized: 28819 -// gas legacy: 31067 -// gas legacyOptimized: 28426 +// gas irOptimized: 28566 +// gas legacy: 30979 +// gas legacyOptimized: 28357 // mul(int256,int256): 3141592653589793238, 88714123 -> 278703637 -// gas irOptimized: 22225 -// gas legacy: 22807 -// gas legacyOptimized: 22295 +// gas irOptimized: 22147 +// gas legacy: 22775 +// gas legacyOptimized: 22288 // pow(int256,uint256): 3141592653589793238, 5 -> 306019684785281453040 -// gas irOptimized: 22635 -// gas legacy: 23508 +// gas irOptimized: 22488 +// gas legacy: 23453 // gas legacyOptimized: 22921 // sqrt(int256): 3141592653589793238 -> 1772453850905516027 -// gas irOptimized: 22650 -// gas legacy: 22802 -// gas legacyOptimized: 22422 +// gas irOptimized: 22458 +// gas legacy: 22784 +// gas legacyOptimized: 22420 // benchmark(int256): 3141592653589793238 -> 998882724338592125, 1000000000000000000, 1000000000000000000 -// gas irOptimized: 36630 -// gas legacy: 36673 -// gas legacyOptimized: 34729 +// gas irOptimized: 34893 +// gas legacy: 35244 +// gas legacyOptimized: 33996 diff --git a/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol index 5902ba4905..38d2a20baa 100644 --- a/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol +++ b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol @@ -55,42 +55,42 @@ contract test { // gas legacyOptimized: 168857 // gas legacyOptimized code: 1556200 // div(uint256,uint256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 -// gas irOptimized: 22004 -// gas legacy: 22497 -// gas legacyOptimized: 22010 +// gas irOptimized: 21912 +// gas legacy: 22475 +// gas legacyOptimized: 21998 // exp(uint256): 3141592653589793238 -> 23140692632779268978 -// gas irOptimized: 24444 -// gas legacy: 25104 -// gas legacyOptimized: 24258 +// gas irOptimized: 24334 +// gas legacy: 25026 +// gas legacyOptimized: 24253 // exp2(uint256): 3141592653589793238 -> 8824977827076287620 -// gas irOptimized: 24198 -// gas legacy: 24814 -// gas legacyOptimized: 24062 +// gas irOptimized: 24115 +// gas legacy: 24738 +// gas legacyOptimized: 24058 // gm(uint256,uint256): 3141592653589793238, 88714123 -> 16694419339601 -// gas irOptimized: 22950 -// gas legacy: 23269 -// gas legacyOptimized: 22724 +// gas irOptimized: 22786 +// gas legacy: 23244 +// gas legacyOptimized: 22727 // log10(uint256): 3141592653589793238 -> 0x44fe4fc084a52b8a -// gas irOptimized: 30269 -// gas legacy: 32898 -// gas legacyOptimized: 29925 +// gas irOptimized: 30079 +// gas legacy: 32808 +// gas legacyOptimized: 29912 // log2(uint256): 3141592653589793238 -> 1651496129472318782 -// gas irOptimized: 28235 -// gas legacy: 30986 -// gas legacyOptimized: 28001 +// gas irOptimized: 28107 +// gas legacy: 30900 +// gas legacyOptimized: 27994 // mul(uint256,uint256): 3141592653589793238, 88714123 -> 278703637 -// gas irOptimized: 22048 -// gas legacy: 22604 -// gas legacyOptimized: 22090 +// gas irOptimized: 21988 +// gas legacy: 22581 +// gas legacyOptimized: 22089 // pow(uint256,uint256): 3141592653589793238, 5 -> 306019684785281453040 -// gas irOptimized: 22406 -// gas legacy: 23245 -// gas legacyOptimized: 22646 +// gas irOptimized: 22316 +// gas legacy: 23196 +// gas legacyOptimized: 22652 // sqrt(uint256): 3141592653589793238 -> 1772453850905516027 -// gas irOptimized: 22672 -// gas legacy: 22820 -// gas legacyOptimized: 22440 +// gas irOptimized: 22481 +// gas legacy: 22803 +// gas legacyOptimized: 22439 // benchmark(uint256): 3141592653589793238 -> 998882724338592125, 1000000000000000000, 1000000000000000000 -// gas irOptimized: 35603 -// gas legacy: 35385 -// gas legacyOptimized: 33449 +// gas irOptimized: 33961 +// gas legacy: 34006 +// gas legacyOptimized: 32724 diff --git a/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol b/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol index 70a3004ac4..a98e83001a 100644 --- a/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol +++ b/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol @@ -40,6 +40,6 @@ contract test { // gas legacyOptimized: 82667 // gas legacyOptimized code: 369200 // prb_pi() -> 3141592656369545286 -// gas irOptimized: 57478 +// gas irOptimized: 55036 // gas legacy: 100657 -// gas legacyOptimized: 75735 +// gas legacyOptimized: 74311 diff --git a/test/libsolidity/semanticTests/externalContracts/strings.sol b/test/libsolidity/semanticTests/externalContracts/strings.sol index cb9616bbe4..8deb86ed51 100644 --- a/test/libsolidity/semanticTests/externalContracts/strings.sol +++ b/test/libsolidity/semanticTests/externalContracts/strings.sol @@ -56,21 +56,21 @@ contract test { // gas legacyOptimized: 102639 // gas legacyOptimized code: 612400 // toSlice(string): 0x20, 11, "hello world" -> 11, 0xa0 -// gas irOptimized: 22660 -// gas legacy: 23190 -// gas legacyOptimized: 22508 +// gas irOptimized: 22646 +// gas legacy: 23168 +// gas legacyOptimized: 22488 // roundtrip(string): 0x20, 11, "hello world" -> 0x20, 11, "hello world" -// gas irOptimized: 23408 -// gas legacy: 23820 -// gas legacyOptimized: 23123 +// gas irOptimized: 23225 +// gas legacy: 23674 +// gas legacyOptimized: 22991 // utf8len(string): 0x20, 16, "\xf0\x9f\x98\x83\xf0\x9f\x98\x83\xf0\x9f\x98\x83\xf0\x9f\x98\x83" -> 4 # Input: "😃😃😃😃" # -// gas irOptimized: 24026 -// gas legacy: 25716 -// gas legacyOptimized: 24115 +// gas irOptimized: 23956 +// gas legacy: 25621 +// gas legacyOptimized: 24096 // multiconcat(string,uint256): 0x40, 3, 11, "hello world" -> 0x20, 0x58, 0x68656c6c6f20776f726c6468656c6c6f20776f726c6468656c6c6f20776f726c, 0x6468656c6c6f20776f726c6468656c6c6f20776f726c6468656c6c6f20776f72, 49027192869463622675296414541903001712009715982962058146354235762728281047040 # concatenating 3 times # -// gas irOptimized: 28440 -// gas legacy: 31621 -// gas legacyOptimized: 27914 +// gas irOptimized: 27630 +// gas legacy: 30778 +// gas legacyOptimized: 27519 // benchmark(string,bytes32): 0x40, 0x0842021, 8, "solidity" -> 0x2020 // gas irOptimized: 1976778 // gas legacy: 4234020 diff --git a/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol b/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol index 94a7213232..8eeaf7b51c 100644 --- a/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol +++ b/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol @@ -15,8 +15,10 @@ contract Main { } // ---- // constructor(), 20 wei -> -// gas irOptimized: 100264 +// gas irOptimized: 56314 +// gas irOptimized code: 37400 // gas legacy: 57555 // gas legacy code: 53000 -// gas legacyOptimized: 100361 +// gas legacyOptimized: 56463 +// gas legacyOptimized code: 39600 // s() -> true diff --git a/test/libsolidity/semanticTests/functionTypes/store_function.sol b/test/libsolidity/semanticTests/functionTypes/store_function.sol index eeab740aa0..904bc705f7 100644 --- a/test/libsolidity/semanticTests/functionTypes/store_function.sol +++ b/test/libsolidity/semanticTests/functionTypes/store_function.sol @@ -25,7 +25,8 @@ contract C { } // ---- // t() -> 9 -// gas irOptimized: 99064 +// gas irOptimized: 77300 +// gas irOptimized code: 19000 // gas legacy: 79492 // gas legacy code: 69600 // gas legacyOptimized: 77587 diff --git a/test/libsolidity/semanticTests/inheritance/address_overload_resolution.sol b/test/libsolidity/semanticTests/inheritance/address_overload_resolution.sol index 90a3f9ec8c..f339b52445 100644 --- a/test/libsolidity/semanticTests/inheritance/address_overload_resolution.sol +++ b/test/libsolidity/semanticTests/inheritance/address_overload_resolution.sol @@ -22,10 +22,12 @@ contract D { // EVMVersion: >=constantinople // ---- // f() -> 1 -// gas irOptimized: 77051 +// gas irOptimized: 54014 +// gas irOptimized code: 20200 // gas legacy: 54553 // gas legacy code: 57800 // g() -> 5 -// gas irOptimized: 77106 +// gas irOptimized: 54042 +// gas irOptimized code: 20200 // gas legacy: 55090 // gas legacy code: 57800 diff --git a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_calldata_interface.sol b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_calldata_interface.sol index f64a2e12a1..e9013d6f52 100644 --- a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_calldata_interface.sol +++ b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_calldata_interface.sol @@ -22,6 +22,7 @@ contract B { } // ---- // g() -> 42 -// gas irOptimized: 80813 +// gas irOptimized: 54536 +// gas irOptimized code: 24800 // gas legacy: 55868 // gas legacy code: 66600 diff --git a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol index 07beb2c537..36ed370624 100644 --- a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol +++ b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol @@ -22,7 +22,8 @@ contract B { } // ---- // g() -> 42 -// gas irOptimized: 100282 +// gas irOptimized: 54828 +// gas irOptimized code: 42000 // gas legacy: 56839 // gas legacy code: 123600 // gas legacyOptimized: 55001 diff --git a/test/libsolidity/semanticTests/inheritance/interface_inheritance_conversions.sol b/test/libsolidity/semanticTests/inheritance/interface_inheritance_conversions.sol index 2448b3e03b..95f48f60a1 100644 --- a/test/libsolidity/semanticTests/inheritance/interface_inheritance_conversions.sol +++ b/test/libsolidity/semanticTests/inheritance/interface_inheritance_conversions.sol @@ -29,10 +29,10 @@ contract C is SubA, SubB { } // ---- // convertParent() -> 1 -// gas irOptimized: 85524 +// gas irOptimized: 21793 // convertSubA() -> 1, 2 -// gas irOptimized: 86155 -// gas legacy: 99047 +// gas irOptimized: 22459 +// gas legacy: 23286 // convertSubB() -> 1, 3 -// gas irOptimized: 86098 -// gas legacy: 98981 +// gas irOptimized: 22330 +// gas legacy: 23154 diff --git a/test/libsolidity/semanticTests/inlineAssembly/keccak_yul_optimization.sol b/test/libsolidity/semanticTests/inlineAssembly/keccak_yul_optimization.sol index 88088bbda8..e3344bf5d5 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/keccak_yul_optimization.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/keccak_yul_optimization.sol @@ -24,10 +24,10 @@ contract C { } // ---- // f() -> 0xcdb56c384a9682c600315e3470157a4cf7638d0d33e9dae5c40ffd2644fc5a80 -// gas irOptimized: 22239 -// gas legacy: 23385 -// gas legacyOptimized: 23092 +// gas irOptimized: 22212 +// gas legacy: 23378 +// gas legacyOptimized: 23089 // g() -> 0xcdb56c384a9682c600315e3470157a4cf7638d0d33e9dae5c40ffd2644fc5a80 -// gas irOptimized: 21277 -// gas legacy: 21462 -// gas legacyOptimized: 21256 +// gas irOptimized: 21251 +// gas legacy: 21456 +// gas legacyOptimized: 21254 diff --git a/test/libsolidity/semanticTests/libraries/using_library_structs.sol b/test/libsolidity/semanticTests/libraries/using_library_structs.sol index c2ecf1036e..15398e312d 100644 --- a/test/libsolidity/semanticTests/libraries/using_library_structs.sol +++ b/test/libsolidity/semanticTests/libraries/using_library_structs.sol @@ -20,5 +20,5 @@ contract Test { // ---- // library: Lib // f() -> 7, 8 -// gas irOptimized: 101818 +// gas irOptimized: 97577 // gas legacy: 101428 diff --git a/test/libsolidity/semanticTests/operators/userDefined/operator_making_pure_external_call.sol b/test/libsolidity/semanticTests/operators/userDefined/operator_making_pure_external_call.sol index 364e589e55..b901cab035 100644 --- a/test/libsolidity/semanticTests/operators/userDefined/operator_making_pure_external_call.sol +++ b/test/libsolidity/semanticTests/operators/userDefined/operator_making_pure_external_call.sol @@ -54,13 +54,15 @@ contract C { // EVMVersion: >=constantinople // ---- // testMul(int32,int32): 42, 10 -> 420 -// gas irOptimized: 102563 +// gas irOptimized: 54784 +// gas irOptimized code: 40600 // gas legacy: 57117 // gas legacy code: 127000 // gas legacyOptimized: 55246 // gas legacyOptimized code: 68400 // testInc(int32): 42 -> 43 -// gas irOptimized: 102386 +// gas irOptimized: 54621 +// gas irOptimized code: 40600 // gas legacy: 56378 // gas legacy code: 127000 // gas legacyOptimized: 54943 diff --git a/test/libsolidity/semanticTests/operators/userDefined/operator_making_view_external_call.sol b/test/libsolidity/semanticTests/operators/userDefined/operator_making_view_external_call.sol index 54048113c3..15501b3fc2 100644 --- a/test/libsolidity/semanticTests/operators/userDefined/operator_making_view_external_call.sol +++ b/test/libsolidity/semanticTests/operators/userDefined/operator_making_view_external_call.sol @@ -60,13 +60,15 @@ contract C { // EVMVersion: >=constantinople // ---- // testMul(int32,int32): 42, 10 -> 420 -// gas irOptimized: 102563 +// gas irOptimized: 54784 +// gas irOptimized code: 40600 // gas legacy: 57117 // gas legacy code: 127000 // gas legacyOptimized: 55246 // gas legacyOptimized code: 68400 // testInc(int32): 42 -> 43 -// gas irOptimized: 102386 +// gas irOptimized: 54621 +// gas irOptimized code: 40600 // gas legacy: 56378 // gas legacy code: 127000 // gas legacyOptimized: 54943 diff --git a/test/libsolidity/semanticTests/structs/struct_delete_storage_nested_small.sol b/test/libsolidity/semanticTests/structs/struct_delete_storage_nested_small.sol index 57bf38e52c..a8e29dcad8 100644 --- a/test/libsolidity/semanticTests/structs/struct_delete_storage_nested_small.sol +++ b/test/libsolidity/semanticTests/structs/struct_delete_storage_nested_small.sol @@ -33,4 +33,4 @@ contract C { // compileViaYul: true // ---- // f() -> 0, 0, 0 -// gas irOptimized: 117101 +// gas irOptimized: 93830 diff --git a/test/libsolidity/semanticTests/structs/struct_delete_storage_with_arrays_small.sol b/test/libsolidity/semanticTests/structs/struct_delete_storage_with_arrays_small.sol index 2345fea3cf..871c614f56 100644 --- a/test/libsolidity/semanticTests/structs/struct_delete_storage_with_arrays_small.sol +++ b/test/libsolidity/semanticTests/structs/struct_delete_storage_with_arrays_small.sol @@ -27,4 +27,4 @@ contract C { // compileViaYul: true // ---- // f() -> 0 -// gas irOptimized: 111570 +// gas irOptimized: 89435 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/calldata_to_storage.sol b/test/libsolidity/semanticTests/userDefinedValueType/calldata_to_storage.sol index c3d58811ae..292b9fbfe3 100644 --- a/test/libsolidity/semanticTests/userDefinedValueType/calldata_to_storage.sol +++ b/test/libsolidity/semanticTests/userDefinedValueType/calldata_to_storage.sol @@ -23,19 +23,19 @@ contract C { // ---- // s() -> 0, 0, 0x00, 0 // f((uint8,uint16,bytes2,uint8)): 1, 0xff, "ab", 15 -> -// gas irOptimized: 44405 -// gas legacy: 47200 -// gas legacyOptimized: 44923 +// gas irOptimized: 44237 +// gas legacy: 47154 +// gas legacyOptimized: 45018 // s() -> 1, 0xff, 0x6162000000000000000000000000000000000000000000000000000000000000, 15 // g(uint16[]): 0x20, 3, 1, 2, 3 -> 0x20, 3, 1, 2, 3 -// gas irOptimized: 69097 -// gas legacy: 75466 -// gas legacyOptimized: 74255 +// gas irOptimized: 68578 +// gas legacy: 75429 +// gas legacyOptimized: 74244 // small(uint256): 0 -> 1 // small(uint256): 1 -> 2 // h(bytes2[]): 0x20, 3, "ab", "cd", "ef" -> 0x20, 3, "ab", "cd", "ef" -// gas irOptimized: 69174 -// gas legacy: 75156 -// gas legacyOptimized: 74342 +// gas irOptimized: 68635 +// gas legacy: 75125 +// gas legacyOptimized: 74331 // l(uint256): 0 -> 0x6162000000000000000000000000000000000000000000000000000000000000 // l(uint256): 1 -> 0x6364000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/erc20.sol b/test/libsolidity/semanticTests/userDefinedValueType/erc20.sol index 0e0759d62a..acd9ea8408 100644 --- a/test/libsolidity/semanticTests/userDefinedValueType/erc20.sol +++ b/test/libsolidity/semanticTests/userDefinedValueType/erc20.sol @@ -120,29 +120,29 @@ contract ERC20 { // gas legacyOptimized: 127464 // gas legacyOptimized code: 285400 // totalSupply() -> 20 -// gas irOptimized: 23415 -// gas legacy: 23653 -// gas legacyOptimized: 23368 +// gas irOptimized: 23334 +// gas legacy: 23645 +// gas legacyOptimized: 23367 // transfer(address,uint256): 2, 5 -> true // ~ emit Transfer(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x05 -// gas irOptimized: 48471 -// gas legacy: 49572 -// gas legacyOptimized: 48575 +// gas irOptimized: 50997 +// gas legacy: 52323 +// gas legacyOptimized: 51380 // decreaseAllowance(address,uint256): 2, 0 -> true // ~ emit Approval(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x00 -// gas irOptimized: 26275 -// gas legacy: 27204 -// gas legacyOptimized: 26317 +// gas irOptimized: 26054 +// gas legacy: 27165 +// gas legacyOptimized: 26320 // decreaseAllowance(address,uint256): 2, 1 -> FAILURE, hex"4e487b71", 0x11 -// gas irOptimized: 24042 -// gas legacy: 24506 -// gas legacyOptimized: 24077 +// gas irOptimized: 23954 +// gas legacy: 24500 +// gas legacyOptimized: 24076 // transfer(address,uint256): 2, 14 -> true // ~ emit Transfer(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x0e -// gas irOptimized: 28571 -// gas legacy: 29672 -// gas legacyOptimized: 28675 +// gas irOptimized: 33897 +// gas legacy: 35223 +// gas legacyOptimized: 34280 // transfer(address,uint256): 2, 2 -> FAILURE, hex"4e487b71", 0x11 -// gas irOptimized: 24071 -// gas legacy: 24492 -// gas legacyOptimized: 24074 +// gas irOptimized: 23990 +// gas legacy: 24483 +// gas legacyOptimized: 24072 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/memory_to_storage.sol b/test/libsolidity/semanticTests/userDefinedValueType/memory_to_storage.sol index db745dce31..585249f2f5 100644 --- a/test/libsolidity/semanticTests/userDefinedValueType/memory_to_storage.sol +++ b/test/libsolidity/semanticTests/userDefinedValueType/memory_to_storage.sol @@ -23,19 +23,19 @@ contract C { // ---- // s() -> 0, 0, 0x00, 0 // f((uint8,uint16,bytes2,uint8)): 1, 0xff, "ab", 15 -> -// gas irOptimized: 44473 -// gas legacy: 46213 -// gas legacyOptimized: 44671 +// gas irOptimized: 44360 +// gas legacy: 46189 +// gas legacyOptimized: 44673 // s() -> 1, 0xff, 0x6162000000000000000000000000000000000000000000000000000000000000, 15 // g(uint16[]): 0x20, 3, 1, 2, 3 -> 0x20, 3, 1, 2, 3 -// gas irOptimized: 69555 -// gas legacy: 76557 -// gas legacyOptimized: 74834 +// gas irOptimized: 68898 +// gas legacy: 76511 +// gas legacyOptimized: 74832 // small(uint256): 0 -> 1 // small(uint256): 1 -> 2 // h(bytes2[]): 0x20, 3, "ab", "cd", "ef" -> 0x20, 3, "ab", "cd", "ef" -// gas irOptimized: 69617 -// gas legacy: 76238 -// gas legacyOptimized: 74921 +// gas irOptimized: 68937 +// gas legacy: 76198 +// gas legacyOptimized: 74919 // l(uint256): 0 -> 0x6162000000000000000000000000000000000000000000000000000000000000 // l(uint256): 1 -> 0x6364000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/zero_cost_abstraction_comparison_elementary.sol b/test/libsolidity/semanticTests/userDefinedValueType/zero_cost_abstraction_comparison_elementary.sol index 2c5a15bb93..c254d9abdd 100644 --- a/test/libsolidity/semanticTests/userDefinedValueType/zero_cost_abstraction_comparison_elementary.sol +++ b/test/libsolidity/semanticTests/userDefinedValueType/zero_cost_abstraction_comparison_elementary.sol @@ -17,18 +17,18 @@ contract C { } // ---- // getX() -> 0 -// gas irOptimized: 23379 -// gas legacy: 23479 -// gas legacyOptimized: 23311 +// gas irOptimized: 23311 +// gas legacy: 23473 +// gas legacyOptimized: 23309 // setX(int256): 5 -> -// gas irOptimized: 43510 -// gas legacy: 43724 -// gas legacyOptimized: 43516 +// gas irOptimized: 43464 +// gas legacy: 43718 +// gas legacyOptimized: 43513 // getX() -> 5 -// gas irOptimized: 23379 -// gas legacy: 23479 -// gas legacyOptimized: 23311 +// gas irOptimized: 23311 +// gas legacy: 23473 +// gas legacyOptimized: 23309 // add(int256,int256): 200, 99 -> 299 -// gas irOptimized: 21764 -// gas legacy: 22394 -// gas legacyOptimized: 21813 +// gas irOptimized: 21623 +// gas legacy: 22354 +// gas legacyOptimized: 21768 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/zero_cost_abstraction_comparison_userdefined.sol b/test/libsolidity/semanticTests/userDefinedValueType/zero_cost_abstraction_comparison_userdefined.sol index 2f275c543b..89f811862f 100644 --- a/test/libsolidity/semanticTests/userDefinedValueType/zero_cost_abstraction_comparison_userdefined.sol +++ b/test/libsolidity/semanticTests/userDefinedValueType/zero_cost_abstraction_comparison_userdefined.sol @@ -18,18 +18,18 @@ contract C { } // ---- // getX() -> 0 -// gas irOptimized: 23379 -// gas legacy: 23608 -// gas legacyOptimized: 23311 +// gas irOptimized: 23311 +// gas legacy: 23599 +// gas legacyOptimized: 23309 // setX(int256): 5 -> -// gas irOptimized: 43510 -// gas legacy: 43724 -// gas legacyOptimized: 43516 +// gas irOptimized: 43464 +// gas legacy: 43718 +// gas legacyOptimized: 43513 // getX() -> 5 -// gas irOptimized: 23379 -// gas legacy: 23608 -// gas legacyOptimized: 23311 +// gas irOptimized: 23311 +// gas legacy: 23599 +// gas legacyOptimized: 23309 // add(int256,int256): 200, 99 -> 299 -// gas irOptimized: 21764 -// gas legacy: 22523 -// gas legacyOptimized: 21813 +// gas irOptimized: 21623 +// gas legacy: 22480 +// gas legacyOptimized: 21768 diff --git a/test/libsolidity/semanticTests/various/codebalance_assembly.sol b/test/libsolidity/semanticTests/various/codebalance_assembly.sol index fad0d3e5b4..6faffd6399 100644 --- a/test/libsolidity/semanticTests/various/codebalance_assembly.sol +++ b/test/libsolidity/semanticTests/various/codebalance_assembly.sol @@ -21,7 +21,8 @@ contract C { // EVMVersion: >=constantinople // ---- // constructor(), 23 wei -> -// gas legacy: 100517 +// gas legacy: 56667 +// gas legacy code: 42000 // f() -> 0 // g() -> 1 // h() -> 23 diff --git a/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol b/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol index 0ebf772fba..d1fae4b7fa 100644 --- a/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol +++ b/test/libsolidity/semanticTests/various/contract_binary_dependencies.sol @@ -17,4 +17,5 @@ contract C { } // ---- // constructor() -> -// gas irOptimized: 100415 +// gas irOptimized: 56611 +// gas irOptimized code: 39400 diff --git a/test/libsolidity/semanticTests/various/erc20.sol b/test/libsolidity/semanticTests/various/erc20.sol index 1cc77270f6..13b914d9b8 100644 --- a/test/libsolidity/semanticTests/various/erc20.sol +++ b/test/libsolidity/semanticTests/various/erc20.sol @@ -103,29 +103,29 @@ contract ERC20 { // gas legacyOptimized: 126934 // gas legacyOptimized code: 282000 // totalSupply() -> 20 -// gas irOptimized: 23415 -// gas legacy: 23524 -// gas legacyOptimized: 23368 +// gas irOptimized: 23334 +// gas legacy: 23519 +// gas legacyOptimized: 23367 // transfer(address,uint256): 2, 5 -> true // ~ emit Transfer(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x05 -// gas irOptimized: 48471 -// gas legacy: 49317 -// gas legacyOptimized: 48491 +// gas irOptimized: 51054 +// gas legacy: 52073 +// gas legacyOptimized: 51298 // decreaseAllowance(address,uint256): 2, 0 -> true // ~ emit Approval(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x00 -// gas irOptimized: 26275 -// gas legacy: 27012 -// gas legacyOptimized: 26275 +// gas irOptimized: 26087 +// gas legacy: 26977 +// gas legacyOptimized: 26279 // decreaseAllowance(address,uint256): 2, 1 -> FAILURE, hex"4e487b71", 0x11 -// gas irOptimized: 24042 -// gas legacy: 24467 +// gas irOptimized: 23981 +// gas legacy: 24462 // gas legacyOptimized: 24056 // transfer(address,uint256): 2, 14 -> true // ~ emit Transfer(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x0e -// gas irOptimized: 28571 -// gas legacy: 29417 -// gas legacyOptimized: 28591 +// gas irOptimized: 33954 +// gas legacy: 34973 +// gas legacyOptimized: 34198 // transfer(address,uint256): 2, 2 -> FAILURE, hex"4e487b71", 0x11 -// gas irOptimized: 24071 -// gas legacy: 24453 -// gas legacyOptimized: 24053 +// gas irOptimized: 24020 +// gas legacy: 24445 +// gas legacyOptimized: 24052 From da092b5b2640b74df6ed20aa404e3e3f4eddff4e Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Tue, 16 Dec 2025 17:00:02 +0100 Subject: [PATCH 107/243] Move constantinople and petersburg runs to nightly --- .circleci/config.yml | 2 +- .circleci/soltest_all.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 69ae1999fc..3cab7fbadb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1328,7 +1328,7 @@ jobs: - attach_workspace: at: build - run_soltest_all: - evm_versions: homestead byzantium + evm_versions: homestead byzantium constantinople petersburg - store_test_results: path: test_results/ - store_artifacts_test_results diff --git a/.circleci/soltest_all.sh b/.circleci/soltest_all.sh index 8d6fc486b8..effbc33da0 100755 --- a/.circleci/soltest_all.sh +++ b/.circleci/soltest_all.sh @@ -31,7 +31,7 @@ REPODIR="$(realpath "$(dirname "$0")"/..)" # shellcheck source=scripts/common.sh source "${REPODIR}/scripts/common.sh" -DEFAULT_EVM_VALUES=(constantinople petersburg istanbul berlin london paris shanghai cancun prague osaka) +DEFAULT_EVM_VALUES=(istanbul berlin london paris shanghai cancun prague osaka) EVMS_WITH_EOF=(osaka) # Deserialize the EVM_VALUES array if it was provided as argument or From eaaba5f4a581605d0ecdf52c910792ccc5f6aa90 Mon Sep 17 00:00:00 2001 From: rodiazet Date: Mon, 8 Dec 2025 17:17:00 +0100 Subject: [PATCH 108/243] Apply fix --- libsolidity/codegen/ExpressionCompiler.cpp | 1 + libsolidity/codegen/ir/IRGeneratorForStatements.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 85634906e0..2927444e2b 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -2133,6 +2133,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) dynamic_cast(_memberAccess.annotation().referencedDeclaration) || dynamic_cast(_memberAccess.annotation().referencedDeclaration) || dynamic_cast(_memberAccess.annotation().referencedDeclaration) || + dynamic_cast(_memberAccess.annotation().referencedDeclaration) || category == Type::Category::TypeType || category == Type::Category::Module, "" diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 5902cfef68..66736da5f4 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -2252,6 +2252,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) dynamic_cast(_memberAccess.annotation().referencedDeclaration) || dynamic_cast(_memberAccess.annotation().referencedDeclaration) || dynamic_cast(_memberAccess.annotation().referencedDeclaration) || + dynamic_cast(_memberAccess.annotation().referencedDeclaration) || category == Type::Category::TypeType || category == Type::Category::Module, "" From 910350b0609a5df76bf26807da840dcbe675d704 Mon Sep 17 00:00:00 2001 From: rodiazet Date: Mon, 8 Dec 2025 17:17:09 +0100 Subject: [PATCH 109/243] Add tests --- ...t_event_from_module_via_member_access.json | 1046 +++++++++++++++++ ...it_event_from_module_via_member_access.sol | 18 + .../error_from_module_via_member_access.json | 544 +++++++++ .../error_from_module_via_member_access.sol | 22 + ...it_event_from_module_via_member_access.sol | 115 ++ .../error_from_module_via_member_access.sol | 127 ++ ...or_throw_from_module_via_member_access.sol | 31 + ...ent_emit_from_module_via_member_access.sol | 26 + 8 files changed, 1929 insertions(+) create mode 100644 test/libsolidity/ASTJSON/emit_event_from_module_via_member_access.json create mode 100644 test/libsolidity/ASTJSON/emit_event_from_module_via_member_access.sol create mode 100644 test/libsolidity/ASTJSON/error_from_module_via_member_access.json create mode 100644 test/libsolidity/ASTJSON/error_from_module_via_member_access.sol create mode 100644 test/libsolidity/natspecJSON/emit_event_from_module_via_member_access.sol create mode 100644 test/libsolidity/natspecJSON/error_from_module_via_member_access.sol create mode 100644 test/libsolidity/semanticTests/errors/error_throw_from_module_via_member_access.sol create mode 100644 test/libsolidity/semanticTests/events/event_emit_from_module_via_member_access.sol diff --git a/test/libsolidity/ASTJSON/emit_event_from_module_via_member_access.json b/test/libsolidity/ASTJSON/emit_event_from_module_via_member_access.json new file mode 100644 index 0000000000..a33847450b --- /dev/null +++ b/test/libsolidity/ASTJSON/emit_event_from_module_via_member_access.json @@ -0,0 +1,1046 @@ +[ +{ + "absolutePath": "A.sol", + "exportedSymbols": { + "AContract": [ + 17 + ], + "TransferA": [ + 8 + ] + }, + "id": 18, + "nodeType": "SourceUnit", + "nodes": [ + { + "anonymous": false, + "eventSelector": "ef0684aa87ada311bea2dad2b653ce28cd604aa1c10a123df8fb2f789c3178f0", + "id": 8, + "name": "TransferA", + "nameLocation": "6:9:1", + "nodeType": "EventDefinition", + "parameters": { + "id": 7, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2, + "indexed": true, + "mutability": "mutable", + "name": "from", + "nameLocation": "32:4:1", + "nodeType": "VariableDeclaration", + "scope": 8, + "src": "16:20:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 1, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "16:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 4, + "indexed": true, + "mutability": "mutable", + "name": "to", + "nameLocation": "54:2:1", + "nodeType": "VariableDeclaration", + "scope": 8, + "src": "38:18:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 3, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "38:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 6, + "indexed": false, + "mutability": "mutable", + "name": "amount", + "nameLocation": "63:6:1", + "nodeType": "VariableDeclaration", + "scope": 8, + "src": "58:11:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 5, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "58:4:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "15:55:1" + }, + "src": "0:71:1" + }, + { + "abstract": false, + "baseContracts": [], + "canonicalName": "AContract", + "contractDependencies": [], + "contractKind": "contract", + "fullyImplemented": true, + "id": 17, + "linearizedBaseContracts": [ + 17 + ], + "name": "AContract", + "nameLocation": "81:9:1", + "nodeType": "ContractDefinition", + "nodes": [ + { + "anonymous": false, + "eventSelector": "665f5beb8eab22c2d0fba9f14e33a83ad719ffbed2f484458c23490e8577adc9", + "id": 16, + "name": "TransferAContract", + "nameLocation": "103:17:1", + "nodeType": "EventDefinition", + "parameters": { + "id": 15, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 10, + "indexed": true, + "mutability": "mutable", + "name": "from", + "nameLocation": "137:4:1", + "nodeType": "VariableDeclaration", + "scope": 16, + "src": "121:20:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 9, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "121:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 12, + "indexed": true, + "mutability": "mutable", + "name": "to", + "nameLocation": "159:2:1", + "nodeType": "VariableDeclaration", + "scope": 16, + "src": "143:18:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 11, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "143:7:1", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 14, + "indexed": false, + "mutability": "mutable", + "name": "amount", + "nameLocation": "168:6:1", + "nodeType": "VariableDeclaration", + "scope": 16, + "src": "163:11:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 13, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "163:4:1", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "120:55:1" + }, + "src": "97:79:1" + } + ], + "scope": 18, + "src": "72:106:1", + "usedErrors": [], + "usedEvents": [ + 16 + ] + } + ], + "src": "0:179:1" +}, +{ + "absolutePath": "ERC20.sol", + "exportedSymbols": { + "A": [ + 19 + ], + "ERC20": [ + 74 + ] + }, + "id": 75, + "nodeType": "SourceUnit", + "nodes": [ + { + "absolutePath": "A.sol", + "file": "A.sol", + "id": 19, + "nameLocation": "12:1:2", + "nodeType": "ImportDirective", + "scope": 75, + "sourceUnit": 18, + "src": "0:27:2", + "symbolAliases": [], + "unitAlias": "A" + }, + { + "abstract": false, + "baseContracts": [], + "canonicalName": "ERC20", + "contractDependencies": [], + "contractKind": "contract", + "fullyImplemented": true, + "id": 74, + "linearizedBaseContracts": [ + 74 + ], + "name": "ERC20", + "nameLocation": "37:5:2", + "nodeType": "ContractDefinition", + "nodes": [ + { + "anonymous": false, + "eventSelector": "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "id": 27, + "name": "Transfer", + "nameLocation": "55:8:2", + "nodeType": "EventDefinition", + "parameters": { + "id": 26, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 21, + "indexed": true, + "mutability": "mutable", + "name": "from", + "nameLocation": "80:4:2", + "nodeType": "VariableDeclaration", + "scope": 27, + "src": "64:20:2", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 20, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "64:7:2", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 23, + "indexed": true, + "mutability": "mutable", + "name": "to", + "nameLocation": "102:2:2", + "nodeType": "VariableDeclaration", + "scope": 27, + "src": "86:18:2", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + }, + "typeName": { + "id": 22, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "86:7:2", + "stateMutability": "nonpayable", + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 25, + "indexed": false, + "mutability": "mutable", + "name": "amount", + "nameLocation": "111:6:2", + "nodeType": "VariableDeclaration", + "scope": 27, + "src": "106:11:2", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + }, + "typeName": { + "id": 24, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "106:4:2", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "visibility": "internal" + } + ], + "src": "63:55:2" + }, + "src": "49:70:2" + }, + { + "body": { + "id": 72, + "nodeType": "Block", + "src": "145:181:2", + "statements": [ + { + "eventCall": { + "arguments": [ + { + "arguments": [ + { + "hexValue": "31", + "id": 33, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "177:1:2", + "typeDescriptions": { + "typeIdentifier": "t_rational_1_by_1", + "typeString": "int_const 1" + }, + "value": "1" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_rational_1_by_1", + "typeString": "int_const 1" + } + ], + "id": 32, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "nodeType": "ElementaryTypeNameExpression", + "src": "169:7:2", + "typeDescriptions": { + "typeIdentifier": "t_type$_t_address_$", + "typeString": "type(address)" + }, + "typeName": { + "id": 31, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "169:7:2", + "typeDescriptions": {} + } + }, + "id": 34, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "typeConversion", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "169:10:2", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "arguments": [ + { + "hexValue": "32", + "id": 37, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "189:1:2", + "typeDescriptions": { + "typeIdentifier": "t_rational_2_by_1", + "typeString": "int_const 2" + }, + "value": "2" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_rational_2_by_1", + "typeString": "int_const 2" + } + ], + "id": 36, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "nodeType": "ElementaryTypeNameExpression", + "src": "181:7:2", + "typeDescriptions": { + "typeIdentifier": "t_type$_t_address_$", + "typeString": "type(address)" + }, + "typeName": { + "id": 35, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "181:7:2", + "typeDescriptions": {} + } + }, + "id": 38, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "typeConversion", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "181:10:2", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "hexValue": "33", + "id": 39, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "193:1:2", + "typeDescriptions": { + "typeIdentifier": "t_rational_3_by_1", + "typeString": "int_const 3" + }, + "value": "3" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_rational_3_by_1", + "typeString": "int_const 3" + } + ], + "id": 30, + "name": "Transfer", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 27, + "src": "160:8:2", + "typeDescriptions": { + "typeIdentifier": "t_function_event_nonpayable$_t_address_$_t_address_$_t_uint256_$returns$__$", + "typeString": "function (address,address,uint256)" + } + }, + "id": 40, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "160:35:2", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 41, + "nodeType": "EmitStatement", + "src": "155:40:2" + }, + { + "eventCall": { + "arguments": [ + { + "arguments": [ + { + "hexValue": "31", + "id": 47, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "230:1:2", + "typeDescriptions": { + "typeIdentifier": "t_rational_1_by_1", + "typeString": "int_const 1" + }, + "value": "1" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_rational_1_by_1", + "typeString": "int_const 1" + } + ], + "id": 46, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "nodeType": "ElementaryTypeNameExpression", + "src": "222:7:2", + "typeDescriptions": { + "typeIdentifier": "t_type$_t_address_$", + "typeString": "type(address)" + }, + "typeName": { + "id": 45, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "222:7:2", + "typeDescriptions": {} + } + }, + "id": 48, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "typeConversion", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "222:10:2", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "arguments": [ + { + "hexValue": "32", + "id": 51, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "242:1:2", + "typeDescriptions": { + "typeIdentifier": "t_rational_2_by_1", + "typeString": "int_const 2" + }, + "value": "2" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_rational_2_by_1", + "typeString": "int_const 2" + } + ], + "id": 50, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "nodeType": "ElementaryTypeNameExpression", + "src": "234:7:2", + "typeDescriptions": { + "typeIdentifier": "t_type$_t_address_$", + "typeString": "type(address)" + }, + "typeName": { + "id": 49, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "234:7:2", + "typeDescriptions": {} + } + }, + "id": 52, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "typeConversion", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "234:10:2", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "hexValue": "33", + "id": 53, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "246:1:2", + "typeDescriptions": { + "typeIdentifier": "t_rational_3_by_1", + "typeString": "int_const 3" + }, + "value": "3" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_rational_3_by_1", + "typeString": "int_const 3" + } + ], + "expression": { + "id": 42, + "name": "A", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 19, + "src": "210:1:2", + "typeDescriptions": { + "typeIdentifier": "t_module_18", + "typeString": "module \"A.sol\"" + } + }, + "id": 44, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "memberLocation": "212:9:2", + "memberName": "TransferA", + "nodeType": "MemberAccess", + "referencedDeclaration": 8, + "src": "210:11:2", + "typeDescriptions": { + "typeIdentifier": "t_function_event_nonpayable$_t_address_$_t_address_$_t_uint256_$returns$__$", + "typeString": "function (address,address,uint256)" + } + }, + "id": 54, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "210:38:2", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 55, + "nodeType": "EmitStatement", + "src": "205:43:2" + }, + { + "eventCall": { + "arguments": [ + { + "arguments": [ + { + "hexValue": "31", + "id": 63, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "301:1:2", + "typeDescriptions": { + "typeIdentifier": "t_rational_1_by_1", + "typeString": "int_const 1" + }, + "value": "1" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_rational_1_by_1", + "typeString": "int_const 1" + } + ], + "id": 62, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "nodeType": "ElementaryTypeNameExpression", + "src": "293:7:2", + "typeDescriptions": { + "typeIdentifier": "t_type$_t_address_$", + "typeString": "type(address)" + }, + "typeName": { + "id": 61, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "293:7:2", + "typeDescriptions": {} + } + }, + "id": 64, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "typeConversion", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "293:10:2", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "arguments": [ + { + "hexValue": "32", + "id": 67, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "313:1:2", + "typeDescriptions": { + "typeIdentifier": "t_rational_2_by_1", + "typeString": "int_const 2" + }, + "value": "2" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_rational_2_by_1", + "typeString": "int_const 2" + } + ], + "id": 66, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "nodeType": "ElementaryTypeNameExpression", + "src": "305:7:2", + "typeDescriptions": { + "typeIdentifier": "t_type$_t_address_$", + "typeString": "type(address)" + }, + "typeName": { + "id": 65, + "name": "address", + "nodeType": "ElementaryTypeName", + "src": "305:7:2", + "typeDescriptions": {} + } + }, + "id": 68, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "typeConversion", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "305:10:2", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_address", + "typeString": "address" + } + }, + { + "hexValue": "33", + "id": 69, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "317:1:2", + "typeDescriptions": { + "typeIdentifier": "t_rational_3_by_1", + "typeString": "int_const 3" + }, + "value": "3" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_address", + "typeString": "address" + }, + { + "typeIdentifier": "t_rational_3_by_1", + "typeString": "int_const 3" + } + ], + "expression": { + "expression": { + "id": 56, + "name": "A", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 19, + "src": "263:1:2", + "typeDescriptions": { + "typeIdentifier": "t_module_18", + "typeString": "module \"A.sol\"" + } + }, + "id": 59, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "memberLocation": "265:9:2", + "memberName": "AContract", + "nodeType": "MemberAccess", + "referencedDeclaration": 17, + "src": "263:11:2", + "typeDescriptions": { + "typeIdentifier": "t_type$_t_contract$_AContract_$17_$", + "typeString": "type(contract AContract)" + } + }, + "id": 60, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberLocation": "275:17:2", + "memberName": "TransferAContract", + "nodeType": "MemberAccess", + "referencedDeclaration": 16, + "src": "263:29:2", + "typeDescriptions": { + "typeIdentifier": "t_function_event_nonpayable$_t_address_$_t_address_$_t_uint256_$returns$__$", + "typeString": "function (address,address,uint256)" + } + }, + "id": 70, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "263:56:2", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_tuple$__$", + "typeString": "tuple()" + } + }, + "id": 71, + "nodeType": "EmitStatement", + "src": "258:61:2" + } + ] + }, + "functionSelector": "26121ff0", + "id": 73, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nameLocation": "133:1:2", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 28, + "nodeType": "ParameterList", + "parameters": [], + "src": "135:2:2" + }, + "returnParameters": { + "id": 29, + "nodeType": "ParameterList", + "parameters": [], + "src": "145:0:2" + }, + "scope": 74, + "src": "124:202:2", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "scope": 75, + "src": "28:300:2", + "usedErrors": [], + "usedEvents": [ + 8, + 16, + 27 + ] + } + ], + "src": "0:329:2" +} +] diff --git a/test/libsolidity/ASTJSON/emit_event_from_module_via_member_access.sol b/test/libsolidity/ASTJSON/emit_event_from_module_via_member_access.sol new file mode 100644 index 0000000000..fa9614e214 --- /dev/null +++ b/test/libsolidity/ASTJSON/emit_event_from_module_via_member_access.sol @@ -0,0 +1,18 @@ +==== Source: A.sol ==== +event TransferA(address indexed from, address indexed to, uint amount); +contract AContract { + event TransferAContract(address indexed from, address indexed to, uint amount); +} + +==== Source: ERC20.sol ==== +import * as A from "A.sol"; +contract ERC20 { + event Transfer(address indexed from, address indexed to, uint amount); + function f () public { + emit Transfer(address(1), address(2), 3); + emit A.TransferA(address(1), address(2), 3); + emit A.AContract.TransferAContract(address(1), address(2), 3); + } +} + +// ---- diff --git a/test/libsolidity/ASTJSON/error_from_module_via_member_access.json b/test/libsolidity/ASTJSON/error_from_module_via_member_access.json new file mode 100644 index 0000000000..db9062891a --- /dev/null +++ b/test/libsolidity/ASTJSON/error_from_module_via_member_access.json @@ -0,0 +1,544 @@ +[ +{ + "absolutePath": "A.sol", + "exportedSymbols": { + "AContract": [ + 9 + ], + "ErrorA": [ + 4 + ] + }, + "id": 10, + "nodeType": "SourceUnit", + "nodes": [ + { + "errorSelector": "23b0db14", + "id": 4, + "name": "ErrorA", + "nameLocation": "6:6:1", + "nodeType": "ErrorDefinition", + "parameters": { + "id": 3, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 2, + "mutability": "mutable", + "name": "msg", + "nameLocation": "20:3:1", + "nodeType": "VariableDeclaration", + "scope": 4, + "src": "13:10:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_string_memory_ptr", + "typeString": "string" + }, + "typeName": { + "id": 1, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "13:6:1", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } + }, + "visibility": "internal" + } + ], + "src": "12:12:1" + }, + "src": "0:25:1" + }, + { + "abstract": false, + "baseContracts": [], + "canonicalName": "AContract", + "contractDependencies": [], + "contractKind": "contract", + "fullyImplemented": true, + "id": 9, + "linearizedBaseContracts": [ + 9 + ], + "name": "AContract", + "nameLocation": "35:9:1", + "nodeType": "ContractDefinition", + "nodes": [ + { + "errorSelector": "e00f4941", + "id": 8, + "name": "ErrorAContract", + "nameLocation": "57:14:1", + "nodeType": "ErrorDefinition", + "parameters": { + "id": 7, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 6, + "mutability": "mutable", + "name": "msg", + "nameLocation": "79:3:1", + "nodeType": "VariableDeclaration", + "scope": 8, + "src": "72:10:1", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_string_memory_ptr", + "typeString": "string" + }, + "typeName": { + "id": 5, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "72:6:1", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } + }, + "visibility": "internal" + } + ], + "src": "71:12:1" + }, + "src": "51:33:1" + } + ], + "scope": 10, + "src": "26:60:1", + "usedErrors": [ + 8 + ], + "usedEvents": [] + } + ], + "src": "0:87:1" +}, +{ + "absolutePath": "B.sol", + "exportedSymbols": { + "A": [ + 11 + ], + "BContract": [ + 46 + ] + }, + "id": 47, + "nodeType": "SourceUnit", + "nodes": [ + { + "absolutePath": "A.sol", + "file": "A.sol", + "id": 11, + "nameLocation": "12:1:2", + "nodeType": "ImportDirective", + "scope": 47, + "sourceUnit": 10, + "src": "0:27:2", + "symbolAliases": [], + "unitAlias": "A" + }, + { + "abstract": false, + "baseContracts": [], + "canonicalName": "BContract", + "contractDependencies": [], + "contractKind": "contract", + "fullyImplemented": true, + "id": 46, + "linearizedBaseContracts": [ + 46 + ], + "name": "BContract", + "nameLocation": "37:9:2", + "nodeType": "ContractDefinition", + "nodes": [ + { + "errorSelector": "1e012d26", + "id": 15, + "name": "ErrorBContract", + "nameLocation": "59:14:2", + "nodeType": "ErrorDefinition", + "parameters": { + "id": 14, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 13, + "mutability": "mutable", + "name": "msg", + "nameLocation": "81:3:2", + "nodeType": "VariableDeclaration", + "scope": 15, + "src": "74:10:2", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_string_memory_ptr", + "typeString": "string" + }, + "typeName": { + "id": 12, + "name": "string", + "nodeType": "ElementaryTypeName", + "src": "74:6:2", + "typeDescriptions": { + "typeIdentifier": "t_string_storage_ptr", + "typeString": "string" + } + }, + "visibility": "internal" + } + ], + "src": "73:12:2" + }, + "src": "53:33:2" + }, + { + "body": { + "id": 22, + "nodeType": "Block", + "src": "113:52:2", + "statements": [ + { + "errorCall": { + "arguments": [ + { + "hexValue": "736f6d65206572726f72", + "id": 19, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "145:12:2", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_b4ae9cd779cde5df80ff601d28c3fa893d24133d1b4e6fdffbb3a05b40d7a577", + "typeString": "literal_string \"some error\"" + }, + "value": "some error" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_stringliteral_b4ae9cd779cde5df80ff601d28c3fa893d24133d1b4e6fdffbb3a05b40d7a577", + "typeString": "literal_string \"some error\"" + } + ], + "id": 18, + "name": "ErrorBContract", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 15, + "src": "130:14:2", + "typeDescriptions": { + "typeIdentifier": "t_function_error_pure$_t_string_memory_ptr_$returns$_t_error_$", + "typeString": "function (string memory) pure returns (error)" + } + }, + "id": 20, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "130:28:2", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_error", + "typeString": "error" + } + }, + "id": 21, + "nodeType": "RevertStatement", + "src": "123:35:2" + } + ] + }, + "functionSelector": "c27fc305", + "id": 23, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f1", + "nameLocation": "100:2:2", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 16, + "nodeType": "ParameterList", + "parameters": [], + "src": "103:2:2" + }, + "returnParameters": { + "id": 17, + "nodeType": "ParameterList", + "parameters": [], + "src": "113:0:2" + }, + "scope": 46, + "src": "91:74:2", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + }, + { + "body": { + "id": 32, + "nodeType": "Block", + "src": "192:46:2", + "statements": [ + { + "errorCall": { + "arguments": [ + { + "hexValue": "736f6d65206572726f72", + "id": 29, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "218:12:2", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_b4ae9cd779cde5df80ff601d28c3fa893d24133d1b4e6fdffbb3a05b40d7a577", + "typeString": "literal_string \"some error\"" + }, + "value": "some error" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_stringliteral_b4ae9cd779cde5df80ff601d28c3fa893d24133d1b4e6fdffbb3a05b40d7a577", + "typeString": "literal_string \"some error\"" + } + ], + "expression": { + "id": 26, + "name": "A", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 11, + "src": "209:1:2", + "typeDescriptions": { + "typeIdentifier": "t_module_10", + "typeString": "module \"A.sol\"" + } + }, + "id": 28, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "memberLocation": "211:6:2", + "memberName": "ErrorA", + "nodeType": "MemberAccess", + "referencedDeclaration": 4, + "src": "209:8:2", + "typeDescriptions": { + "typeIdentifier": "t_function_error_pure$_t_string_memory_ptr_$returns$_t_error_$", + "typeString": "function (string memory) pure returns (error)" + } + }, + "id": 30, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "209:22:2", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_error", + "typeString": "error" + } + }, + "id": 31, + "nodeType": "RevertStatement", + "src": "202:29:2" + } + ] + }, + "functionSelector": "9942ec6f", + "id": 33, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f2", + "nameLocation": "179:2:2", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 24, + "nodeType": "ParameterList", + "parameters": [], + "src": "182:2:2" + }, + "returnParameters": { + "id": 25, + "nodeType": "ParameterList", + "parameters": [], + "src": "192:0:2" + }, + "scope": 46, + "src": "170:68:2", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + }, + { + "body": { + "id": 44, + "nodeType": "Block", + "src": "265:64:2", + "statements": [ + { + "errorCall": { + "arguments": [ + { + "hexValue": "736f6d65206572726f72", + "id": 41, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "string", + "lValueRequested": false, + "nodeType": "Literal", + "src": "309:12:2", + "typeDescriptions": { + "typeIdentifier": "t_stringliteral_b4ae9cd779cde5df80ff601d28c3fa893d24133d1b4e6fdffbb3a05b40d7a577", + "typeString": "literal_string \"some error\"" + }, + "value": "some error" + } + ], + "expression": { + "argumentTypes": [ + { + "typeIdentifier": "t_stringliteral_b4ae9cd779cde5df80ff601d28c3fa893d24133d1b4e6fdffbb3a05b40d7a577", + "typeString": "literal_string \"some error\"" + } + ], + "expression": { + "expression": { + "id": 36, + "name": "A", + "nodeType": "Identifier", + "overloadedDeclarations": [], + "referencedDeclaration": 11, + "src": "282:1:2", + "typeDescriptions": { + "typeIdentifier": "t_module_10", + "typeString": "module \"A.sol\"" + } + }, + "id": 39, + "isConstant": false, + "isLValue": false, + "isPure": true, + "lValueRequested": false, + "memberLocation": "284:9:2", + "memberName": "AContract", + "nodeType": "MemberAccess", + "referencedDeclaration": 9, + "src": "282:11:2", + "typeDescriptions": { + "typeIdentifier": "t_type$_t_contract$_AContract_$9_$", + "typeString": "type(contract AContract)" + } + }, + "id": 40, + "isConstant": false, + "isLValue": false, + "isPure": false, + "lValueRequested": false, + "memberLocation": "294:14:2", + "memberName": "ErrorAContract", + "nodeType": "MemberAccess", + "referencedDeclaration": 8, + "src": "282:26:2", + "typeDescriptions": { + "typeIdentifier": "t_function_error_pure$_t_string_memory_ptr_$returns$_t_error_$", + "typeString": "function (string memory) pure returns (error)" + } + }, + "id": 42, + "isConstant": false, + "isLValue": false, + "isPure": false, + "kind": "functionCall", + "lValueRequested": false, + "nameLocations": [], + "names": [], + "nodeType": "FunctionCall", + "src": "282:40:2", + "tryCall": false, + "typeDescriptions": { + "typeIdentifier": "t_error", + "typeString": "error" + } + }, + "id": 43, + "nodeType": "RevertStatement", + "src": "275:47:2" + } + ] + }, + "functionSelector": "aaf05f3d", + "id": 45, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f3", + "nameLocation": "252:2:2", + "nodeType": "FunctionDefinition", + "parameters": { + "id": 34, + "nodeType": "ParameterList", + "parameters": [], + "src": "255:2:2" + }, + "returnParameters": { + "id": 35, + "nodeType": "ParameterList", + "parameters": [], + "src": "265:0:2" + }, + "scope": 46, + "src": "243:86:2", + "stateMutability": "nonpayable", + "virtual": false, + "visibility": "public" + } + ], + "scope": 47, + "src": "28:303:2", + "usedErrors": [ + 4, + 8, + 15 + ], + "usedEvents": [] + } + ], + "src": "0:332:2" +} +] diff --git a/test/libsolidity/ASTJSON/error_from_module_via_member_access.sol b/test/libsolidity/ASTJSON/error_from_module_via_member_access.sol new file mode 100644 index 0000000000..74c29fabb2 --- /dev/null +++ b/test/libsolidity/ASTJSON/error_from_module_via_member_access.sol @@ -0,0 +1,22 @@ +==== Source: A.sol ==== +error ErrorA(string msg); +contract AContract { + error ErrorAContract(string msg); +} + +==== Source: B.sol ==== +import * as A from "A.sol"; +contract BContract { + error ErrorBContract(string msg); + function f1 () public { + revert ErrorBContract("some error"); + } + function f2 () public { + revert A.ErrorA("some error"); + } + function f3 () public { + revert A.AContract.ErrorAContract("some error"); + } +} + +// ---- diff --git a/test/libsolidity/natspecJSON/emit_event_from_module_via_member_access.sol b/test/libsolidity/natspecJSON/emit_event_from_module_via_member_access.sol new file mode 100644 index 0000000000..39d1644fe5 --- /dev/null +++ b/test/libsolidity/natspecJSON/emit_event_from_module_via_member_access.sol @@ -0,0 +1,115 @@ +==== Source: A.sol ==== +/// @notice This event is emitted when a transfer from A occurs. +/// @param from The source account. +/// @param to The destination account. +/// @param amount The amount. +/// @dev A test case! +event TransferA(address indexed from, address indexed to, uint amount); + +contract AContract { + /// @notice This event is emitted when a transfer from AContract occurs. + /// @param from The source account. + /// @param to The destination account. + /// @param amount The amount. + /// @dev A test case! + event TransferAContract(address indexed from, address indexed to, uint amount); +} + +==== Source: ERC20.sol ==== +import * as A from "A.sol"; +contract ERC20 { + /// @notice This event is emitted when a transfer from ERC20 occurs. + /// @param from The source account. + /// @param to The destination account. + /// @param amount The amount. + /// @dev A test case! + event Transfer(address indexed from, address indexed to, uint amount); + + function test() public { + emit Transfer(address(1), address(5), 1); + emit A.AContract.TransferAContract(address(1), address(5), 1); + emit A.TransferA(address(1), address(5), 1); + } +} + +// ---- +// ---- +// A.sol:AContract devdoc +// { +// "events": { +// "TransferAContract(address,address,uint256)": { +// "details": "A test case!", +// "params": { +// "amount": "The amount.", +// "from": "The source account.", +// "to": "The destination account." +// } +// } +// }, +// "kind": "dev", +// "methods": {}, +// "version": 1 +// } +// +// A.sol:AContract userdoc +// { +// "events": { +// "TransferAContract(address,address,uint256)": { +// "notice": "This event is emitted when a transfer from AContract occurs." +// } +// }, +// "kind": "user", +// "methods": {}, +// "version": 1 +// } +// +// ERC20.sol:ERC20 devdoc +// { +// "events": { +// "Transfer(address,address,uint256)": { +// "details": "A test case!", +// "params": { +// "amount": "The amount.", +// "from": "The source account.", +// "to": "The destination account." +// } +// }, +// "TransferA(address,address,uint256)": { +// "details": "A test case!", +// "params": { +// "amount": "The amount.", +// "from": "The source account.", +// "to": "The destination account." +// } +// }, +// "TransferAContract(address,address,uint256)": { +// "details": "A test case!", +// "params": { +// "amount": "The amount.", +// "from": "The source account.", +// "to": "The destination account." +// } +// } +// }, +// "kind": "dev", +// "methods": {}, +// "version": 1 +// } +// +// ERC20.sol:ERC20 userdoc +// { +// "events": { +// "Transfer(address,address,uint256)": { +// "notice": "This event is emitted when a transfer from ERC20 occurs." +// }, +// "TransferA(address,address,uint256)": { +// "notice": "This event is emitted when a transfer from A occurs." +// }, +// "TransferAContract(address,address,uint256)": { +// "notice": "This event is emitted when a transfer from AContract occurs." +// } +// }, +// "kind": "user", +// "methods": {}, +// "version": 1 +// } diff --git a/test/libsolidity/natspecJSON/error_from_module_via_member_access.sol b/test/libsolidity/natspecJSON/error_from_module_via_member_access.sol new file mode 100644 index 0000000000..0c2e44e3ce --- /dev/null +++ b/test/libsolidity/natspecJSON/error_from_module_via_member_access.sol @@ -0,0 +1,127 @@ +==== Source: A.sol ==== +/// Something failed. +/// @dev an error. +/// @param a first parameter +/// @param b second parameter +error E(uint a, uint b); + +contract AContract { + /// Something failed. + /// @dev an error. + /// @param a first parameter + /// @param b second parameter + error EAContract(uint a, uint b); +} + +==== Source: ERC20.sol ==== +import * as A from "A.sol"; +contract ERC20 { + /// Something failed. + /// @dev an error. + /// @param a first parameter + /// @param b second parameter + error EERC20(uint a, uint b); + + function f(uint a) public pure { + if (a > 0) + revert EERC20(1, 2); + else if (a < 0) + revert A.AContract.EAContract(5, 6); + else + revert A.E(5, 6); + } +} + +// ---- +// ---- +// A.sol:AContract devdoc +// { +// "errors": { +// "EAContract(uint256,uint256)": [ +// { +// "details": "an error.", +// "params": { +// "a": "first parameter", +// "b": "second parameter" +// } +// } +// ] +// }, +// "kind": "dev", +// "methods": {}, +// "version": 1 +// } +// +// A.sol:AContract userdoc +// { +// "errors": { +// "EAContract(uint256,uint256)": [ +// { +// "notice": "Something failed." +// } +// ] +// }, +// "kind": "user", +// "methods": {}, +// "version": 1 +// } +// +// ERC20.sol:ERC20 devdoc +// { +// "errors": { +// "E(uint256,uint256)": [ +// { +// "details": "an error.", +// "params": { +// "a": "first parameter", +// "b": "second parameter" +// } +// } +// ], +// "EAContract(uint256,uint256)": [ +// { +// "details": "an error.", +// "params": { +// "a": "first parameter", +// "b": "second parameter" +// } +// } +// ], +// "EERC20(uint256,uint256)": [ +// { +// "details": "an error.", +// "params": { +// "a": "first parameter", +// "b": "second parameter" +// } +// } +// ] +// }, +// "kind": "dev", +// "methods": {}, +// "version": 1 +// } +// +// ERC20.sol:ERC20 userdoc +// { +// "errors": { +// "E(uint256,uint256)": [ +// { +// "notice": "Something failed." +// } +// ], +// "EAContract(uint256,uint256)": [ +// { +// "notice": "Something failed." +// } +// ], +// "EERC20(uint256,uint256)": [ +// { +// "notice": "Something failed." +// } +// ] +// }, +// "kind": "user", +// "methods": {}, +// "version": 1 +// } diff --git a/test/libsolidity/semanticTests/errors/error_throw_from_module_via_member_access.sol b/test/libsolidity/semanticTests/errors/error_throw_from_module_via_member_access.sol new file mode 100644 index 0000000000..f9b968694a --- /dev/null +++ b/test/libsolidity/semanticTests/errors/error_throw_from_module_via_member_access.sol @@ -0,0 +1,31 @@ +==== Source: A.sol ==== +error ErrorA(string msg); +==== Source: B.sol ==== +import * as A from "A.sol"; + +error ErrorB(string msg); + +contract BContract { + error ErrorB(string msg); +} +==== Source: C.sol ==== +import * as B from "B.sol"; + +contract CContract { + function error1() external { + revert B.ErrorB("B error"); + } + + function error2() external { + revert B.BContract.ErrorB("B.BContract error"); + } + + function error3() external { + revert B.A.ErrorA("B.A error"); + } +} + +// ---- +// error1() -> FAILURE, hex"a5f9ec67", 0x20, 7, "B error" +// error2() -> FAILURE, hex"a5f9ec67", 0x20, 17, "B.BContract error" +// error3() -> FAILURE, hex"23b0db14", 0x20, 9, "B.A error" diff --git a/test/libsolidity/semanticTests/events/event_emit_from_module_via_member_access.sol b/test/libsolidity/semanticTests/events/event_emit_from_module_via_member_access.sol new file mode 100644 index 0000000000..b944ea1148 --- /dev/null +++ b/test/libsolidity/semanticTests/events/event_emit_from_module_via_member_access.sol @@ -0,0 +1,26 @@ +==== Source: A.sol ==== +event Transfer(address indexed _from, address indexed _to, uint256 _value); +==== Source: B.sol ==== +import * as A from "A.sol"; + +event Transfer(address indexed _from, address indexed _to, uint256 _value); + +contract BContract { + event Transfer(address indexed _from, address indexed _to, uint256 _value); +} +==== Source: C.sol ==== +import * as B from "B.sol"; + +contract CContract { + function returnAddress() external { + emit B.Transfer(address(0x0b), address(0x0c), 0x0d); + emit B.BContract.Transfer(address(0x0e), address(0x0f), 0x10); + emit B.A.Transfer(address(0x11), address(0x12), 0x13); + } +} + +// ---- +// returnAddress() -> +// ~ emit Transfer(address,address,uint256): #0x0b, #0x0c, 0x0d +// ~ emit Transfer(address,address,uint256): #0x0e, #0x0f, 0x10 +// ~ emit Transfer(address,address,uint256): #0x11, #0x12, 0x13 From 53a9efb068522a92ad89d487e8aa30389e700ee9 Mon Sep 17 00:00:00 2001 From: rodiazet Date: Wed, 10 Dec 2025 14:27:12 +0100 Subject: [PATCH 110/243] Update parser error message and tests results. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kamil Śliwak --- libsolidity/parsing/Parser.cpp | 2 +- .../standard_outputs_on_parsing_error/output.json | 4 ++-- .../syntaxTests/freeFunctions/free_constructor.sol | 2 +- test/libsolidity/syntaxTests/freeFunctions/free_fallback.sol | 2 +- test/libsolidity/syntaxTests/freeFunctions/free_receive.sol | 2 +- test/libsolidity/syntaxTests/parsing/unexpected.sol | 2 +- .../layout_specified_by_function_empty_call_options.sol | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index ddffdb6002..77b7026739 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -170,7 +170,7 @@ ASTPointer Parser::parse(CharStream& _charStream) expectToken(Token::Semicolon); } else - fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum/constant/function/error definition."); + fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/user-defined type/constant/function/error/event definition."); } } solAssert(m_recursionDepth == 0, ""); diff --git a/test/cmdlineTests/standard_outputs_on_parsing_error/output.json b/test/cmdlineTests/standard_outputs_on_parsing_error/output.json index 6730d772ee..39610363ee 100644 --- a/test/cmdlineTests/standard_outputs_on_parsing_error/output.json +++ b/test/cmdlineTests/standard_outputs_on_parsing_error/output.json @@ -3,14 +3,14 @@ { "component": "general", "errorCode": "7858", - "formattedMessage": "ParserError: Expected pragma, import directive or contract/interface/library/struct/enum/constant/function/error definition. + "formattedMessage": "ParserError: Expected pragma, import directive or contract/interface/library/user-defined type/constant/function/error/event definition. --> C:5:14: | 5 | contract C {}} | ^ ", - "message": "Expected pragma, import directive or contract/interface/library/struct/enum/constant/function/error definition.", + "message": "Expected pragma, import directive or contract/interface/library/user-defined type/constant/function/error/event definition.", "severity": "error", "sourceLocation": { "end": 121, diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_constructor.sol b/test/libsolidity/syntaxTests/freeFunctions/free_constructor.sol index 7878983571..5e263606d6 100644 --- a/test/libsolidity/syntaxTests/freeFunctions/free_constructor.sol +++ b/test/libsolidity/syntaxTests/freeFunctions/free_constructor.sol @@ -1,3 +1,3 @@ constructor() {} // ---- -// ParserError 7858: (0-11): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function/error definition. +// ParserError 7858: (0-11): Expected pragma, import directive or contract/interface/library/user-defined type/constant/function/error/event definition. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_fallback.sol b/test/libsolidity/syntaxTests/freeFunctions/free_fallback.sol index 897ed59647..cec4c7de07 100644 --- a/test/libsolidity/syntaxTests/freeFunctions/free_fallback.sol +++ b/test/libsolidity/syntaxTests/freeFunctions/free_fallback.sol @@ -1,3 +1,3 @@ fallback(){} // ---- -// ParserError 7858: (0-8): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function/error definition. +// ParserError 7858: (0-8): Expected pragma, import directive or contract/interface/library/user-defined type/constant/function/error/event definition. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_receive.sol b/test/libsolidity/syntaxTests/freeFunctions/free_receive.sol index 83dfee4463..ad021b2214 100644 --- a/test/libsolidity/syntaxTests/freeFunctions/free_receive.sol +++ b/test/libsolidity/syntaxTests/freeFunctions/free_receive.sol @@ -1,3 +1,3 @@ receive() {} // ---- -// ParserError 7858: (0-7): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function/error definition. +// ParserError 7858: (0-7): Expected pragma, import directive or contract/interface/library/user-defined type/constant/function/error/event definition. diff --git a/test/libsolidity/syntaxTests/parsing/unexpected.sol b/test/libsolidity/syntaxTests/parsing/unexpected.sol index c6fae414c3..9dd495c8b5 100644 --- a/test/libsolidity/syntaxTests/parsing/unexpected.sol +++ b/test/libsolidity/syntaxTests/parsing/unexpected.sol @@ -1,3 +1,3 @@ unexpected // ---- -// ParserError 7858: (0-10): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function/error definition. +// ParserError 7858: (0-10): Expected pragma, import directive or contract/interface/library/user-defined type/constant/function/error/event definition. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_function_empty_call_options.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_function_empty_call_options.sol index e0b1f4e5cb..79bcb88786 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_function_empty_call_options.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_function_empty_call_options.sol @@ -3,4 +3,4 @@ contract A { } contract C is A layout at this.f{}() {} // ---- -// ParserError 7858: (98-99): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function/error definition. +// ParserError 7858: (98-99): Expected pragma, import directive or contract/interface/library/user-defined type/constant/function/error/event definition. From dad604cbdfc79f8f70b6c8433082b4b66fe76cec Mon Sep 17 00:00:00 2001 From: rodiazet Date: Wed, 10 Dec 2025 14:37:19 +0100 Subject: [PATCH 111/243] Update changelog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kamil Śliwak --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 42d679caa3..7ed3d7ef64 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Language Features: Compiler Features: Bugfixes: +* Codegen: Fix internal compiler error when emitting events via module member access. * TypeChecker: Fix error and event selectors not being considered compile-time constant. From 2d6ecc5f501f49b6537a6756e12e5a60e9e46cd7 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Wed, 17 Dec 2025 13:49:09 -0300 Subject: [PATCH 112/243] legacy: Fix handling of storage array boundaries (deletion, cleanup, and copy) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Kirchner Co-authored-by: clonker <1685266+clonker@users.noreply.github.com> Co-authored-by: Kamil Śliwak --- libsolidity/codegen/ArrayUtils.cpp | 469 ++++------------------------- libsolidity/codegen/ArrayUtils.h | 10 +- 2 files changed, 68 insertions(+), 411 deletions(-) diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 455cfe0842..841161fa91 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -46,256 +46,65 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons // this copies source to target and also clears target if it was larger // need to leave "target_ref target_byte_off" on the stack at the end - // stack layout: [source_ref] [source length] target_ref (top) + // stack layout: source_ref [source length] target_ref (top) solAssert(_targetType.location() == DataLocation::Storage, ""); - Type const* targetBaseType = _targetType.baseType(); - Type const* sourceBaseType = _sourceType.baseType(); - // TODO unroll loop for small sizes - bool sourceIsStorage = _sourceType.location() == DataLocation::Storage; bool fromCalldata = _sourceType.location() == DataLocation::CallData; - bool directCopy = sourceIsStorage && sourceBaseType->isValueType() && *sourceBaseType == *targetBaseType; - bool haveByteOffsetSource = !directCopy && sourceIsStorage && sourceBaseType->storageBytes() <= 16; - bool haveByteOffsetTarget = !directCopy && targetBaseType->storageBytes() <= 16; - unsigned byteOffsetSize = (haveByteOffsetSource ? 1u : 0u) + (haveByteOffsetTarget ? 1u : 0u); + bool haveSourceLengthOnStack = fromCalldata && _sourceType.isDynamicallySized(); - // stack: source_ref [source_length] target_ref - // store target_ref for (unsigned i = _sourceType.sizeOnStack(); i > 0; --i) m_context << swapInstruction(i); // stack: target_ref source_ref [source_length] - if (_targetType.isByteArrayOrString()) + if (_sourceType.baseType()->category() == Type::Category::Array) { - // stack: target_ref source_ref [source_length] - if (fromCalldata && _sourceType.isDynamicallySized()) - { - // stack: target_ref source_ref source_length - m_context << Instruction::SWAP1; - // stack: target_ref source_length source_ref - m_context << Instruction::DUP3; - // stack: target_ref source_length source_ref target_ref - m_context.callYulFunction( - m_context.utilFunctions().copyByteArrayToStorageFunction(_sourceType, _targetType), - 3, - 0 - ); - } - else - { - // stack: target_ref source_ref - m_context << Instruction::DUP2; - // stack: target_ref source_ref target_ref - m_context.callYulFunction( - m_context.utilFunctions().copyByteArrayToStorageFunction(_sourceType, _targetType), - 2, - 0 - ); - } - // stack: target_ref - return; + // TODO: This limitation can now be removed since we use Yul utility functions that handle nested arrays correctly. + // The old inline assembly implementation couldn't handle nested calldata dynamic arrays, but the Yul functions + // support them through recursive calls. We keep this check temporarily for backward compatibility. + auto const& sourceBaseArrayType = dynamic_cast(*_sourceType.baseType()); + solUnimplementedAssert( + !fromCalldata || + !_sourceType.isDynamicallyEncoded() || + !sourceBaseArrayType.isDynamicallySized(), + "Copying nested calldata dynamic arrays to storage is not implemented in the old code generator." + ); } - - // retrieve source length - if (_sourceType.location() != DataLocation::CallData || !_sourceType.isDynamicallySized()) - retrieveLength(_sourceType); // otherwise, length is already there - if (_sourceType.location() == DataLocation::Memory && _sourceType.isDynamicallySized()) + else { - // increment source pointer to point to data - m_context << Instruction::SWAP1 << u256(0x20); - m_context << Instruction::ADD << Instruction::SWAP1; + // TODO: This limitation can now be removed since we use Yul utility functions that handle non-value types correctly. + // The old inline assembly implementation couldn't handle copying arrays of non-value types from memory or calldata to storage, + // but the Yul functions support them. We keep this check temporarily for backward compatibility. + bool fromMemoryOrCalldata = _sourceType.location() == DataLocation::Memory || _sourceType.location() == DataLocation::CallData; + solUnimplementedAssert( + _sourceType.baseType()->isValueType() || !fromMemoryOrCalldata, + "Copying of type " + _sourceType.toString(false) + " to storage is not supported in legacy (only supported by the IR pipeline). " + + "Hint: try compiling with `--via-ir` (CLI) or the equivalent `viaIR: true` (Standard JSON)." + ); } - // stack: target_ref source_ref source_length - Type const* targetType = &_targetType; - Type const* sourceType = &_sourceType; - m_context.callLowLevelFunction( - "$copyArrayToStorage_" + sourceType->identifier() + "_to_" + targetType->identifier(), - 3, - 1, - [=](CompilerContext& _context) - { - ArrayUtils utils(_context); - ArrayType const& _sourceType = dynamic_cast(*sourceType); - ArrayType const& _targetType = dynamic_cast(*targetType); - // stack: target_ref source_ref source_length - _context << Instruction::DUP3; - // stack: target_ref source_ref source_length target_ref - utils.retrieveLength(_targetType); - // stack: target_ref source_ref source_length target_ref target_length - if (_targetType.isDynamicallySized()) - { - // store new target length - solAssert(!_targetType.isByteArrayOrString()); - _context << Instruction::DUP3 << Instruction::DUP3 << Instruction::SSTORE; - } - if (sourceBaseType->category() == Type::Category::Mapping) - { - solAssert(targetBaseType->category() == Type::Category::Mapping, ""); - solAssert(_sourceType.location() == DataLocation::Storage, ""); - // nothing to copy - _context - << Instruction::POP << Instruction::POP - << Instruction::POP << Instruction::POP; - return; - } - // stack: target_ref source_ref source_length target_ref target_length - // compute hashes (data positions) - _context << Instruction::SWAP1; - if (_targetType.isDynamicallySized()) - CompilerUtils(_context).computeHashStatic(); - // stack: target_ref source_ref source_length target_length target_data_pos - _context << Instruction::SWAP1; - utils.convertLengthToSize(_targetType); - _context << Instruction::DUP2 << Instruction::ADD; - // stack: target_ref source_ref source_length target_data_pos target_data_end - _context << Instruction::SWAP3; - // stack: target_ref target_data_end source_length target_data_pos source_ref - - evmasm::AssemblyItem copyLoopEndWithoutByteOffset = _context.newTag(); - solAssert(!_targetType.isByteArrayOrString()); - // skip copying if source length is zero - _context << Instruction::DUP3 << Instruction::ISZERO; - _context.appendConditionalJumpTo(copyLoopEndWithoutByteOffset); - - if (_sourceType.location() == DataLocation::Storage && _sourceType.isDynamicallySized()) - CompilerUtils(_context).computeHashStatic(); - // stack: target_ref target_data_end source_length target_data_pos source_data_pos - _context << Instruction::SWAP2; - utils.convertLengthToSize(_sourceType); - _context << Instruction::DUP3 << Instruction::ADD; - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end - if (haveByteOffsetTarget) - _context << u256(0); - if (haveByteOffsetSource) - _context << u256(0); - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] - evmasm::AssemblyItem copyLoopStart = _context.newTag(); - _context << copyLoopStart; - // check for loop condition - _context - << dupInstruction(3 + byteOffsetSize) << dupInstruction(2 + byteOffsetSize) - << Instruction::GT << Instruction::ISZERO; - evmasm::AssemblyItem copyLoopEnd = _context.appendConditionalJump(); - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] - // copy - if (sourceBaseType->category() == Type::Category::Array) - { - solAssert(byteOffsetSize == 0, "Byte offset for array as base type."); - auto const& sourceBaseArrayType = dynamic_cast(*sourceBaseType); - - solUnimplementedAssert( - _sourceType.location() != DataLocation::CallData || - !_sourceType.isDynamicallyEncoded() || - !sourceBaseArrayType.isDynamicallySized(), - "Copying nested calldata dynamic arrays to storage is not implemented in the old code generator." - ); - _context << Instruction::DUP3; - if (sourceBaseArrayType.location() == DataLocation::Memory) - _context << Instruction::MLOAD; - _context << Instruction::DUP3; - utils.copyArrayToStorage(dynamic_cast(*targetBaseType), sourceBaseArrayType); - _context << Instruction::POP; - } - else if (directCopy) - { - solAssert(byteOffsetSize == 0, "Byte offset for direct copy."); - _context - << Instruction::DUP3 << Instruction::SLOAD - << Instruction::DUP3 << Instruction::SSTORE; - } - else - { - // Note that we have to copy each element on its own in case conversion is involved. - // We might copy too much if there is padding at the last element, but this way end - // checking is easier. - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] - _context << dupInstruction(3 + byteOffsetSize); - if (_sourceType.location() == DataLocation::Storage) - { - if (haveByteOffsetSource) - _context << Instruction::DUP2; - else - _context << u256(0); - StorageItem(_context, *sourceBaseType).retrieveValue(SourceLocation(), true); - } - else if (sourceBaseType->isValueType()) - CompilerUtils(_context).loadFromMemoryDynamic(*sourceBaseType, fromCalldata, true, false); - else - solUnimplemented("Copying of type " + _sourceType.toString(false) + " to storage is not supported in legacy (only supported by the IR pipeline). Hint: try compiling with `--via-ir` (CLI) or the equivalent `viaIR: true` (Standard JSON)"); - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] ... - assertThrow( - 2 + byteOffsetSize + sourceBaseType->sizeOnStack() <= 16, - StackTooDeepError, - util::stackTooDeepString - ); - // fetch target storage reference - _context << dupInstruction(2 + byteOffsetSize + sourceBaseType->sizeOnStack()); - if (haveByteOffsetTarget) - _context << dupInstruction(1 + byteOffsetSize + sourceBaseType->sizeOnStack()); - else - _context << u256(0); - StorageItem(_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true); - } - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] - // increment source - if (haveByteOffsetSource) - utils.incrementByteOffset(sourceBaseType->storageBytes(), 1, haveByteOffsetTarget ? 5 : 4); - else - { - _context << swapInstruction(2 + byteOffsetSize); - if (sourceIsStorage) - _context << sourceBaseType->storageSize(); - else if (_sourceType.location() == DataLocation::Memory) - _context << sourceBaseType->memoryHeadSize(); - else - _context << sourceBaseType->calldataHeadSize(); - _context - << Instruction::ADD - << swapInstruction(2 + byteOffsetSize); - } - // increment target - if (haveByteOffsetTarget) - utils.incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2); - else - _context - << swapInstruction(1 + byteOffsetSize) - << targetBaseType->storageSize() - << Instruction::ADD - << swapInstruction(1 + byteOffsetSize); - _context.appendJumpTo(copyLoopStart); - _context << copyLoopEnd; - if (haveByteOffsetTarget) - { - // clear elements that might be left over in the current slot in target - // stack: target_ref target_data_end source_data_pos target_data_pos source_data_end target_byte_offset [source_byte_offset] - _context << dupInstruction(byteOffsetSize) << Instruction::ISZERO; - evmasm::AssemblyItem copyCleanupLoopEnd = _context.appendConditionalJump(); - _context << dupInstruction(2 + byteOffsetSize) << dupInstruction(1 + byteOffsetSize); - StorageItem(_context, *targetBaseType).setToZero(SourceLocation(), true); - utils.incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2); - _context.appendJumpTo(copyLoopEnd); - - _context << copyCleanupLoopEnd; - _context << Instruction::POP; // might pop the source, but then target is popped next - } - if (haveByteOffsetSource) - _context << Instruction::POP; - _context << copyLoopEndWithoutByteOffset; + if (haveSourceLengthOnStack) + { + // stack: target_ref source_ref source_length + m_context << Instruction::SWAP1; + // stack: target_ref source_length source_ref + m_context << Instruction::DUP3; + // stack: target_ref source_length source_ref target_ref + } + else + { + // stack: target_ref source_ref + m_context << Instruction::DUP2; + // stack: target_ref source_ref target_ref + } - // zero-out leftovers in target - // stack: target_ref target_data_end source_data_pos target_data_pos_updated source_data_end - _context << Instruction::POP << Instruction::SWAP1 << Instruction::POP; - // stack: target_ref target_data_end target_data_pos_updated - if (targetBaseType->storageBytes() < 32) - utils.clearStorageLoop(TypeProvider::uint256()); - else - utils.clearStorageLoop(targetBaseType); - _context << Instruction::POP; - } + m_context.callYulFunction( + m_context.utilFunctions().copyArrayToStorageFunction(_sourceType, _targetType), + haveSourceLengthOnStack ? 3 : 2, + 0 ); + // stack: target_ref } void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWordBoundaries) const @@ -586,9 +395,9 @@ void ArrayUtils::clearArray(ArrayType const& _typeIn) const } else { - _context << Instruction::DUP1 << _type.length(); + _context << _type.length(); ArrayUtils(_context).convertLengthToSize(_type); - _context << Instruction::ADD << Instruction::SWAP1; + // stack: storage_ref slot_count if (_type.baseType()->storageBytes() < 32) ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256()); else @@ -623,13 +432,12 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const } // stack: ref old_length convertLengthToSize(_type); - // compute data positions + // stack: ref slot_count m_context << Instruction::SWAP1; CompilerUtils(m_context).computeHashStatic(); - // stack: len data_pos - m_context << Instruction::SWAP1 << Instruction::DUP2 << Instruction::ADD - << Instruction::SWAP1; - // stack: data_pos_end data_pos + // stack: slot_count data_pos + m_context << Instruction::SWAP1; + // stack: data_pos slot_count if (_type.storageStride() < 32) clearStorageLoop(TypeProvider::uint256()); else @@ -639,156 +447,6 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const m_context << Instruction::POP; } -void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const -{ - Type const* type = &_typeIn; - m_context.callLowLevelFunction( - "$resizeDynamicArray_" + _typeIn.identifier(), - 2, - 0, - [type](CompilerContext& _context) - { - ArrayType const& _type = dynamic_cast(*type); - solAssert(_type.location() == DataLocation::Storage, ""); - solAssert(_type.isDynamicallySized(), ""); - if (!_type.isByteArrayOrString() && _type.baseType()->storageBytes() < 32) - solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type."); - - unsigned stackHeightStart = _context.stackHeight(); - evmasm::AssemblyItem resizeEnd = _context.newTag(); - - // stack: ref new_length - // fetch old length - ArrayUtils(_context).retrieveLength(_type, 1); - // stack: ref new_length old_length - solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "2"); - - // Special case for short byte arrays, they are stored together with their length - if (_type.isByteArrayOrString()) - { - evmasm::AssemblyItem regularPath = _context.newTag(); - // We start by a large case-distinction about the old and new length of the byte array. - - _context << Instruction::DUP3 << Instruction::SLOAD; - // stack: ref new_length current_length ref_value - - solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - _context << Instruction::DUP2 << u256(31) << Instruction::LT; - evmasm::AssemblyItem currentIsLong = _context.appendConditionalJump(); - _context << Instruction::DUP3 << u256(31) << Instruction::LT; - evmasm::AssemblyItem newIsLong = _context.appendConditionalJump(); - - // Here: short -> short - - // Compute 1 << (256 - 8 * new_size) - evmasm::AssemblyItem shortToShort = _context.newTag(); - _context << shortToShort; - _context << Instruction::DUP3 << u256(8) << Instruction::MUL; - _context << u256(0x100) << Instruction::SUB; - _context << u256(2) << Instruction::EXP; - // Divide and multiply by that value, clearing bits. - _context << Instruction::DUP1 << Instruction::SWAP2; - _context << Instruction::DIV << Instruction::MUL; - // Insert 2*length. - _context << Instruction::DUP3 << Instruction::DUP1 << Instruction::ADD; - _context << Instruction::OR; - // Store. - _context << Instruction::DUP4 << Instruction::SSTORE; - solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "3"); - _context.appendJumpTo(resizeEnd); - - _context.adjustStackOffset(1); // we have to do that because of the jumps - // Here: short -> long - - _context << newIsLong; - // stack: ref new_length current_length ref_value - solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - // Zero out lower-order byte. - _context << u256(0xff) << Instruction::NOT << Instruction::AND; - // Store at data location. - _context << Instruction::DUP4; - CompilerUtils(_context).computeHashStatic(); - _context << Instruction::SSTORE; - // stack: ref new_length current_length - // Store new length: Compute 2*length + 1 and store it. - _context << Instruction::DUP2 << Instruction::DUP1 << Instruction::ADD; - _context << u256(1) << Instruction::ADD; - // stack: ref new_length current_length 2*new_length+1 - _context << Instruction::DUP4 << Instruction::SSTORE; - solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "3"); - _context.appendJumpTo(resizeEnd); - - _context.adjustStackOffset(1); // we have to do that because of the jumps - - _context << currentIsLong; - _context << Instruction::DUP3 << u256(31) << Instruction::LT; - _context.appendConditionalJumpTo(regularPath); - - // Here: long -> short - // Read the first word of the data and store it on the stack. Clear the data location and - // then jump to the short -> short case. - - // stack: ref new_length current_length ref_value - solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - _context << Instruction::POP << Instruction::DUP3; - CompilerUtils(_context).computeHashStatic(); - _context << Instruction::DUP1 << Instruction::SLOAD << Instruction::SWAP1; - // stack: ref new_length current_length first_word data_location - _context << Instruction::DUP3; - ArrayUtils(_context).convertLengthToSize(_type); - _context << Instruction::DUP2 << Instruction::ADD << Instruction::SWAP1; - // stack: ref new_length current_length first_word data_location_end data_location - ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256()); - _context << Instruction::POP; - // stack: ref new_length current_length first_word - solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3"); - _context.appendJumpTo(shortToShort); - - _context << regularPath; - // stack: ref new_length current_length ref_value - _context << Instruction::POP; - } - - // Change of length for a regular array (i.e. length at location, data at KECCAK256(location)). - // stack: ref new_length old_length - // store new length - _context << Instruction::DUP2; - if (_type.isByteArrayOrString()) - // For a "long" byte array, store length as 2*length+1 - _context << Instruction::DUP1 << Instruction::ADD << u256(1) << Instruction::ADD; - _context << Instruction::DUP4 << Instruction::SSTORE; - // skip if size is not reduced - _context << Instruction::DUP2 << Instruction::DUP2 - << Instruction::GT << Instruction::ISZERO; - _context.appendConditionalJumpTo(resizeEnd); - - // size reduced, clear the end of the array - // stack: ref new_length old_length - ArrayUtils(_context).convertLengthToSize(_type); - _context << Instruction::DUP2; - ArrayUtils(_context).convertLengthToSize(_type); - // stack: ref new_length old_size new_size - // compute data positions - _context << Instruction::DUP4; - CompilerUtils(_context).computeHashStatic(); - // stack: ref new_length old_size new_size data_pos - _context << Instruction::SWAP2 << Instruction::DUP3 << Instruction::ADD; - // stack: ref new_length data_pos new_size delete_end - _context << Instruction::SWAP2 << Instruction::ADD; - // stack: ref new_length delete_end delete_start - if (_type.storageStride() < 32) - ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256()); - else - ArrayUtils(_context).clearStorageLoop(_type.baseType()); - - _context << resizeEnd; - // cleanup - _context << Instruction::POP << Instruction::POP << Instruction::POP; - solAssert(_context.stackHeight() == stackHeightStart - 2, ""); - } - ); -} - void ArrayUtils::incrementDynamicArraySize(ArrayType const& _type) const { solAssert(_type.location() == DataLocation::Storage, ""); @@ -936,29 +594,32 @@ void ArrayUtils::clearStorageLoop(Type const* _type) const _context << Instruction::POP; return; } - // stack: end_pos pos - + // stack: start_pos slot_count + // Initialize loop counter i = 0 + _context << u256(0); + // stack: start_pos slot_count i evmasm::AssemblyItem loopStart = _context.appendJumpToNew(); _context << loopStart; - // check for loop condition - _context << - Instruction::DUP1 << - Instruction::DUP3 << - Instruction::GT << - Instruction::ISZERO; + // check for loop condition: !(slot_count > i) = (i >= slot_count) + _context << Instruction::DUP1 << Instruction::DUP3 << Instruction::GT << Instruction::ISZERO; evmasm::AssemblyItem zeroLoopEnd = _context.newTag(); _context.appendConditionalJumpTo(zeroLoopEnd); - // delete + // stack: start_pos slot_count i + // compute storage position: start_pos + i + _context << Instruction::DUP3 << Instruction::DUP2 << Instruction::ADD; + // stack: start_pos slot_count i (start_pos+i) + // delete storage slot _context << u256(0); - StorageItem(_context, *_type).setToZero(SourceLocation(), false); - _context << Instruction::POP; - // increment + StorageItem(_context, *_type).setToZero(SourceLocation(), /* _removeReference = */ true); + // stack: start_pos slot_count i + // increment counter: i += storageSize _context << _type->storageSize() << Instruction::ADD; _context.appendJumpTo(loopStart); // cleanup _context << zeroLoopEnd; - _context << Instruction::POP; - + // stack: start_pos slot_count i + _context << Instruction::POP << Instruction::POP; + // stack: start_pos solAssert(_context.stackHeight() == stackHeightStart - 1, ""); } ); diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h index 326084cb63..a62df8a7b2 100644 --- a/libsolidity/codegen/ArrayUtils.h +++ b/libsolidity/codegen/ArrayUtils.h @@ -61,10 +61,6 @@ class ArrayUtils /// Stack pre: reference (excludes byte offset) /// Stack post: void clearDynamicArray(ArrayType const& _type) const; - /// Changes the size of a dynamic array and clears the tail if it is shortened. - /// Stack pre: reference (excludes byte offset) new_length - /// Stack post: - void resizeDynamicArray(ArrayType const& _type) const; /// Increments the size of a dynamic array by one. /// Does not touch the new data element. In case of a byte array, this might move the /// data. @@ -76,9 +72,9 @@ class ArrayUtils /// Stack pre: reference /// Stack post: void popStorageArrayElement(ArrayType const& _type) const; - /// Appends a loop that clears a sequence of storage slots of the given type (excluding end). - /// Stack pre: end_ref start_ref - /// Stack post: end_ref + /// Appends a loop that clears a sequence of storage slots of the given type. + /// Stack pre: start_ref slot_count + /// Stack post: start_ref void clearStorageLoop(Type const* _type) const; /// Converts length to size (number of storage slots or calldata/memory bytes). /// if @a _pad then add padding to multiples of 32 bytes for calldata/memory. From eb4149477d6663fc61b59eacf5b4df4125a6c710 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Wed, 17 Dec 2025 13:49:14 -0300 Subject: [PATCH 113/243] via-ir: Fix handling of storage array boundaries (deletion and cleanup) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Kirchner Co-authored-by: Kamil Śliwak --- libsolidity/codegen/YulUtilFunctions.cpp | 43 +++++++++++++++--------- libsolidity/codegen/YulUtilFunctions.h | 11 +++--- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index ffeaa790e1..4c0a702692 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1419,14 +1419,13 @@ std::string YulUtilFunctions::cleanUpStorageArrayEndFunction(ArrayType const& _t let newSlotCount := (startIndex) let arrayDataStart := (array) let deleteStart := add(arrayDataStart, newSlotCount) - let deleteEnd := add(arrayDataStart, oldSlotCount) // if we are dealing with packed array and offset is greater than zero // we have to partially clear last slot that is still used, so decreasing start by one let offset := mul(mod(startIndex, ), ) if gt(offset, 0) { (sub(deleteStart, 1), offset) } - (deleteStart, deleteEnd) + (deleteStart, sub(oldSlotCount, newSlotCount)) } )") ("convertToSize", arrayConvertLengthToSize(_type)) @@ -1474,11 +1473,17 @@ std::string YulUtilFunctions::cleanUpDynamicByteArrayEndSlotsFunction(ArrayType _args = {"array", "len", "startIndex"}; return Whiskers(R"( if gt(len, 31) { - let dataArea := (array) - let deleteStart := add(dataArea, (startIndex)) - // If we are clearing array to be short byte array, we want to clear only data starting from array data area. - if lt(startIndex, 32) { deleteStart := dataArea } - (deleteStart, add(dataArea, (len))) + if gt(len, startIndex) { + let dataArea := (array) + let oldSlotCount := (len) + let newSlotCount := (startIndex) + // If we are clearing array to be short byte array, we want to clear only data starting from array data area. + if lt(startIndex, 32) { + newSlotCount := 0 + } + let deleteStart := add(dataArea, newSlotCount) + (deleteStart, sub(oldSlotCount, newSlotCount)) + } } )") ("dataLocation", arrayDataAreaFunction(_type)) @@ -1496,14 +1501,21 @@ std::string YulUtilFunctions::decreaseByteArraySizeFunction(ArrayType const& _ty function (array, data, oldLen, newLen) { switch lt(newLen, 32) case 0 { + // NOTE: This branch (newLen >= 32) is currently unreachable from Solidity code. + // All delete operations use newLen=0, and assignment uses copyByteArrayToStorageFunction. + // However, we maintain correct logic here for future-proofing and consistency. + let newSlots := (newLen) + let oldSlots := (oldLen) let arrayDataStart := (array) - let deleteStart := add(arrayDataStart, (newLen)) + let deleteStart := add(arrayDataStart, newSlots) // we have to partially clear last slot that is still used let offset := and(newLen, 0x1f) if offset { (sub(deleteStart, 1), offset) } - (deleteStart, add(arrayDataStart, (oldLen))) + if gt(oldSlots, newSlots) { + (deleteStart, sub(oldSlots, newSlots)) + } sstore(array, or(mul(2, newLen), 1)) } @@ -1511,8 +1523,9 @@ std::string YulUtilFunctions::decreaseByteArraySizeFunction(ArrayType const& _ty switch gt(oldLen, 31) case 1 { let arrayDataStart := (array) - // clear whole old array, as we are transforming to short bytes array - (add(arrayDataStart, 1), add(arrayDataStart, (oldLen))) + // Clear whole old array, as we are transforming to short bytes array. + // () will clear the first slot after copying its content. + (add(arrayDataStart, 1), sub((oldLen), 1)) (array, newLen) } default { @@ -1826,10 +1839,10 @@ std::string YulUtilFunctions::clearStorageRangeFunction(Type const& _type) return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( - function (start, end) { - for {} lt(start, end) { start := add(start, ) } + function (startSlot, slotCount) { + for { let i := 0 } lt(i, slotCount) { i := add(i, ) } { - (start, 0) + (add(startSlot, i), 0) } } )") @@ -1861,7 +1874,7 @@ std::string YulUtilFunctions::clearStorageArrayFunction(ArrayType const& _type) (slot, 0) - (slot, add(slot, ())) + (slot, ()) } )") diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index c28ba855c8..f4172017b4 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -266,8 +266,11 @@ class YulUtilFunctions std::string storageArrayPushZeroFunction(ArrayType const& _type); /// @returns the name of a function that will clear the storage area given - /// by the start and end (exclusive) parameters (slots). - /// signature: (start, end) + /// by the start position and number of slots to clear. The start position is in terms of storage slots and we + /// assume that the beginning of the clear range starts at the beginning of the start slot. + /// `slot_count` is assumed to be a multiple of `_type.storageSize()`. The function clears storage in increments + /// of `_type.storageSize()` and does not perform any runtime checks. + /// signature: (start_slot, slot_count) std::string clearStorageRangeFunction(Type const& _type); /// @returns the name of a function that will clear the given storage array @@ -297,7 +300,7 @@ class YulUtilFunctions /// The function reverts for too large lengths. std::string arrayAllocationSizeFunction(ArrayType const& _type); - /// @returns the name of a function that converts a storage slot number + /// @returns the name of a function that converts a storage slot number, /// a memory pointer or a calldata pointer to the slot number / memory pointer / calldata pointer /// for the data position of an array which is stored in that slot / memory area / calldata area. std::string arrayDataAreaFunction(ArrayType const& _type); @@ -618,7 +621,7 @@ class YulUtilFunctions std::string cleanUpDynamicByteArrayEndSlotsFunction(ArrayType const& _type); /// @returns the name of a function that increases size of byte array - /// when we resize byte array frextractUsedSetLenom < 32 elements to >= 32 elements or we push to byte array of size 31 copying of data will occur + /// when we resize byte array from < 32 elements to >= 32 elements or we push to byte array of size 31 copying of data will occur /// signature: (array, data, oldLen, newLen) std::string increaseByteArraySizeFunction(ArrayType const& _type); From 4f0e20cf36e1107141f5b9aef13adddb1e05c03d Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Wed, 17 Dec 2025 13:48:21 -0300 Subject: [PATCH 114/243] Add test for arrays at storage boundaries and extend coverage of legacy and via-ir on deletion, partial assigments and copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Kirchner Co-authored-by: Kamil Śliwak --- test/libsolidity/SolidityOptimizer.cpp | 4 +- .../abi_decode_simple_storage.sol | 4 +- .../abiEncoderV1/abi_decode_v2_storage.sol | 4 +- .../struct/struct_storage_ptr.sol | 6 +- .../calldata_overlapped_dynamic_arrays.sol | 6 +- .../abiEncoderV2/storage_array_encoding.sol | 10 +- .../arrays_complex_from_and_to_storage.sol | 6 +- .../array/bytes_length_member.sol | 4 +- .../array/constant_var_as_array_length.sol | 4 +- .../copying/array_copy_calldata_storage.sol | 6 +- .../copying/array_copy_cleanup_uint128.sol | 6 +- .../copying/array_copy_cleanup_uint40.sol | 6 +- .../copying/array_copy_clear_storage.sol | 6 +- .../array_copy_clear_storage_packed.sol | 12 +- .../copying/array_copy_different_packing.sol | 4 +- .../copying/array_copy_including_array.sol | 12 +- .../array/copying/array_copy_nested_array.sol | 6 +- ...ay_copy_storage_storage_different_base.sol | 4 +- ..._storage_storage_different_base_nested.sol | 6 +- .../array_copy_storage_storage_dyn_dyn.sol | 6 +- ...y_copy_storage_storage_dynamic_dynamic.sol | 4 +- ...ay_copy_storage_storage_static_dynamic.sol | 4 +- ...ray_copy_storage_storage_static_simple.sol | 4 +- ...ray_copy_storage_storage_static_static.sol | 6 +- .../array_copy_storage_storage_struct.sol | 6 +- .../copying/array_copy_target_leftover.sol | 6 +- .../copying/array_copy_target_leftover2.sol | 6 +- .../copying/array_copy_target_simple.sol | 6 +- .../copying/array_copy_target_simple_2.sol | 6 +- .../copying/array_elements_to_mapping.sol | 6 +- .../array_nested_calldata_to_storage.sol | 4 +- .../array_nested_memory_to_storage.sol | 14 +- ...on_external_storage_to_storage_dynamic.sol | 6 +- ...o_storage_dynamic_different_mutability.sol | 6 +- .../array_of_struct_calldata_to_storage.sol | 2 +- ..._containing_arrays_calldata_to_storage.sol | 2 +- .../array/copying/array_to_mapping.sol | 6 +- .../copying/arrays_from_and_to_storage.sol | 4 +- .../array/copying/bytes_inside_mappings.sol | 7 +- .../copying/bytes_storage_to_storage.sol | 23 +-- .../calldata_array_dynamic_to_storage.sol | 6 +- ...nup_during_multi_element_per_slot_copy.sol | 12 +- .../copy_byte_array_in_struct_to_storage.sol | 9 +- .../copying/copy_byte_array_to_storage.sol | 6 +- .../copy_function_internal_storage_array.sol | 6 +- ...opy_internal_function_array_to_storage.sol | 4 +- .../array/copying/copy_removes_bytes_data.sol | 3 +- .../copying/copying_bytes_multiassign.sol | 6 +- ...d_array_of_structs_calldata_to_storage.sol | 6 +- ...ted_array_of_structs_memory_to_storage.sol | 6 +- .../function_type_array_to_storage.sol | 12 +- .../memory_dyn_2d_bytes_to_storage.sol | 6 +- ...sted_array_element_calldata_to_storage.sol | 2 +- ...nested_array_element_memory_to_storage.sol | 2 +- ...ested_array_element_storage_to_storage.sol | 12 +- ...d_array_of_structs_calldata_to_storage.sol | 6 +- ...ted_array_of_structs_memory_to_storage.sol | 6 +- ...amic_array_element_calldata_to_storage.sol | 4 +- .../copying/storage_memory_nested_bytes.sol | 2 +- .../array/delete/bytes_delete_element.sol | 4 +- .../array/delete/delete_bytes_array.sol | 1 + .../delete/delete_storage_array_packed.sol | 6 +- .../array/fixed_array_cleanup.sol | 6 +- ...nvalid_encoding_for_storage_byte_array.sol | 6 +- .../long_byte_array_cleanup_after_delete.sol | 77 ++++++++ ...rray_cleanup_after_overwrite_with_long.sol | 105 +++++++++++ .../array/nested_calldata_storage2.sol | 2 +- .../array/pop/array_pop_array_transition.sol | 6 +- .../push/array_push_nested_from_calldata.sol | 4 +- .../array/push/array_push_struct.sol | 4 +- .../array/push/nested_bytes_push.sol | 2 +- .../copy_from_calldata_removes_bytes_data.sol | 3 +- .../cleanup/byte_array_to_storage_cleanup.sol | 13 +- .../constructor/arrays_in_constructors.sol | 4 +- .../bytes_in_constructors_packer.sol | 6 +- .../bytes_in_constructors_unpacker.sol | 6 +- .../constructor_static_array_argument.sol | 4 +- .../event_dynamic_nested_array_storage_v2.sol | 2 +- .../fallback/call_forward_bytes.sol | 1 + .../storage/empty_nonempty_empty.sol | 5 +- .../storage/static_array_copy_cleanup.sol | 94 ++++++++++ ...ray_and_partial_assignment_with_layout.sol | 54 ++++++ .../storage_boundary_array_assignment.sol | 28 +++ .../storage/storage_boundary_array_copy.sol | 61 +++++++ .../storage/storage_boundary_array_delete.sol | 34 ++++ ...dary_array_delete_overlapping_variable.sol | 40 ++++ ...array_packing_not_overlapping_variable.sol | 63 +++++++ ...rage_boundary_array_partial_assignment.sol | 41 +++++ .../storage_boundary_delete_overflow_bug.sol | 80 ++++++++ .../storage/storage_boundary_packed_array.sol | 35 ++++ ...rage_boundary_struct_array_mixed_types.sol | 172 ++++++++++++++++++ ...torage_boundary_struct_array_multislot.sol | 128 +++++++++++++ .../storage_boundary_struct_array_packed.sol | 128 +++++++++++++ .../storage/storage_packed_array_copy.sol | 35 ++++ .../state_variable_dynamic_array.sol | 6 +- .../state_variable_mapping.sol | 2 +- .../structs/copy_from_mapping.sol | 4 +- .../copy_struct_array_from_storage.sol | 4 +- .../copy_substructures_from_mapping.sol | 4 +- .../structs/copy_substructures_to_mapping.sol | 12 +- .../semanticTests/structs/copy_to_mapping.sol | 12 +- ...truct_containing_bytes_copy_and_delete.sol | 4 +- .../struct_delete_storage_nested_small.sol | 2 +- .../struct_delete_storage_with_array.sol | 12 +- ...truct_delete_storage_with_arrays_small.sol | 2 +- .../mapping/copy_from_mapping_to_mapping.sol | 4 +- .../userDefinedValueType/calldata.sol | 12 +- .../calldata_to_storage.sol | 10 +- .../memory_to_storage.sol | 8 +- .../semanticTests/various/address_code.sol | 6 +- .../semanticTests/various/create_calldata.sol | 6 +- .../various/destructuring_assignment.sol | 6 +- .../skip_dynamic_types_for_structs.sol | 4 +- ...alldata_struct_array_to_storage_legacy.sol | 16 ++ ..._memory_struct_array_to_storage_legacy.sol | 17 ++ 115 files changed, 1502 insertions(+), 277 deletions(-) create mode 100644 test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_delete.sol create mode 100644 test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_overwrite_with_long.sol create mode 100644 test/libsolidity/semanticTests/storage/static_array_copy_cleanup.sol create mode 100644 test/libsolidity/semanticTests/storage/storage_boundary_array_and_partial_assignment_with_layout.sol create mode 100644 test/libsolidity/semanticTests/storage/storage_boundary_array_assignment.sol create mode 100644 test/libsolidity/semanticTests/storage/storage_boundary_array_copy.sol create mode 100644 test/libsolidity/semanticTests/storage/storage_boundary_array_delete.sol create mode 100644 test/libsolidity/semanticTests/storage/storage_boundary_array_delete_overlapping_variable.sol create mode 100644 test/libsolidity/semanticTests/storage/storage_boundary_array_packing_not_overlapping_variable.sol create mode 100644 test/libsolidity/semanticTests/storage/storage_boundary_array_partial_assignment.sol create mode 100644 test/libsolidity/semanticTests/storage/storage_boundary_delete_overflow_bug.sol create mode 100644 test/libsolidity/semanticTests/storage/storage_boundary_packed_array.sol create mode 100644 test/libsolidity/semanticTests/storage/storage_boundary_struct_array_mixed_types.sol create mode 100644 test/libsolidity/semanticTests/storage/storage_boundary_struct_array_multislot.sol create mode 100644 test/libsolidity/semanticTests/storage/storage_boundary_struct_array_packed.sol create mode 100644 test/libsolidity/semanticTests/storage/storage_packed_array_copy.sol create mode 100644 test/libsolidity/syntaxTests/array/copy_calldata_struct_array_to_storage_legacy.sol create mode 100644 test/libsolidity/syntaxTests/array/copy_memory_struct_array_to_storage_legacy.sol diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index 832299baff..b3116a3722 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -634,8 +634,8 @@ BOOST_AUTO_TEST_CASE(optimise_multi_stores) )"; compileBothVersions(sourceCode); compareVersions("f()"); - BOOST_CHECK_EQUAL(numInstructions(m_nonOptimizedBytecode, Instruction::SSTORE), 8); - BOOST_CHECK_EQUAL(numInstructions(m_optimizedBytecode, Instruction::SSTORE), 7); + BOOST_CHECK_EQUAL(numInstructions(m_nonOptimizedBytecode, Instruction::SSTORE), 9); + BOOST_CHECK_EQUAL(numInstructions(m_optimizedBytecode, Instruction::SSTORE), 6); } BOOST_AUTO_TEST_CASE(optimise_constant_to_codecopy) diff --git a/test/libsolidity/semanticTests/abiEncodeDecode/abi_decode_simple_storage.sol b/test/libsolidity/semanticTests/abiEncodeDecode/abi_decode_simple_storage.sol index 54ce693d5c..6c4fd52b36 100644 --- a/test/libsolidity/semanticTests/abiEncodeDecode/abi_decode_simple_storage.sol +++ b/test/libsolidity/semanticTests/abiEncodeDecode/abi_decode_simple_storage.sol @@ -8,6 +8,6 @@ contract C { } // ---- // f(bytes): 0x20, 0x80, 0x21, 0x40, 0x7, "abcdefg" -> 0x21, 0x40, 0x7, "abcdefg" -// gas irOptimized: 135499 +// gas irOptimized: 135441 // gas legacy: 137095 -// gas legacyOptimized: 135823 +// gas legacyOptimized: 135828 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol index 5c3bcba971..e5aa910ce1 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol @@ -21,6 +21,6 @@ contract C { } // ---- // f() -> 0x20, 0x8, 0x40, 0x3, 0x9, 0xa, 0xb -// gas irOptimized: 203167 +// gas irOptimized: 203109 // gas legacy: 206263 -// gas legacyOptimized: 203172 +// gas legacyOptimized: 203177 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/struct/struct_storage_ptr.sol b/test/libsolidity/semanticTests/abiEncoderV1/struct/struct_storage_ptr.sol index ed983c77b5..ff98c66ca9 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/struct/struct_storage_ptr.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/struct/struct_storage_ptr.sol @@ -24,6 +24,6 @@ contract C { // ---- // library: L // f() -> 8, 7, 1, 2, 7, 12 -// gas irOptimized: 166761 -// gas legacy: 169273 -// gas legacyOptimized: 167243 +// gas irOptimized: 166766 +// gas legacy: 170486 +// gas legacyOptimized: 167252 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_overlapped_dynamic_arrays.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_overlapped_dynamic_arrays.sol index aa1e494c21..302aeb5de4 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_overlapped_dynamic_arrays.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_overlapped_dynamic_arrays.sol @@ -33,8 +33,8 @@ contract C { // f_which(uint256[],uint256[2],uint256): 0x40, 1, 2, 1, 5, 6 -> 0x20, 0x40, 5, 2 // f_which(uint256[],uint256[2],uint256): 0x40, 1, 2, 1 -> FAILURE // f_storage(uint256[],uint256[2]): 0x20, 1, 2 -> 0x20, 0x60, 0x20, 1, 2 -// gas irOptimized: 111409 -// gas legacy: 112707 -// gas legacyOptimized: 111845 +// gas irOptimized: 111356 +// gas legacy: 113826 +// gas legacyOptimized: 111724 // f_storage(uint256[],uint256[2]): 0x40, 1, 2, 5, 6 -> 0x20, 0x80, 0x20, 2, 5, 6 // f_storage(uint256[],uint256[2]): 0x40, 1, 2, 5 -> FAILURE diff --git a/test/libsolidity/semanticTests/abiEncoderV2/storage_array_encoding.sol b/test/libsolidity/semanticTests/abiEncoderV2/storage_array_encoding.sol index 9599af6c88..31f759d3bf 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/storage_array_encoding.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/storage_array_encoding.sol @@ -18,10 +18,10 @@ contract C { // EVMVersion: >homestead // ---- // h(uint256[2][]): 0x20, 3, 123, 124, 223, 224, 323, 324 -> 32, 256, 0x20, 3, 123, 124, 223, 224, 323, 324 -// gas irOptimized: 180080 -// gas legacy: 184233 -// gas legacyOptimized: 180856 +// gas irOptimized: 180022 +// gas legacy: 186541 +// gas legacyOptimized: 180429 // i(uint256[2][2]): 123, 124, 223, 224 -> 32, 128, 123, 124, 223, 224 // gas irOptimized: 112031 -// gas legacy: 115091 -// gas legacyOptimized: 112657 +// gas legacy: 116683 +// gas legacyOptimized: 112303 diff --git a/test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol b/test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol index a9d08ec0c0..ac3cd8495f 100644 --- a/test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol +++ b/test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol @@ -12,9 +12,9 @@ contract Test { } // ---- // set(uint24[3][]): 0x20, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12 -> 0x06 -// gas irOptimized: 185216 -// gas legacy: 211054 -// gas legacyOptimized: 206077 +// gas irOptimized: 185221 +// gas legacy: 199970 +// gas legacyOptimized: 186342 // data(uint256,uint256): 0x02, 0x02 -> 0x09 // data(uint256,uint256): 0x05, 0x01 -> 0x11 // data(uint256,uint256): 0x06, 0x00 -> FAILURE diff --git a/test/libsolidity/semanticTests/array/bytes_length_member.sol b/test/libsolidity/semanticTests/array/bytes_length_member.sol index 5159538bc1..85b9282c66 100644 --- a/test/libsolidity/semanticTests/array/bytes_length_member.sol +++ b/test/libsolidity/semanticTests/array/bytes_length_member.sol @@ -13,7 +13,7 @@ contract c { // ---- // getLength() -> 0 // set(): 1, 2 -> true -// gas irOptimized: 110422 +// gas irOptimized: 110393 // gas legacy: 110951 -// gas legacyOptimized: 110576 +// gas legacyOptimized: 110577 // getLength() -> 68 diff --git a/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol b/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol index 342f3218fc..833c9dec3e 100644 --- a/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol +++ b/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol @@ -10,9 +10,9 @@ contract C { // constructor(): 1, 2, 3 -> // gas irOptimized: 124991 // gas irOptimized code: 14800 -// gas legacy: 134317 +// gas legacy: 142553 // gas legacy code: 46200 -// gas legacyOptimized: 127166 +// gas legacyOptimized: 126306 // gas legacyOptimized code: 23400 // a(uint256): 0 -> 1 // a(uint256): 1 -> 2 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_calldata_storage.sol b/test/libsolidity/semanticTests/array/copying/array_copy_calldata_storage.sol index ecfe565f12..8131e7434d 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_calldata_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_calldata_storage.sol @@ -20,7 +20,7 @@ contract c { } // ---- // store(uint256[9],uint8[3][]): 21, 22, 23, 24, 25, 26, 27, 28, 29, 0x140, 4, 1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33 -> 32 -// gas irOptimized: 647725 -// gas legacy: 694354 -// gas legacyOptimized: 693849 +// gas irOptimized: 647730 +// gas legacy: 659420 +// gas legacyOptimized: 648899 // retrieve() -> 9, 28, 9, 28, 4, 3, 32 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint128.sol b/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint128.sol index e1cfa7ce7e..ba05d8fb67 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint128.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint128.sol @@ -21,6 +21,6 @@ contract C { } // ---- // f() -> true -// gas irOptimized: 73992 -// gas legacy: 74395 -// gas legacyOptimized: 73794 +// gas irOptimized: 73981 +// gas legacy: 75550 +// gas legacyOptimized: 73812 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint40.sol b/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint40.sol index d65f4f37c1..e4e7bf62f3 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint40.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_cleanup_uint40.sol @@ -46,6 +46,6 @@ contract C { } // ---- // f() -> true -// gas irOptimized: 122541 -// gas legacy: 124643 -// gas legacyOptimized: 122801 +// gas irOptimized: 122596 +// gas legacy: 125651 +// gas legacyOptimized: 121668 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage.sol b/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage.sol index e9f1448a27..f206b432c9 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage.sol @@ -13,6 +13,6 @@ contract C { } // ---- // f() -> 0 -// gas irOptimized: 108229 -// gas legacy: 108216 -// gas legacyOptimized: 107625 +// gas irOptimized: 108096 +// gas legacy: 110351 +// gas legacyOptimized: 107708 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage_packed.sol b/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage_packed.sol index 43827773c1..12d9bf1fd3 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage_packed.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_clear_storage_packed.sol @@ -40,11 +40,11 @@ contract C { } // ---- // f() -> 0 -// gas irOptimized: 74056 -// gas legacy: 74372 -// gas legacyOptimized: 73796 +// gas irOptimized: 74044 +// gas legacy: 75528 +// gas legacyOptimized: 73861 // g() -> 0 // h() -> 0 -// gas irOptimized: 74126 -// gas legacy: 74398 -// gas legacyOptimized: 73833 +// gas irOptimized: 74114 +// gas legacy: 75553 +// gas legacyOptimized: 73904 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_different_packing.sol b/test/libsolidity/semanticTests/array/copying/array_copy_different_packing.sol index ee564d01a2..1bbb541911 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_different_packing.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_different_packing.sol @@ -19,5 +19,5 @@ contract c { // ---- // test() -> 0x01000000000000000000000000000000000000000000000000, 0x02000000000000000000000000000000000000000000000000, 0x03000000000000000000000000000000000000000000000000, 0x04000000000000000000000000000000000000000000000000, 0x05000000000000000000000000000000000000000000000000 // gas irOptimized: 208415 -// gas legacy: 220711 -// gas legacyOptimized: 220097 +// gas legacy: 220059 +// gas legacyOptimized: 211458 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol b/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol index bb621fcc84..ba50c7300f 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol @@ -35,12 +35,12 @@ contract c { } // ---- // test() -> 0x02000202 -// gas irOptimized: 4549676 -// gas legacy: 4475394 -// gas legacyOptimized: 4447665 +// gas irOptimized: 4560468 +// gas legacy: 4536566 +// gas legacyOptimized: 4456759 // storageEmpty -> 1 // clear() -> 0, 0 -// gas irOptimized: 4478184 -// gas legacy: 4407185 -// gas legacyOptimized: 4381337 +// gas irOptimized: 4488719 +// gas legacy: 4407780 +// gas legacyOptimized: 4385089 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_nested_array.sol b/test/libsolidity/semanticTests/array/copying/array_copy_nested_array.sol index 2d325a804b..34d6c75ef8 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_nested_array.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_nested_array.sol @@ -12,6 +12,6 @@ contract c { } // ---- // test(uint256[2][]): 32, 3, 7, 8, 9, 10, 11, 12 -> 10 -// gas irOptimized: 689552 -// gas legacy: 686176 -// gas legacyOptimized: 685611 +// gas irOptimized: 689539 +// gas legacy: 713478 +// gas legacyOptimized: 690424 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base.sol index 54639df55d..af31d4b8e4 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base.sol @@ -18,5 +18,5 @@ contract c { // ---- // test() -> 5, 4 // gas irOptimized: 205667 -// gas legacy: 213863 -// gas legacyOptimized: 212901 +// gas legacy: 207971 +// gas legacyOptimized: 204828 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base_nested.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base_nested.sol index ad745ff420..6b1dd7ff02 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base_nested.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_different_base_nested.sol @@ -21,6 +21,6 @@ contract c { } // ---- // test() -> 3, 4 -// gas irOptimized: 169669 -// gas legacy: 175415 -// gas legacyOptimized: 172533 +// gas irOptimized: 169553 +// gas legacy: 182361 +// gas legacyOptimized: 169554 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol index a38685404d..98e2f5adb4 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol @@ -15,9 +15,9 @@ contract c { // ---- // setData1(uint256,uint256,uint256): 10, 5, 4 -> // copyStorageStorage() -> -// gas irOptimized: 111350 -// gas legacy: 109272 -// gas legacyOptimized: 109262 +// gas irOptimized: 111321 +// gas legacy: 112434 +// gas legacyOptimized: 111494 // getData2(uint256): 5 -> 10, 4 // setData1(uint256,uint256,uint256): 0, 0, 0 -> // copyStorageStorage() -> diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dynamic_dynamic.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dynamic_dynamic.sol index 83df3aef8f..5a66704b55 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dynamic_dynamic.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dynamic_dynamic.sol @@ -18,5 +18,5 @@ contract c { // ---- // test() -> 5, 4 // gas irOptimized: 253591 -// gas legacy: 250892 -// gas legacyOptimized: 250045 +// gas legacy: 253664 +// gas legacyOptimized: 252200 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_dynamic.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_dynamic.sol index 94e57c96da..29d8bc0ebe 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_dynamic.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_dynamic.sol @@ -12,5 +12,5 @@ contract c { // ---- // test() -> 9, 4 // gas irOptimized: 123180 -// gas legacy: 123566 -// gas legacyOptimized: 123202 +// gas legacy: 124642 +// gas legacyOptimized: 123345 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_simple.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_simple.sol index 84642c1439..29e62dd3ce 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_simple.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_simple.sol @@ -11,5 +11,5 @@ contract C { } // ---- // test() -> left(0x01), left(0x02) -// gas legacy: 89698 -// gas legacyOptimized: 88928 +// gas legacy: 69235 +// gas legacyOptimized: 66974 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_static.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_static.sol index d1f2cbdedc..65ce656548 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_static.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_static_static.sol @@ -14,6 +14,6 @@ contract c { } // ---- // test() -> 8, 0 -// gas irOptimized: 196251 -// gas legacy: 194842 -// gas legacyOptimized: 194281 +// gas irOptimized: 196370 +// gas legacy: 210073 +// gas legacyOptimized: 196759 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol index e638f2dad1..f1cd76a3ad 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol @@ -17,7 +17,7 @@ contract c { } // ---- // test() -> 4, 5 -// gas irOptimized: 190628 -// gas legacy: 190828 -// gas legacyOptimized: 189657 +// gas irOptimized: 190676 +// gas legacy: 210706 +// gas legacyOptimized: 190472 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover.sol b/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover.sol index 22105f6429..2deb372fa3 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover.sol @@ -17,6 +17,6 @@ contract c { } // ---- // test() -> 0xffffffff, 0x0000000000000000000000000a00090008000700060005000400030002000100, 0x0000000000000000000000000000000000000000000000000000000000000000 -// gas irOptimized: 100496 -// gas legacy: 158109 -// gas legacyOptimized: 141019 +// gas irOptimized: 100506 +// gas legacy: 146700 +// gas legacyOptimized: 122680 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover2.sol b/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover2.sol index dd49c7a97b..706130082f 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover2.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_target_leftover2.sol @@ -19,6 +19,6 @@ contract c { } // ---- // test() -> 0x04000000000000000000000000000000000000000000000000, 0x0, 0x0 -// gas irOptimized: 91348 -// gas legacy: 96648 -// gas legacyOptimized: 93841 +// gas irOptimized: 91320 +// gas legacy: 97777 +// gas legacyOptimized: 91993 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_target_simple.sol b/test/libsolidity/semanticTests/array/copying/array_copy_target_simple.sol index 27fb9f0c57..054d61c677 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_target_simple.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_target_simple.sol @@ -18,6 +18,6 @@ contract c { } // ---- // test() -> 0x01000000000000000000000000000000000000000000000000, 0x02000000000000000000000000000000000000000000000000, 0x03000000000000000000000000000000000000000000000000, 0x04000000000000000000000000000000000000000000000000, 0x0 -// gas irOptimized: 273543 -// gas legacy: 282601 -// gas legacyOptimized: 281510 +// gas irOptimized: 273548 +// gas legacy: 280148 +// gas legacyOptimized: 274502 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_target_simple_2.sol b/test/libsolidity/semanticTests/array/copying/array_copy_target_simple_2.sol index 980e414d14..7ceadf481c 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_target_simple_2.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_target_simple_2.sol @@ -18,6 +18,6 @@ contract c { } // ---- // test() -> 0x01000000000000000000000000000000000000000000000000, 0x02000000000000000000000000000000000000000000000000, 0x03000000000000000000000000000000000000000000000000, 0x04000000000000000000000000000000000000000000000000, 0x00 -// gas irOptimized: 232995 -// gas legacy: 235694 -// gas legacyOptimized: 235193 +// gas irOptimized: 233012 +// gas legacy: 239400 +// gas legacyOptimized: 233948 diff --git a/test/libsolidity/semanticTests/array/copying/array_elements_to_mapping.sol b/test/libsolidity/semanticTests/array/copying/array_elements_to_mapping.sol index becc3d4cde..a1849bd65f 100644 --- a/test/libsolidity/semanticTests/array/copying/array_elements_to_mapping.sol +++ b/test/libsolidity/semanticTests/array/copying/array_elements_to_mapping.sol @@ -52,9 +52,9 @@ contract C { } // ---- // from_storage() -> 0x20, 2, 0x40, 0xa0, 2, 10, 11, 3, 12, 13, 14 -// gas irOptimized: 149868 -// gas legacy: 150737 -// gas legacyOptimized: 148690 +// gas irOptimized: 149856 +// gas legacy: 156721 +// gas legacyOptimized: 149000 // from_storage_ptr() -> 0x20, 2, 0x40, 0xa0, 2, 10, 11, 3, 12, 13, 14 // from_memory() -> 0x20, 2, 0x40, 0xa0, 2, 10, 11, 3, 12, 13, 14 // from_calldata(uint8[][]): 0x20, 2, 0x40, 0xa0, 2, 10, 11, 3, 12, 13, 14 -> 0x20, 2, 0x40, 0xa0, 2, 10, 11, 3, 12, 13, 14 diff --git a/test/libsolidity/semanticTests/array/copying/array_nested_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/array_nested_calldata_to_storage.sol index 5d4e5b45e4..ffa0c68adc 100644 --- a/test/libsolidity/semanticTests/array/copying/array_nested_calldata_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/array_nested_calldata_to_storage.sol @@ -38,10 +38,10 @@ contract c { // compileViaYul: true // ---- // test1(uint256[][]): 0x20, 2, 0x40, 0x40, 2, 23, 42 -> 2, 65 -// gas irOptimized: 181029 +// gas irOptimized: 180918 // test2(uint256[][2]): 0x20, 0x40, 0x40, 2, 23, 42 -> 2, 65 // gas irOptimized: 157604 // test3(uint256[2][]): 0x20, 2, 23, 42, 23, 42 -> 2, 65 -// gas irOptimized: 134801 +// gas irOptimized: 134685 // test4(uint256[2][2]): 23, 42, 23, 42 -> 65 // gas irOptimized: 111177 diff --git a/test/libsolidity/semanticTests/array/copying/array_nested_memory_to_storage.sol b/test/libsolidity/semanticTests/array/copying/array_nested_memory_to_storage.sol index b892ec991c..933a1ab1b1 100644 --- a/test/libsolidity/semanticTests/array/copying/array_nested_memory_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/array_nested_memory_to_storage.sol @@ -38,12 +38,14 @@ contract Test { } // ---- // test() -> 24 -// gas irOptimized: 226734 -// gas legacy: 227083 -// gas legacyOptimized: 226529 +// gas irOptimized: 226647 +// gas legacy: 229060 +// gas legacyOptimized: 226495 // test1() -> 3 // test2() -> 6 +// gas irOptimized: 95905 +// gas legacy: 100519 // test3() -> 24 -// gas irOptimized: 141319 -// gas legacy: 142238 -// gas legacyOptimized: 141365 +// gas irOptimized: 141297 +// gas legacy: 146668 +// gas legacyOptimized: 141331 diff --git a/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic.sol b/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic.sol index ab3c172d33..e3b9cdaadc 100644 --- a/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic.sol +++ b/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic.sol @@ -45,7 +45,7 @@ contract C { } // ---- // copyExternalStorageArrayOfFunctionType() -> true -// gas irOptimized: 104566 -// gas legacy: 108554 -// gas legacyOptimized: 102405 +// gas irOptimized: 104629 +// gas legacy: 111170 +// gas legacyOptimized: 104620 // copyInternalArrayOfFunctionType() -> true diff --git a/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic_different_mutability.sol b/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic_different_mutability.sol index d121b22d49..92b61b9291 100644 --- a/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic_different_mutability.sol +++ b/test/libsolidity/semanticTests/array/copying/array_of_function_external_storage_to_storage_dynamic_different_mutability.sol @@ -49,7 +49,7 @@ contract C { // ---- // copyExternalStorageArraysOfFunctionType() -> true // gas irOptimized: 104238 -// gas legacy: 108295 -// gas legacyOptimized: 102162 +// gas legacy: 110911 +// gas legacyOptimized: 104377 // copyInternalArrayOfFunctionType() -> true -// gas legacy: 36192 +// gas legacy: 38695 diff --git a/test/libsolidity/semanticTests/array/copying/array_of_struct_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/array_of_struct_calldata_to_storage.sol index 3147401cfa..e4b3d14fd7 100644 --- a/test/libsolidity/semanticTests/array/copying/array_of_struct_calldata_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/array_of_struct_calldata_to_storage.sol @@ -17,4 +17,4 @@ contract C { // compileViaYul: true // ---- // f((uint128,uint64,uint128)[]): 0x20, 3, 0, 0, 12, 0, 11, 0, 10, 0, 0 -> 10, 11, 12 -// gas irOptimized: 120747 +// gas irOptimized: 120751 diff --git a/test/libsolidity/semanticTests/array/copying/array_of_structs_containing_arrays_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/array_of_structs_containing_arrays_calldata_to_storage.sol index 09037719a6..acc9683892 100644 --- a/test/libsolidity/semanticTests/array/copying/array_of_structs_containing_arrays_calldata_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/array_of_structs_containing_arrays_calldata_to_storage.sol @@ -23,4 +23,4 @@ contract C { // compileViaYul: true // ---- // f((uint256[])[]): 0x20, 3, 0x60, 0x60, 0x60, 0x20, 3, 1, 2, 3 -> 3, 1 -// gas irOptimized: 327456 +// gas irOptimized: 327461 diff --git a/test/libsolidity/semanticTests/array/copying/array_to_mapping.sol b/test/libsolidity/semanticTests/array/copying/array_to_mapping.sol index c5519e83aa..667685c9c8 100644 --- a/test/libsolidity/semanticTests/array/copying/array_to_mapping.sol +++ b/test/libsolidity/semanticTests/array/copying/array_to_mapping.sol @@ -37,8 +37,8 @@ contract C { } // ---- // from_storage() -> 0x20, 2, 0x40, 0xa0, 2, 10, 11, 3, 12, 13, 14 -// gas irOptimized: 147755 -// gas legacy: 148892 -// gas legacyOptimized: 146917 +// gas irOptimized: 147749 +// gas legacy: 154218 +// gas legacyOptimized: 147020 // from_storage_ptr() -> 0x20, 2, 0x40, 0xa0, 2, 10, 11, 3, 12, 13, 14 // from_memory() -> 0x20, 2, 0x40, 0xa0, 2, 10, 11, 3, 12, 13, 14 diff --git a/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol b/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol index 93b541620a..fea2c895d8 100644 --- a/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/arrays_from_and_to_storage.sol @@ -11,8 +11,8 @@ contract Test { // ---- // set(uint24[]): 0x20, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 -> 18 // gas irOptimized: 95505 -// gas legacy: 103509 -// gas legacyOptimized: 101266 +// gas legacy: 104176 +// gas legacyOptimized: 96785 // data(uint256): 7 -> 8 // data(uint256): 15 -> 16 // data(uint256): 18 -> FAILURE diff --git a/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol b/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol index 90012eed4c..4ab381f4e6 100644 --- a/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol +++ b/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol @@ -5,17 +5,20 @@ contract c { } // ---- // set(uint256): 1, 2 -> true -// gas irOptimized: 110550 +// gas irOptimized: 110560 // gas legacy: 111310 // gas legacyOptimized: 110741 // set(uint256): 2, 2, 3, 4, 5 -> true -// gas irOptimized: 177501 +// gas irOptimized: 177511 // gas legacy: 178312 // gas legacyOptimized: 177716 // storageEmpty -> 0 // copy(uint256,uint256): 1, 2 -> true +// gas irOptimized: 48421 // storageEmpty -> 0 // copy(uint256,uint256): 99, 1 -> true +// gas irOptimized: 35641 // storageEmpty -> 0 // copy(uint256,uint256): 99, 2 -> true +// gas irOptimized: 35641 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol b/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol index d00f5573a0..37769cb496 100644 --- a/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol @@ -19,23 +19,24 @@ contract c { // f(uint256): 31 -> 0x20, 0x1f, 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e00 // gas irOptimized: 103268 // gas legacy: 112904 -// gas legacyOptimized: 112647 +// gas legacyOptimized: 112645 // f(uint256): 32 -> 0x20, 0x20, 1780731860627700044960722568376592200742329637303199754547598369979440671 // gas irOptimized: 117825 // gas legacy: 128964 -// gas legacyOptimized: 128854 +// gas legacyOptimized: 128859 // f(uint256): 33 -> 0x20, 33, 1780731860627700044960722568376592200742329637303199754547598369979440671, 0x2000000000000000000000000000000000000000000000000000000000000000 -// gas irOptimized: 124091 -// gas legacy: 136092 -// gas legacyOptimized: 135469 +// gas irOptimized: 123888 +// gas legacy: 135510 +// gas legacyOptimized: 135218 // f(uint256): 63 -> 0x20, 0x3f, 1780731860627700044960722568376592200742329637303199754547598369979440671, 14532552714582660066924456880521368950258152170031413196862950297402215316992 -// gas irOptimized: 127151 -// gas legacy: 148692 -// gas legacyOptimized: 148699 +// gas irOptimized: 126948 +// gas legacy: 148110 +// gas legacyOptimized: 148448 // f(uint256): 12 -> 0x20, 0x0c, 0x0102030405060708090a0b0000000000000000000000000000000000000000 -// gas legacy: 59751 -// gas legacyOptimized: 57208 +// gas irOptimized: 54204 +// gas legacy: 59832 +// gas legacyOptimized: 57268 // f(uint256): 129 -> 0x20, 0x81, 1780731860627700044960722568376592200742329637303199754547598369979440671, 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f, 29063324697304692433803953038474361308315562010425523193971352996434451193439, 0x606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f, -57896044618658097711785492504343953926634992332820282019728792003956564819968 // gas irOptimized: 416918 // gas legacy: 458997 -// gas legacyOptimized: 460664 +// gas legacyOptimized: 460669 diff --git a/test/libsolidity/semanticTests/array/copying/calldata_array_dynamic_to_storage.sol b/test/libsolidity/semanticTests/array/copying/calldata_array_dynamic_to_storage.sol index 3df22c7251..4008347728 100644 --- a/test/libsolidity/semanticTests/array/copying/calldata_array_dynamic_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/calldata_array_dynamic_to_storage.sol @@ -9,6 +9,6 @@ contract C { } // ---- // f(uint256[]): 0x20, 0x03, 0x1, 0x2, 0x3 -> 0x1 -// gas irOptimized: 111084 -// gas legacy: 111548 -// gas legacyOptimized: 111321 +// gas irOptimized: 110973 +// gas legacy: 112436 +// gas legacyOptimized: 111313 diff --git a/test/libsolidity/semanticTests/array/copying/cleanup_during_multi_element_per_slot_copy.sol b/test/libsolidity/semanticTests/array/copying/cleanup_during_multi_element_per_slot_copy.sol index e6284072d1..cdc3c4f667 100644 --- a/test/libsolidity/semanticTests/array/copying/cleanup_during_multi_element_per_slot_copy.sol +++ b/test/libsolidity/semanticTests/array/copying/cleanup_during_multi_element_per_slot_copy.sol @@ -16,10 +16,10 @@ contract C { } // ---- // constructor() -// gas irOptimized: 90065 -// gas irOptimized code: 149000 -// gas legacy: 89553 -// gas legacy code: 126200 -// gas legacyOptimized: 83556 -// gas legacyOptimized code: 98200 +// gas irOptimized: 89349 +// gas irOptimized code: 140200 +// gas legacy: 100902 +// gas legacy code: 271400 +// gas legacyOptimized: 83062 +// gas legacyOptimized code: 91800 // f() -> 0 diff --git a/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol b/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol index 787973c5f0..9a68f88d89 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol @@ -37,10 +37,11 @@ contract C { // f() -> 0x40, 0x80, 6, 0x6162636465660000000000000000000000000000000000000000000000000000, 0x49, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738390000000000000000000000000000000000000000000000 // gas irOptimized: 179405 // gas legacy: 180675 -// gas legacyOptimized: 179686 +// gas legacyOptimized: 179690 // g() -> 0x40, 0xc0, 0x49, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738390000000000000000000000000000000000000000000000, 0x11, 0x3132333435363738393233343536373839000000000000000000000000000000 -// gas irOptimized: 106338 -// gas legacy: 109415 -// gas legacyOptimized: 106600 +// gas irOptimized: 106381 +// gas legacy: 109472 +// gas legacyOptimized: 106648 // h() -> 0x40, 0x60, 0x00, 0x00 +// gas irOptimized: 46948 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/copying/copy_byte_array_to_storage.sol b/test/libsolidity/semanticTests/array/copying/copy_byte_array_to_storage.sol index fc274bf47b..caa0596610 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_byte_array_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_byte_array_to_storage.sol @@ -46,6 +46,6 @@ contract C { } // ---- // f() -> 0xff -// gas irOptimized: 143857 -// gas legacy: 153404 -// gas legacyOptimized: 146676 +// gas irOptimized: 143935 +// gas legacy: 153487 +// gas legacyOptimized: 146748 diff --git a/test/libsolidity/semanticTests/array/copying/copy_function_internal_storage_array.sol b/test/libsolidity/semanticTests/array/copying/copy_function_internal_storage_array.sol index 7db82f891d..5fc9813409 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_function_internal_storage_array.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_function_internal_storage_array.sol @@ -15,6 +15,6 @@ contract C { } // ---- // test() -> 7 -// gas irOptimized: 122459 -// gas legacy: 205176 -// gas legacyOptimized: 204971 +// gas irOptimized: 122575 +// gas legacy: 208558 +// gas legacyOptimized: 202409 diff --git a/test/libsolidity/semanticTests/array/copying/copy_internal_function_array_to_storage.sol b/test/libsolidity/semanticTests/array/copying/copy_internal_function_array_to_storage.sol index 95ba9d8ed8..6c1d061d30 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_internal_function_array_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_internal_function_array_to_storage.sol @@ -18,6 +18,6 @@ contract C { } // ---- // one() -> 3 -// gas legacy: 140253 -// gas legacyOptimized: 140093 +// gas legacy: 142687 +// gas legacyOptimized: 135991 // two() -> FAILURE, hex"4e487b71", 0x51 diff --git a/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol b/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol index 46216e912c..6872995d1c 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol @@ -9,7 +9,8 @@ contract c { // set(): 1, 2, 3, 4, 5 -> true // gas irOptimized: 177344 // gas legacy: 177953 -// gas legacyOptimized: 177550 +// gas legacyOptimized: 177551 // storageEmpty -> 0 // reset() -> true +// gas irOptimized: 47341 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/copying/copying_bytes_multiassign.sol b/test/libsolidity/semanticTests/array/copying/copying_bytes_multiassign.sol index ce4344e871..d29feccf42 100644 --- a/test/libsolidity/semanticTests/array/copying/copying_bytes_multiassign.sol +++ b/test/libsolidity/semanticTests/array/copying/copying_bytes_multiassign.sol @@ -18,13 +18,15 @@ contract sender { } // ---- // (): 7 -> -// gas irOptimized: 110822 +// gas irOptimized: 110735 // gas legacy: 111388 -// gas legacyOptimized: 111065 +// gas legacyOptimized: 111066 // val() -> 0 // forward(bool): true -> true +// gas irOptimized: 49573 // val() -> 0x80 // forward(bool): false -> true +// gas irOptimized: 31410 // val() -> 0x80 // forward(bool): true -> true // val() -> 0x80 diff --git a/test/libsolidity/semanticTests/array/copying/elements_of_nested_array_of_structs_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/elements_of_nested_array_of_structs_calldata_to_storage.sol index 672e7435f1..5c64ba63f8 100644 --- a/test/libsolidity/semanticTests/array/copying/elements_of_nested_array_of_structs_calldata_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/elements_of_nested_array_of_structs_calldata_to_storage.sol @@ -31,8 +31,8 @@ contract C { // compileViaYul: true // ---- // test1((uint8[],uint8[2])[][][]): 0x20, 1, 0x20, 2, 0x40, 0x0140, 1, 0x20, 0x60, 3, 7, 2, 1, 2, 2, 0x40, 0x0100, 0x60, 17, 19, 2, 11, 13, 0x60, 31, 37, 2, 23, 29 -> 0x20, 2, 0x40, 0x0140, 1, 0x20, 0x60, 3, 7, 2, 1, 2, 2, 0x40, 0x0100, 0x60, 17, 19, 2, 11, 13, 0x60, 31, 37, 2, 23, 29 -// gas irOptimized: 327921 +// gas irOptimized: 327936 // test2((uint8[],uint8[2])[][1][]): 0x20, 2, 0x40, 0x0160, 0x20, 1, 0x20, 0x60, 17, 19, 2, 11, 13, 0x20, 1, 0x20, 0x60, 31, 37, 2, 23, 29 -> 0x20, 0x20, 1, 0x20, 0x60, 17, 19, 2, 11, 13 -// gas irOptimized: 141162 +// gas irOptimized: 141168 // test3((uint8[],uint8[2])[1][][2]): 0x20, 0x40, 0x60, 0, 2, 0x40, 288, 0x20, 0x60, 3, 7, 2, 1, 2, 0x20, 0x60, 17, 19, 2, 11, 13 -> 0x20, 2, 0x40, 288, 0x20, 0x60, 3, 7, 2, 1, 2, 0x20, 0x60, 17, 19, 2, 11, 13 -// gas irOptimized: 188442 +// gas irOptimized: 188448 diff --git a/test/libsolidity/semanticTests/array/copying/elements_of_nested_array_of_structs_memory_to_storage.sol b/test/libsolidity/semanticTests/array/copying/elements_of_nested_array_of_structs_memory_to_storage.sol index f232d8aba9..12c40849fe 100644 --- a/test/libsolidity/semanticTests/array/copying/elements_of_nested_array_of_structs_memory_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/elements_of_nested_array_of_structs_memory_to_storage.sol @@ -31,8 +31,8 @@ contract C { // compileViaYul: true // ---- // test1((uint8[],uint8[2])[][][]): 0x20, 1, 0x20, 2, 0x40, 0x0140, 1, 0x20, 0x60, 3, 7, 2, 1, 2, 2, 0x40, 0x0100, 0x60, 17, 19, 2, 11, 13, 0x60, 31, 37, 2, 23, 29 -> 0x20, 2, 0x40, 0x0140, 1, 0x20, 0x60, 3, 7, 2, 1, 2, 2, 0x40, 0x0100, 0x60, 17, 19, 2, 11, 13, 0x60, 31, 37, 2, 23, 29 -// gas irOptimized: 332567 +// gas irOptimized: 332582 // test2((uint8[],uint8[2])[][1][]): 0x20, 2, 0x40, 0x0160, 0x20, 1, 0x20, 0x60, 17, 19, 2, 11, 13, 0x20, 1, 0x20, 0x60, 31, 37, 2, 23, 29 -> 0x20, 0x20, 1, 0x20, 0x60, 17, 19, 2, 11, 13 -// gas irOptimized: 145409 +// gas irOptimized: 145415 // test3((uint8[],uint8[2])[1][][2]): 0x20, 0x40, 0x60, 0, 2, 0x40, 288, 0x20, 0x60, 3, 7, 2, 1, 2, 0x20, 0x60, 17, 19, 2, 11, 13 -> 0x20, 2, 0x40, 288, 0x20, 0x60, 3, 7, 2, 1, 2, 0x20, 0x60, 17, 19, 2, 11, 13 -// gas irOptimized: 192248 +// gas irOptimized: 192254 diff --git a/test/libsolidity/semanticTests/array/copying/function_type_array_to_storage.sol b/test/libsolidity/semanticTests/array/copying/function_type_array_to_storage.sol index 640046a11a..a5cf480112 100644 --- a/test/libsolidity/semanticTests/array/copying/function_type_array_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/function_type_array_to_storage.sol @@ -46,11 +46,11 @@ contract C { } // ---- // test() -> 0x20, 0x14, "[a called][b called]" -// gas irOptimized: 116518 -// gas legacy: 118841 -// gas legacyOptimized: 116843 +// gas irOptimized: 116512 +// gas legacy: 120226 +// gas legacyOptimized: 116941 // test2() -> 0x20, 0x14, "[b called][a called]" // test3() -> 0x20, 0x14, "[b called][a called]" -// gas irOptimized: 103144 -// gas legacy: 102654 -// gas legacyOptimized: 101556 +// gas irOptimized: 103138 +// gas legacy: 105192 +// gas legacyOptimized: 103752 diff --git a/test/libsolidity/semanticTests/array/copying/memory_dyn_2d_bytes_to_storage.sol b/test/libsolidity/semanticTests/array/copying/memory_dyn_2d_bytes_to_storage.sol index 8a4d870b22..93eb84427d 100644 --- a/test/libsolidity/semanticTests/array/copying/memory_dyn_2d_bytes_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/memory_dyn_2d_bytes_to_storage.sol @@ -18,6 +18,6 @@ contract C { } // ---- // f() -> 3 -// gas irOptimized: 128296 -// gas legacy: 129077 -// gas legacyOptimized: 128210 +// gas irOptimized: 128301 +// gas legacy: 129528 +// gas legacyOptimized: 128171 diff --git a/test/libsolidity/semanticTests/array/copying/nested_array_element_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/nested_array_element_calldata_to_storage.sol index 89eeb27dce..f9d14b59ed 100644 --- a/test/libsolidity/semanticTests/array/copying/nested_array_element_calldata_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/nested_array_element_calldata_to_storage.sol @@ -26,4 +26,4 @@ contract C { // test(uint8[2][2][2]): 1, 2, 3, 4, 5, 6, 7, 8 // test2(uint8[2][2]): 1, 2, 3, 4 // gas irOptimized: 72076 -// gas legacyOptimized: 95103 +// gas legacyOptimized: 72086 diff --git a/test/libsolidity/semanticTests/array/copying/nested_array_element_memory_to_storage.sol b/test/libsolidity/semanticTests/array/copying/nested_array_element_memory_to_storage.sol index bd74ae0158..d604a11e6c 100644 --- a/test/libsolidity/semanticTests/array/copying/nested_array_element_memory_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/nested_array_element_memory_to_storage.sol @@ -26,4 +26,4 @@ contract C { // test(uint8[2][2][2]): 1, 2, 3, 4, 5, 6, 7, 8 // test2(uint8[2][2]): 1, 2, 3, 4 // gas irOptimized: 73031 -// gas legacyOptimized: 96288 +// gas legacyOptimized: 73155 diff --git a/test/libsolidity/semanticTests/array/copying/nested_array_element_storage_to_storage.sol b/test/libsolidity/semanticTests/array/copying/nested_array_element_storage_to_storage.sol index f3934fec0f..1a3fb70e08 100644 --- a/test/libsolidity/semanticTests/array/copying/nested_array_element_storage_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/nested_array_element_storage_to_storage.sol @@ -71,14 +71,14 @@ contract C { // ---- // test1() -> // gas irOptimized: 150488 -// gas legacy: 150949 -// gas legacyOptimized: 150906 +// gas legacy: 156275 +// gas legacyOptimized: 151018 // test2() -> FAILURE // gas irOptimized: 150389 -// gas legacy: 150672 -// gas legacyOptimized: 150575 +// gas legacy: 155998 +// gas legacyOptimized: 150684 // test3() -> // gas irOptimized: 124300 -// gas legacy: 125333 -// gas legacyOptimized: 125127 +// gas legacy: 130649 +// gas legacyOptimized: 125142 // test4() -> FAILURE diff --git a/test/libsolidity/semanticTests/array/copying/nested_array_of_structs_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/nested_array_of_structs_calldata_to_storage.sol index 7621b2477e..b27fe9e064 100644 --- a/test/libsolidity/semanticTests/array/copying/nested_array_of_structs_calldata_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/nested_array_of_structs_calldata_to_storage.sol @@ -29,8 +29,8 @@ contract C { // compileViaYul: true // ---- // test1((uint8[],uint8[2])[][]): 0x20, 2, 0x40, 0x0140, 1, 0x20, 0x60, 3, 7, 2, 1, 2, 2, 0x40, 0x0100, 0x60, 17, 19, 2, 11, 13, 0x60, 31, 37, 2, 23, 29 -> 0x20, 2, 0x40, 0x0140, 1, 0x20, 0x60, 3, 7, 2, 1, 2, 2, 0x40, 0x0100, 0x60, 17, 19, 2, 11, 13, 0x60, 31, 37, 2, 23, 29 -// gas irOptimized: 304788 +// gas irOptimized: 304750 // test2((uint8[],uint8[2])[][1]): 0x20, 0x20, 1, 0x20, 0x60, 17, 19, 2, 11, 13 -> 0x20, 0x20, 1, 0x20, 0x60, 17, 19, 2, 11, 13 -// gas irOptimized: 116653 +// gas irOptimized: 116659 // test3((uint8[],uint8[2])[1][]): 0x20, 2, 0x40, 0x0120, 0x20, 0x60, 3, 7, 2, 1, 2, 0x20, 0x60, 17, 19, 2, 11, 13 -> 0x20, 2, 0x40, 0x0120, 0x20, 0x60, 3, 7, 2, 1, 2, 0x20, 0x60, 17, 19, 2, 11, 13 -// gas irOptimized: 187994 +// gas irOptimized: 187942 diff --git a/test/libsolidity/semanticTests/array/copying/nested_array_of_structs_memory_to_storage.sol b/test/libsolidity/semanticTests/array/copying/nested_array_of_structs_memory_to_storage.sol index 8ec3f77691..146de2f2af 100644 --- a/test/libsolidity/semanticTests/array/copying/nested_array_of_structs_memory_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/nested_array_of_structs_memory_to_storage.sol @@ -29,8 +29,8 @@ contract C { // compileViaYul: true // ---- // test1((uint8[],uint8[2])[][]): 0x20, 2, 0x40, 0x0140, 1, 0x20, 0x60, 3, 7, 2, 1, 2, 2, 0x40, 0x0100, 0x60, 17, 19, 2, 11, 13, 0x60, 31, 37, 2, 23, 29 -> 0x20, 2, 0x40, 0x0140, 1, 0x20, 0x60, 3, 7, 2, 1, 2, 2, 0x40, 0x0100, 0x60, 17, 19, 2, 11, 13, 0x60, 31, 37, 2, 23, 29 -// gas irOptimized: 309072 +// gas irOptimized: 309034 // test2((uint8[],uint8[2])[][1]): 0x20, 0x20, 1, 0x20, 0x60, 17, 19, 2, 11, 13 -> 0x20, 0x20, 1, 0x20, 0x60, 17, 19, 2, 11, 13 -// gas irOptimized: 118314 +// gas irOptimized: 118320 // test3((uint8[],uint8[2])[1][]): 0x20, 2, 0x40, 0x0120, 0x20, 0x60, 3, 7, 2, 1, 2, 0x20, 0x60, 17, 19, 2, 11, 13 -> 0x20, 2, 0x40, 0x0120, 0x20, 0x60, 3, 7, 2, 1, 2, 0x20, 0x60, 17, 19, 2, 11, 13 -// gas irOptimized: 191045 +// gas irOptimized: 190993 diff --git a/test/libsolidity/semanticTests/array/copying/nested_dynamic_array_element_calldata_to_storage.sol b/test/libsolidity/semanticTests/array/copying/nested_dynamic_array_element_calldata_to_storage.sol index 57b3dc521f..7583f5ab4f 100644 --- a/test/libsolidity/semanticTests/array/copying/nested_dynamic_array_element_calldata_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/nested_dynamic_array_element_calldata_to_storage.sol @@ -30,7 +30,7 @@ contract C { // compileViaYul: true // ---- // test(uint8[][][]): 0x20, 2, 0x40, 0x60, 0, 2, 0x40, 0x80, 1, 7, 2, 8, 9 -// gas irOptimized: 137897 +// gas irOptimized: 137891 // test2(uint8[][]): 0x20, 2, 0x40, 0x80, 1, 7, 2, 8, 9 -// gas irOptimized: 164482 +// gas irOptimized: 164249 // gas legacyOptimized: 120228 diff --git a/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol index e5a6ae53b2..3f52085f4b 100644 --- a/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol +++ b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol @@ -13,4 +13,4 @@ contract C { // f() -> 0x20, 0x02, 0x40, 0x80, 3, 0x6162630000000000000000000000000000000000000000000000000000000000, 0x99, 44048183304486788312148433451363384677562265908331949128489393215789685032262, 32241931068525137014058842823026578386641954854143559838526554899205067598957, 49951309422467613961193228765530489307475214998374779756599339590522149884499, 0x54555658595a6162636465666768696a6b6c6d6e6f707172737475767778797a, 0x4142434445464748494a4b4c4d4e4f5051525354555658595a00000000000000 // gas irOptimized: 202083 // gas legacy: 204327 -// gas legacyOptimized: 202899 +// gas legacyOptimized: 202903 diff --git a/test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol b/test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol index f776626ae8..03c47f69b4 100644 --- a/test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol +++ b/test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol @@ -16,6 +16,6 @@ contract c { } // ---- // test1() -> true -// gas irOptimized: 218449 +// gas irOptimized: 218420 // gas legacy: 242263 -// gas legacyOptimized: 241182 +// gas legacyOptimized: 241187 diff --git a/test/libsolidity/semanticTests/array/delete/delete_bytes_array.sol b/test/libsolidity/semanticTests/array/delete/delete_bytes_array.sol index b735d182d5..32a96c72c3 100644 --- a/test/libsolidity/semanticTests/array/delete/delete_bytes_array.sol +++ b/test/libsolidity/semanticTests/array/delete/delete_bytes_array.sol @@ -32,3 +32,4 @@ contract C { // ---- // f() -> 0 // g() -> 0 +// gas irOptimized: 56617 diff --git a/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol b/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol index f486a50cfa..23b0466e75 100644 --- a/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol +++ b/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol @@ -14,6 +14,6 @@ contract C { } // ---- // f() -> 0, 0, 0 -// gas irOptimized: 88026 -// gas legacy: 88800 -// gas legacyOptimized: 87700 +// gas irOptimized: 88028 +// gas legacy: 88799 +// gas legacyOptimized: 87706 diff --git a/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol b/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol index b4ba8b47fd..af0ec6b7fa 100644 --- a/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol +++ b/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol @@ -15,7 +15,7 @@ contract c { // gas legacyOptimized: 466238 // storageEmpty -> 0 // clear() -> -// gas irOptimized: 97705 -// gas legacy: 97919 -// gas legacyOptimized: 97790 +// gas irOptimized: 97800 +// gas legacy: 97947 +// gas legacyOptimized: 97882 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/invalid_encoding_for_storage_byte_array.sol b/test/libsolidity/semanticTests/array/invalid_encoding_for_storage_byte_array.sol index 95df74b4d7..a53d00eaaf 100644 --- a/test/libsolidity/semanticTests/array/invalid_encoding_for_storage_byte_array.sol +++ b/test/libsolidity/semanticTests/array/invalid_encoding_for_storage_byte_array.sol @@ -40,16 +40,18 @@ contract C { // copyFromStorageShort() // x() -> 0x20, 3, 0x6162630000000000000000000000000000000000000000000000000000000000 // copyFromStorageLong() -// gas irOptimized: 121095 +// gas irOptimized: 121092 // gas legacy: 121904 -// gas legacyOptimized: 121388 +// gas legacyOptimized: 121393 // x() -> 0x20, 0x25, 0x3132333435363738393031323334353637383930313233343536373839303132, 0x3334353637000000000000000000000000000000000000000000000000000000 // copyToStorage() // x() -> 0x20, 0x25, 0x3132333435363738393031323334353637383930313233343536373839303132, 0x3334353637000000000000000000000000000000000000000000000000000000 // y() -> 0x20, 0x25, 0x3132333435363738393031323334353637383930313233343536373839303132, 0x3334353637000000000000000000000000000000000000000000000000000000 // del() +// gas irOptimized: 29404 // x() -> 0x20, 0x00 // invalidateXLong() +// gas irOptimized: 47028 // x() -> FAILURE, hex"4e487b71", 0x22 // abiEncode() -> FAILURE, hex"4e487b71", 0x22 // abiEncodePacked() -> FAILURE, hex"4e487b71", 0x22 diff --git a/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_delete.sol b/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_delete.sol new file mode 100644 index 0000000000..b638664aac --- /dev/null +++ b/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_delete.sol @@ -0,0 +1,77 @@ +contract C { + bytes testArray; + + struct Canary { + uint256 value; + } + + function getCanary() internal pure returns (Canary storage canary) { + assembly { + mstore(0, testArray.slot) + let testArrayDataArea := keccak256(0, 0x20) + // testArray's data area occupies 3 slots when filled. Canary goes right after + canary.slot := add(testArrayDataArea, 3) + } + } + + constructor() { + Canary storage canary = getCanary(); + canary.value = type(uint256).max; + } + + function getArrayDataAreaSlot() public pure returns (uint256 slot) { + assembly { + mstore(0, testArray.slot) + slot := keccak256(0, 0x20) + } + assert(slot == 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563); + return slot; + } + + function getCanarySlot() public pure returns (uint256) { + return getArrayDataAreaSlot() + 3; + } + + function checkSlots() public view returns (uint256, uint256, uint256, uint256, uint256) { + uint256 dataSlot = getArrayDataAreaSlot(); + uint256 slot0; + uint256 slot1; + uint256 slot2; + uint256 slot3; + assembly { + slot0 := sload(dataSlot) + slot1 := sload(add(dataSlot, 1)) + slot2 := sload(add(dataSlot, 2)) + slot3 := sload(add(dataSlot, 3)) + } + return (slot0, slot1, slot2, slot3, testArray.length); + } + + function fillArray() public { + // Fill testArray to exactly 96 bytes (3 slots in its data area) + for (uint i = 0; i < 96; i++) { + testArray.push(bytes1(uint8(i + 1))); + } + } + + function deleteArray() public { + // Should clear 3 slots without touching canary + delete testArray; + } + + function canaryValue() public view returns (uint256) { + return getCanary().value; + } +} +// ---- +// getArrayDataAreaSlot() -> 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 +// getCanarySlot() -> 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e566 +// checkSlots() -> 0, 0, 0, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// fillArray() +// gas irOptimized: 197289 +// gas legacy: 220574 +// gas legacyOptimized: 206839 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// deleteArray() +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_overwrite_with_long.sol b/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_overwrite_with_long.sol new file mode 100644 index 0000000000..edb00809bc --- /dev/null +++ b/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_overwrite_with_long.sol @@ -0,0 +1,105 @@ +contract C { + bytes testArray; + + struct Canary { + uint256 value; + } + + function getCanary() internal pure returns (Canary storage canary) { + assembly { + mstore(0, testArray.slot) + let testArrayDataArea := keccak256(0, 0x20) + // testArray's data area occupies 3 slots when filled. Canary goes right after + canary.slot := add(testArrayDataArea, 3) + } + } + + constructor() { + Canary storage canary = getCanary(); + canary.value = type(uint256).max; // Should not be overwritten + } + + function fillArray() public { + // Fill testArray to exactly 96 bytes (3 slots in its data area) + for (uint i = 0; i < 96; i++) { + testArray.push(bytes1(uint8(i + 1))); + } + } + + function shrinkArray() public returns (uint256) { + // Shrink from 96 to 50 bytes. Should clear slot 2 without touching canary + bytes memory newData = new bytes(50); + for (uint i = 0; i < 50; i++) { + newData[i] = bytes1(uint8(i + 2)); + } + testArray = newData; + return testArray.length; + } + + function canaryValue() public view returns (uint256) { + return getCanary().value; + } + + function arrayLength() public view returns (uint256) { + return testArray.length; + } + + function getDataSlotContent(uint256 index) public view returns (bytes32 value) { + assembly { + mstore(0, testArray.slot) + let testArrayDataArea := keccak256(0, 0x20) + let slot := add(testArrayDataArea, index) + value := sload(slot) + } + return value; + } + + function checkSlots() public view returns (bytes32, bytes32, bytes32, uint256, uint256) { + return ( + getDataSlotContent(0), // First data slot + getDataSlotContent(1), // Second data slot (partial cleanup expected) + getDataSlotContent(2), // Third data slot (should be cleared after shrink) + canaryValue(), // Canary value (should never change) + arrayLength() // Current array length + ); + } + + function getSlot1LastBytes() public view returns (bytes14 lastBytes) { + // Get the last 14 bytes of slot 1, which should be zero after partial cleanup + bytes32 slot1 = getDataSlotContent(1); + assembly { + // Shift left by 18 bytes (144 bits) to move the last 14 bytes to the front + lastBytes := shl(144, slot1) + } + return lastBytes; + } + + function getArrayBytes(uint256 start, uint256 count) public view returns (bytes memory) { + bytes memory result = new bytes(count); + for (uint i = 0; i < count && start + i < testArray.length; i++) { + result[i] = testArray[start + i]; + } + return result; + } +} +// ---- +// arrayLength() ->0 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// fillArray() +// gas irOptimized: 197352 +// gas legacy: 220574 +// gas legacyOptimized: 206839 +// arrayLength() ->96 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// getArrayBytes(uint256,uint256): 0, 5 -> 0x20, 5, 0x0102030405000000000000000000000000000000000000000000000000000000 +// getArrayBytes(uint256,uint256): 32, 5 -> 0x20, 5, 0x2122232425000000000000000000000000000000000000000000000000000000 +// getArrayBytes(uint256,uint256): 64, 5 -> 0x20, 5, 0x4142434445000000000000000000000000000000000000000000000000000000 +// shrinkArray() -> 50 +// arrayLength() ->50 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// getArrayBytes(uint256,uint256): 0, 5 -> 0x20, 5, 0x0203040506000000000000000000000000000000000000000000000000000000 +// getArrayBytes(uint256,uint256): 32, 5 -> 0x20, 5, 0x2223242526000000000000000000000000000000000000000000000000000000 +// getArrayBytes(uint256,uint256): 45, 5 -> 0x20, 5, 0x2f30313233000000000000000000000000000000000000000000000000000000 +// getSlot1LastBytes() -> 0 +// getDataSlotContent(uint256): 2 -> 0 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/array/nested_calldata_storage2.sol b/test/libsolidity/semanticTests/array/nested_calldata_storage2.sol index f8db31b54c..f3cb511871 100644 --- a/test/libsolidity/semanticTests/array/nested_calldata_storage2.sol +++ b/test/libsolidity/semanticTests/array/nested_calldata_storage2.sol @@ -8,6 +8,6 @@ contract C { // compileViaYul: true // ---- // i(uint256[][]): 0x20, 2, 0x40, 0xC0, 3, 0x0A01, 0x0A02, 0x0A03, 4, 0x0B01, 0x0B02, 0x0B03, 0x0B04 -// gas irOptimized: 245506 +// gas irOptimized: 245511 // tmp_i(uint256,uint256): 0, 0 -> 0x0A01 // tmp_i(uint256,uint256): 1, 0 -> 0x0B01 diff --git a/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol b/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol index 96fb11a2dc..3b96397166 100644 --- a/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol +++ b/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol @@ -23,7 +23,7 @@ contract c { } // ---- // test() -> 1, 2, 3 -// gas irOptimized: 1828226 -// gas legacy: 1822464 -// gas legacyOptimized: 1813404 +// gas irOptimized: 1828383 +// gas legacy: 1841995 +// gas legacyOptimized: 1821687 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/push/array_push_nested_from_calldata.sol b/test/libsolidity/semanticTests/array/push/array_push_nested_from_calldata.sol index 77ae8c4e23..83c5c17243 100644 --- a/test/libsolidity/semanticTests/array/push/array_push_nested_from_calldata.sol +++ b/test/libsolidity/semanticTests/array/push/array_push_nested_from_calldata.sol @@ -13,5 +13,5 @@ contract C { // ---- // f(uint120[]): 0x20, 3, 1, 2, 3 -> 1 // gas irOptimized: 112852 -// gas legacy: 113657 -// gas legacyOptimized: 113465 +// gas legacy: 114404 +// gas legacyOptimized: 113087 diff --git a/test/libsolidity/semanticTests/array/push/array_push_struct.sol b/test/libsolidity/semanticTests/array/push/array_push_struct.sol index 92f071e1fb..4e1e8b4b3d 100644 --- a/test/libsolidity/semanticTests/array/push/array_push_struct.sol +++ b/test/libsolidity/semanticTests/array/push/array_push_struct.sol @@ -21,5 +21,5 @@ contract c { // ---- // test() -> 2, 3, 4, 5 // gas irOptimized: 135329 -// gas legacy: 147437 -// gas legacyOptimized: 146429 +// gas legacy: 139481 +// gas legacyOptimized: 135795 diff --git a/test/libsolidity/semanticTests/array/push/nested_bytes_push.sol b/test/libsolidity/semanticTests/array/push/nested_bytes_push.sol index 5633199d48..04b2a4a736 100644 --- a/test/libsolidity/semanticTests/array/push/nested_bytes_push.sol +++ b/test/libsolidity/semanticTests/array/push/nested_bytes_push.sol @@ -15,4 +15,4 @@ contract C { // f() -> // gas irOptimized: 179534 // gas legacy: 181013 -// gas legacyOptimized: 180397 +// gas legacyOptimized: 180406 diff --git a/test/libsolidity/semanticTests/calldata/copy_from_calldata_removes_bytes_data.sol b/test/libsolidity/semanticTests/calldata/copy_from_calldata_removes_bytes_data.sol index 33ab184506..c3f2e8d751 100644 --- a/test/libsolidity/semanticTests/calldata/copy_from_calldata_removes_bytes_data.sol +++ b/test/libsolidity/semanticTests/calldata/copy_from_calldata_removes_bytes_data.sol @@ -11,7 +11,8 @@ contract c { // (): 1, 2, 3, 4, 5 -> // gas irOptimized: 155122 // gas legacy: 155473 -// gas legacyOptimized: 155295 +// gas legacyOptimized: 155296 // checkIfDataIsEmpty() -> false // sendMessage() -> true, 0x40, 0 +// gas irOptimized: 41925 // checkIfDataIsEmpty() -> true diff --git a/test/libsolidity/semanticTests/cleanup/byte_array_to_storage_cleanup.sol b/test/libsolidity/semanticTests/cleanup/byte_array_to_storage_cleanup.sol index 238f96b7e5..4c75ad452b 100644 --- a/test/libsolidity/semanticTests/cleanup/byte_array_to_storage_cleanup.sol +++ b/test/libsolidity/semanticTests/cleanup/byte_array_to_storage_cleanup.sol @@ -28,13 +28,14 @@ contract C { // compileViaYul: also // ---- // constructor() -> -// gas irOptimized: 82100 -// gas irOptimized code: 357600 -// gas legacy: 101532 -// gas legacy code: 604800 -// gas legacyOptimized: 84956 -// gas legacyOptimized code: 391800 +// gas irOptimized: 82586 +// gas irOptimized code: 363600 +// gas legacy: 101811 +// gas legacy code: 608200 +// gas legacyOptimized: 85196 +// gas legacyOptimized code: 394800 // h() -> 0x20, 0x40, 0x00, 0 // ~ emit ev(uint256[],uint256): 0x40, 0x21, 0x02, 0x00, 0x00 // g() -> 0x20, 0x40, 0, 0x00 // f(bytes): 0x20, 33, 0, -1 -> 0x20, 0x22, 0, 0xff00000000000000000000000000000000000000000000000000000000000000 +// gas irOptimized: 54117 diff --git a/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol index aeeb1a0c1a..76a4dd961f 100644 --- a/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol +++ b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol @@ -28,7 +28,7 @@ contract Creator { // f(uint256,address[]): 7, 0x40, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 7, 8 // gas irOptimized: 327784 // gas irOptimized code: 94000 -// gas legacy: 336623 +// gas legacy: 338477 // gas legacy code: 244800 -// gas legacyOptimized: 329515 +// gas legacyOptimized: 329166 // gas legacyOptimized code: 117000 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol index 55abe4804c..91be8e3789 100644 --- a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol @@ -26,9 +26,9 @@ contract Creator { // bytecodeFormat: legacy,>=EOFv1 // ---- // f(uint256,bytes): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> 7, "h" -// gas irOptimized: 169292 +// gas irOptimized: 169297 // gas irOptimized code: 99600 -// gas legacy: 172941 +// gas legacy: 172946 // gas legacy code: 239800 -// gas legacyOptimized: 169815 +// gas legacyOptimized: 169823 // gas legacyOptimized code: 118600 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol index 13db633b9b..8a072f3ebe 100644 --- a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol @@ -10,11 +10,11 @@ contract Test { // bytecodeFormat: legacy,>=EOFv1 // ---- // constructor(): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> -// gas irOptimized: 181465 +// gas irOptimized: 181629 // gas irOptimized code: 78400 -// gas legacy: 195212 +// gas legacy: 195484 // gas legacy code: 109400 -// gas legacyOptimized: 181608 +// gas legacyOptimized: 181853 // gas legacyOptimized code: 71400 // m_x() -> 7 // m_s() -> 0x20, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" diff --git a/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol b/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol index ed4eb6e4d1..cc196a95c8 100644 --- a/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol +++ b/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol @@ -13,9 +13,9 @@ contract C { // constructor(): 1, 2, 3, 4 -> // gas irOptimized: 148129 // gas irOptimized code: 23000 -// gas legacy: 157977 +// gas legacy: 166201 // gas legacy code: 60400 -// gas legacyOptimized: 149973 +// gas legacyOptimized: 149177 // gas legacyOptimized code: 26200 // a() -> 1 // b(uint256): 0 -> 2 diff --git a/test/libsolidity/semanticTests/events/event_dynamic_nested_array_storage_v2.sol b/test/libsolidity/semanticTests/events/event_dynamic_nested_array_storage_v2.sol index 1ff58d65b5..42b9b50dd7 100644 --- a/test/libsolidity/semanticTests/events/event_dynamic_nested_array_storage_v2.sol +++ b/test/libsolidity/semanticTests/events/event_dynamic_nested_array_storage_v2.sol @@ -16,5 +16,5 @@ contract C { // createEvent(uint256): 42 -> // ~ emit E(uint256[][]): 0x20, 0x02, 0x40, 0xa0, 0x02, 0x2a, 0x2b, 0x02, 0x2c, 0x2d // gas irOptimized: 185148 -// gas legacy: 187493 +// gas legacy: 188615 // gas legacyOptimized: 184548 diff --git a/test/libsolidity/semanticTests/fallback/call_forward_bytes.sol b/test/libsolidity/semanticTests/fallback/call_forward_bytes.sol index 7bf3aee3ac..dfd3d658fe 100644 --- a/test/libsolidity/semanticTests/fallback/call_forward_bytes.sol +++ b/test/libsolidity/semanticTests/fallback/call_forward_bytes.sol @@ -20,6 +20,7 @@ contract sender { // forward() -> true // val() -> 8 // clear() -> true +// gas irOptimized: 29243 // val() -> 8 // forward() -> true // val() -> 0x80 diff --git a/test/libsolidity/semanticTests/storage/empty_nonempty_empty.sol b/test/libsolidity/semanticTests/storage/empty_nonempty_empty.sol index 8a795373e0..8d72f7c043 100644 --- a/test/libsolidity/semanticTests/storage/empty_nonempty_empty.sol +++ b/test/libsolidity/semanticTests/storage/empty_nonempty_empty.sol @@ -22,11 +22,12 @@ contract Test { // set(bytes): 0x20, 0 // storageEmpty -> 1 // set(bytes): 0x20, 66, "12345678901234567890123456789012", "12345678901234567890123456789012", "12" -// gas irOptimized: 111849 +// gas irOptimized: 111820 // gas legacy: 112734 -// gas legacyOptimized: 112084 +// gas legacyOptimized: 112089 // storageEmpty -> 0 // set(bytes): 0x20, 3, "abc" +// gas irOptimized: 33989 // storageEmpty -> 0 // set(bytes): 0x20, 0 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/storage/static_array_copy_cleanup.sol b/test/libsolidity/semanticTests/storage/static_array_copy_cleanup.sol new file mode 100644 index 0000000000..ca471161d5 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/static_array_copy_cleanup.sol @@ -0,0 +1,94 @@ +contract C { + struct S { + uint64 a; + uint64 b; + uint64 c; + uint64 d; + // All fit in one slot (4 * 64 = 256 bits) + } + + S[5] source; + S[10] dest; + uint256 public canary = type(uint256).max; + + function fillSource() public { + for (uint i = 0; i < 5; i++) { + source[i] = S({ + a: uint64(1 + i * 4), + b: uint64(2 + i * 4), + c: uint64(3 + i * 4), + d: uint64(4 + i * 4) + }); + } + } + + function fillDest() public { + for (uint i = 0; i < 10; i++) { + dest[i] = S({ + a: uint64(100 + i * 4), + b: uint64(101 + i * 4), + c: uint64(102 + i * 4), + d: uint64(103 + i * 4) + }); + } + } + + function copySourceToDest() public { + dest = source; + } + + function deleteSource() public { + delete source; + } + + function deleteDest() public { + delete dest; + } + + function getSourceAsUint() public view returns (uint64[20] memory result) { + for (uint i = 0; i < 5; i++) { + result[i * 4] = source[i].a; + result[i * 4 + 1] = source[i].b; + result[i * 4 + 2] = source[i].c; + result[i * 4 + 3] = source[i].d; + } + } + + function getDestAsUint() public view returns (uint64[40] memory result) { + for (uint i = 0; i < 10; i++) { + result[i * 4] = dest[i].a; + result[i * 4 + 1] = dest[i].b; + result[i * 4 + 2] = dest[i].c; + result[i * 4 + 3] = dest[i].d; + } + } +} +// ---- +// canary() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// getSourceAsUint() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// getDestAsUint() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// fillSource() +// gas irOptimized: 135018 +// gas legacy: 146851 +// gas legacyOptimized: 137549 +// canary() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// getSourceAsUint() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 +// fillDest() +// gas irOptimized: 248706 +// gas legacy: 272468 +// gas legacyOptimized: 253871 +// canary() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// getSourceAsUint() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 +// getDestAsUint() -> 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139 +// copySourceToDest() +// canary() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// getSourceAsUint() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 +// getDestAsUint() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// deleteSource() +// canary() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// getSourceAsUint() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// getDestAsUint() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// deleteDest() +// canary() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// getSourceAsUint() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// getDestAsUint() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_array_and_partial_assignment_with_layout.sol b/test/libsolidity/semanticTests/storage/storage_boundary_array_and_partial_assignment_with_layout.sol new file mode 100644 index 0000000000..1e2637c851 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_array_and_partial_assignment_with_layout.sol @@ -0,0 +1,54 @@ +contract C layout at 2**256 - 5 { + uint256 a; + + function getArray() internal pure returns (uint256[10][1] storage _x) { + assembly { + _x.slot := a.slot + } + } + + function fillArray() public { + uint256[10][1] storage _x = getArray(); + for (uint i = 1; i < 10; i++) + _x[0][i] = i; + } + + function partialAssignArrayBeforeStorageBoundary() public { + uint256[10][1] storage _x = getArray(); + _x[0] = [11, 12, 13]; + } + + function partialAssignArrayCrossStorageBoundary() public { + uint256[10][1] storage _x = getArray(); + _x[0] = [14, 15, 16, 17, 18, 19, 20]; + } + + function clearArray() public { + uint256[10][1] storage _x = getArray(); + delete _x[0]; + } + + function x() public view returns (uint256[10] memory) { + return getArray()[0]; + } +} +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// fillArray() +// gas irOptimized: 220749 +// gas legacy: 221473 +// gas legacyOptimized: 220915 +// partialAssignArrayBeforeStorageBoundary() +// x() -> 11, 12, 13, 0, 0, 0, 0, 0, 0, 0 +// fillArray() +// gas irOptimized: 186549 +// gas legacy: 187273 +// gas legacyOptimized: 186715 +// x() -> 11, 1, 2, 3, 4, 5, 6, 7, 8, 9 +// partialAssignArrayCrossStorageBoundary() +// x() -> 14, 15, 16, 17, 18, 19, 20, 0, 0, 0 +// clearArray() +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 44183 +// gas legacy: 46007 +// gas legacyOptimized: 43907 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_array_assignment.sol b/test/libsolidity/semanticTests/storage/storage_boundary_array_assignment.sol new file mode 100644 index 0000000000..0de66a7147 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_array_assignment.sol @@ -0,0 +1,28 @@ +contract C { + function getArray() internal pure returns (uint256[10][1] storage _x) { + assembly { + _x.slot := sub(0, 5) + } + } + + function assignArray(uint256[10] memory y) public { + uint256[10][1] storage _x = getArray(); + _x[0] = y; + } + + function x() public view returns (uint256[10] memory) { + return getArray()[0]; + } +} +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// assignArray(uint256[10]): 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> +// gas irOptimized: 245236 +// gas legacy: 249351 +// gas legacyOptimized: 245365 +// x() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +// assignArray(uint256[10]): 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 -> +// x() -> 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 +// gas irOptimized: 44183 +// gas legacy: 46012 +// gas legacyOptimized: 43907 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_array_copy.sol b/test/libsolidity/semanticTests/storage/storage_boundary_array_copy.sol new file mode 100644 index 0000000000..949edc4907 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_array_copy.sol @@ -0,0 +1,61 @@ +contract C { + constructor() { + uint256[10][1] storage _x = getX(); + _x[0] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + } + + function getX() internal pure returns (uint256[10][1] storage _x) { + assembly { + _x.slot := sub(0, 5) + } + } + + function getY() internal pure returns (uint256[10][1] storage _y) { + assembly { + _y.slot := 5 + } + } + + function copyXToY() public { + uint256[10][1] storage _x = getX(); + uint256[10][1] storage _y = getY(); + _y[0] = _x[0]; + } + + function clearX() public { + uint256[10][1] storage _x = getX(); + delete _x[0]; + } + + function copyYToX() public { + uint256[10][1] storage _x = getX(); + uint256[10][1] storage _y = getY(); + _x[0] = _y[0]; + } + + function x() public view returns (uint256[10] memory) { + return getX()[0]; + } + + function y() public view returns (uint256[10] memory) { + return getY()[0]; + } +} +// ---- +// x() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +// y() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// copyXToY() +// gas irOptimized: 264224 +// gas legacy: 265434 +// gas legacyOptimized: 264247 +// x() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +// y() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +// clearX() +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// y() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +// copyYToX() +// gas irOptimized: 266243 +// gas legacy: 267456 +// gas legacyOptimized: 266280 +// x() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +// y() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_array_delete.sol b/test/libsolidity/semanticTests/storage/storage_boundary_array_delete.sol new file mode 100644 index 0000000000..20d2e2ec9d --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_array_delete.sol @@ -0,0 +1,34 @@ +contract C { + function getArray() internal pure returns (uint256[10][1] storage _x) { + assembly { + _x.slot := sub(0, 5) + } + } + + function fillArray() public { + uint256[10][1] storage _x = getArray(); + for (uint i = 1; i < 10; i++) + _x[0][i] = i; + } + + function clearArray() public { + uint256[10][1] storage _x = getArray(); + delete _x[0]; + } + + function x() public view returns (uint256[10] memory) { + return getArray()[0]; + } +} +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// fillArray() +// gas irOptimized: 220705 +// gas legacy: 221434 +// gas legacyOptimized: 220871 +// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 +// clearArray() +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 44183 +// gas legacy: 46012 +// gas legacyOptimized: 43907 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_array_delete_overlapping_variable.sol b/test/libsolidity/semanticTests/storage/storage_boundary_array_delete_overlapping_variable.sol new file mode 100644 index 0000000000..3bb605cc91 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_array_delete_overlapping_variable.sol @@ -0,0 +1,40 @@ +contract C { + uint256 public y = 42; + + function getArray() internal pure returns (uint256[10][1] storage _x) { + assembly { + _x.slot := sub(0, 5) + } + } + + function fillArray() public { + uint256[10][1] storage _x = getArray(); + for (uint i = 1; i < 10; i++) + _x[0][i] = i; + } + + function clearArray() public { + uint256[10][1] storage _x = getArray(); + delete _x[0]; + } + + function x() public view returns (uint256[10] memory) { + return getArray()[0]; + } +} + +// ---- +// y() -> 42 +// x() -> 0, 0, 0, 0, 0, 42, 0, 0, 0, 0 +// fillArray() +// gas irOptimized: 203627 +// gas legacy: 204356 +// gas legacyOptimized: 203793 +// y() -> 5 +// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 +// clearArray() +// y() -> 0 +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 44183 +// gas legacy: 46012 +// gas legacyOptimized: 43907 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_array_packing_not_overlapping_variable.sol b/test/libsolidity/semanticTests/storage/storage_boundary_array_packing_not_overlapping_variable.sol new file mode 100644 index 0000000000..77a6158acc --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_array_packing_not_overlapping_variable.sol @@ -0,0 +1,63 @@ +contract C { + struct Canary { + uint256 value; + } + + constructor() { + Canary storage canary = getCanary(); + canary.value = type(uint256).max; // Should not be overwritten + } + + function getArray() internal pure returns (uint64[10][1] storage _x) { + // Array of 10 * uint64 values (8 bytes each) + // Packs 4 uint64 per slot -> 3 slots total (slots -1, 0, 1) + assembly { + _x.slot := sub(0, 1) + } + } + + function getCanary() internal pure returns (Canary storage canary) { + // Canary at slot 2, right after the array ends at slot 1 + assembly { + canary.slot := 2 + } + } + + function fillArray() public { + uint64[10][1] storage _x = getArray(); + for (uint64 i = 0; i < 10; i++) + _x[0][i] = i; + } + + function shrinkTo5() public { + uint64[10][1] storage _x = getArray(); + // Resize by assigning a smaller array + // This should clear items [5..9] without touching y + _x[0] = [uint64(11), 12, 13, 14, 15]; + } + + function clearArray() public { + uint64[10][1] storage _x = getArray(); + delete _x[0]; + } + + function x() public view returns (uint64[10] memory) { + return getArray()[0]; + } + + function canaryValue() public view returns (uint256) { + return getCanary().value; + } +} +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// fillArray() +// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// shrinkTo5() +// x() -> 11, 12, 13, 14, 15, 0, 0, 0, 0, 0 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// clearArray() +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_array_partial_assignment.sol b/test/libsolidity/semanticTests/storage/storage_boundary_array_partial_assignment.sol new file mode 100644 index 0000000000..c4e3ee7794 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_array_partial_assignment.sol @@ -0,0 +1,41 @@ +contract C { + function getArray() internal pure returns (uint256[10][1] storage _x) { + assembly { + _x.slot := sub(0, 5) + } + } + + function fillArray() public { + uint256[10][1] storage _x = getArray(); + for (uint i = 1; i < 10; i++) + _x[0][i] = i; + } + + function x() public view returns (uint256[10] memory) { + return getArray()[0]; + } + + function partialAssignArrayBeforeStorageBoundary() public { + uint256[10][1] storage _x = getArray(); + _x[0] = [21, 22, 23]; + } + + function partialAssignArrayCrossStorageBoundary() public { + uint256[10][1] storage _x = getArray(); + _x[0] = [11, 12, 13, 14, 15, 16, 17]; + } +} +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// fillArray() +// gas irOptimized: 220727 +// gas legacy: 221456 +// gas legacyOptimized: 220893 +// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 +// partialAssignArrayCrossStorageBoundary() +// x() -> 11, 12, 13, 14, 15, 16, 17, 0, 0, 0 +// partialAssignArrayBeforeStorageBoundary() +// x() -> 21, 22, 23, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 44183 +// gas legacy: 46012 +// gas legacyOptimized: 43907 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_delete_overflow_bug.sol b/test/libsolidity/semanticTests/storage/storage_boundary_delete_overflow_bug.sol new file mode 100644 index 0000000000..3ffb777471 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_delete_overflow_bug.sol @@ -0,0 +1,80 @@ +contract C { + mapping(string => uint256[256][2**240]) m; + + function getSlot() internal view returns (uint256) { + uint256[256][2**240] storage _x = m["v 2.2.3"]; + uint256 slot; + assembly { + slot := _x.slot + } + assert(slot == 0xffdb3f1d9f54eb0b5012935c286c508459d381405d269e01c15f4ec2826edbbf); + return slot; + } + + function getIndex() internal view returns (uint256) { + uint256 slot = getSlot(); + // Pick the largest index such that `slot + 256 * index` <= `2**256 - 1` + uint256 index = (type(uint256).max - slot) / 256; + assert(index <= type(uint240).max); + assert((type(uint256).max - slot + 1) % 256 != 0); + + return index; + } + + function getArray() internal view returns (uint256[256][2**240] storage _x) { + uint256 s = getSlot(); + assembly { + _x.slot := s + } + } + + function fillArray() public { + uint256[256][2**240] storage _x = getArray(); + for (uint i = 1; i < 256; i++) + _x[getIndex()][i] = i; + } + + function partialAssignArray() public { + uint256[256][2**240] storage _x = getArray(); + _x[getIndex()] = [11, 22, 33, 44, 55, 66, 77, 88, 99]; + } + + function clearArray() public { + uint256[256][2**240] storage _x = getArray(); + delete _x[getIndex()]; + } + + function x() public view returns (uint256[256] memory) { + return getArray()[getIndex()]; + } +} + +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 604108 +// gas legacy: 644983 +// gas legacyOptimized: 598016 +// fillArray() +// gas irOptimized: 5782148 +// gas legacy: 6044562 +// gas legacyOptimized: 5853893 +// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 +// gas irOptimized: 604108 +// gas legacy: 644983 +// gas legacyOptimized: 598016 +// partialAssignArray() +// gas irOptimized: 1067376 +// gas legacy: 1177356 +// gas legacyOptimized: 1068067 +// x() -> 11, 22, 33, 44, 55, 66, 77, 88, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 604108 +// gas legacy: 644983 +// gas legacyOptimized: 598016 +// clearArray() +// gas irOptimized: 580378 +// gas legacy: 582976 +// gas legacyOptimized: 581290 +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 604108 +// gas legacy: 644983 +// gas legacyOptimized: 598016 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_packed_array.sol b/test/libsolidity/semanticTests/storage/storage_boundary_packed_array.sol new file mode 100644 index 0000000000..aa30a2bcac --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_packed_array.sol @@ -0,0 +1,35 @@ +contract C { + function getArray() internal pure returns (uint64[40][1] storage _x) { + assembly { + _x.slot := sub(0, 5) + } + } + + function fillArray() public { + uint64[40][1] storage _x = getArray(); + for (uint64 i = 1; i < 40; i++) + _x[0][i] = i; + } + + function clearArray() public { + uint64[40][1] storage _x = getArray(); + delete _x[0]; + } + + function x() public view returns (uint64[40] memory) { + return getArray()[0]; + } +} +// ---- +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// fillArray() +// gas irOptimized: 254227 +// gas legacy: 258712 +// gas legacyOptimized: 257258 +// x() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39 +// clearArray() +// gas irOptimized: 57426 +// x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 48087 +// gas legacy: 64080 +// gas legacyOptimized: 56602 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_mixed_types.sol b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_mixed_types.sol new file mode 100644 index 0000000000..213dee1dae --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_mixed_types.sol @@ -0,0 +1,172 @@ +pragma abicoder v2; + +contract C { + struct S { + uint256 a; // slot 0 (bytes 0-31) + uint128 b; // slot 1 (bytes 0-15) + uint64 c; // slot 1 (bytes 16-23) + bytes32 d; // slot 2 (bytes 0-31) + bool e; // slot 3 (byte 0) + // Total: 4 slots per struct + } + + struct Canary { + uint256 value; + } + + function getBoundaryArray() internal pure returns (S[10][1] storage arr) { + // 10 structs * 4 slots = 40 slots total + // Starts at -20, ends at slot 19 + assembly { + arr.slot := sub(0, 20) + } + } + + function getDest() internal pure returns (S[10][1] storage arr) { + assembly { + arr.slot := 21 + } + } + + function getCanary() internal pure returns (Canary storage canary) { + // Array ends at slot 19, canary at slot 20 + assembly { + canary.slot := 20 + } + } + + constructor() { + Canary storage canary = getCanary(); + canary.value = type(uint256).max; + } + + function fillBoundaryArray() public { + S[10][1] storage arr = getBoundaryArray(); + for (uint i = 0; i < 10; i++) { + arr[0][i] = S({ + a: 1 + i * 5, + b: uint128(2 + i * 5), + c: uint64(3 + i * 5), + d: bytes32(uint256(4 + i * 5)), + e: true + }); + } + } + + function deleteBoundaryArray() public { + S[10][1] storage arr = getBoundaryArray(); + delete arr[0]; + } + + function copyFromBoundary() public { + S[10][1] storage source = getBoundaryArray(); + S[10][1] storage dest = getDest(); + dest[0] = source[0]; + } + + function copyToBoundary() public { + S[10][1] storage source = getDest(); + S[10][1] storage dest = getBoundaryArray(); + dest[0] = source[0]; + } + + function fillDestArray() public { + S[10][1] storage dest = getDest(); + for (uint i = 0; i < 10; i++) { + dest[0][i] = S({ + a: 51 + i * 5, + b: uint128(52 + i * 5), + c: uint64(53 + i * 5), + d: bytes32(uint256(54 + i * 5)), + e: true + }); + } + } + + function boundaryArray() public view returns (S[10] memory) { + return getBoundaryArray()[0]; + } + + function destArray() public view returns (S[10] memory) { + return getDest()[0]; + } + + function canaryValue() public view returns (uint256) { + return getCanary().value; + } +} +// ---- +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 113169 +// gas legacy: 120742 +// gas legacyOptimized: 112518 +// destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 113078 +// gas legacy: 120738 +// gas legacyOptimized: 112505 +// fillBoundaryArray() +// gas irOptimized: 912522 +// gas legacy: 930728 +// gas legacyOptimized: 916628 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 1, 2, 3, 4, true, 6, 7, 8, 9, true, 11, 12, 13, 14, true, 16, 17, 18, 19, true, 21, 22, 23, 24, true, 26, 27, 28, 29, true, 31, 32, 33, 34, true, 36, 37, 38, 39, true, 41, 42, 43, 44, true, 46, 47, 48, 49, true +// gas irOptimized: 113169 +// gas legacy: 120742 +// gas legacyOptimized: 112518 +// destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 113078 +// gas legacy: 120738 +// gas legacyOptimized: 112505 +// copyFromBoundary() +// gas irOptimized: 994579 +// gas legacy: 1023407 +// gas legacyOptimized: 994746 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 1, 2, 3, 4, true, 6, 7, 8, 9, true, 11, 12, 13, 14, true, 16, 17, 18, 19, true, 21, 22, 23, 24, true, 26, 27, 28, 29, true, 31, 32, 33, 34, true, 36, 37, 38, 39, true, 41, 42, 43, 44, true, 46, 47, 48, 49, true +// gas irOptimized: 113169 +// gas legacy: 120742 +// gas legacyOptimized: 112518 +// destArray() -> 1, 2, 3, 4, true, 6, 7, 8, 9, true, 11, 12, 13, 14, true, 16, 17, 18, 19, true, 21, 22, 23, 24, true, 26, 27, 28, 29, true, 31, 32, 33, 34, true, 36, 37, 38, 39, true, 41, 42, 43, 44, true, 46, 47, 48, 49, true +// gas irOptimized: 113078 +// gas legacy: 120738 +// gas legacyOptimized: 112505 +// fillDestArray() +// gas irOptimized: 200426 +// gas legacy: 218746 +// gas legacyOptimized: 204648 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 1, 2, 3, 4, true, 6, 7, 8, 9, true, 11, 12, 13, 14, true, 16, 17, 18, 19, true, 21, 22, 23, 24, true, 26, 27, 28, 29, true, 31, 32, 33, 34, true, 36, 37, 38, 39, true, 41, 42, 43, 44, true, 46, 47, 48, 49, true +// gas irOptimized: 113169 +// gas legacy: 120742 +// gas legacyOptimized: 112518 +// destArray() -> 51, 52, 53, 54, true, 56, 57, 58, 59, true, 61, 62, 63, 64, true, 66, 67, 68, 69, true, 71, 72, 73, 74, true, 76, 77, 78, 79, true, 81, 82, 83, 84, true, 86, 87, 88, 89, true, 91, 92, 93, 94, true, 96, 97, 98, 99, true +// gas irOptimized: 113078 +// gas legacy: 120738 +// gas legacyOptimized: 112505 +// copyToBoundary() +// gas irOptimized: 282623 +// gas legacy: 311362 +// gas legacyOptimized: 282712 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 51, 52, 53, 54, true, 56, 57, 58, 59, true, 61, 62, 63, 64, true, 66, 67, 68, 69, true, 71, 72, 73, 74, true, 76, 77, 78, 79, true, 81, 82, 83, 84, true, 86, 87, 88, 89, true, 91, 92, 93, 94, true, 96, 97, 98, 99, true +// gas irOptimized: 113169 +// gas legacy: 120742 +// gas legacyOptimized: 112518 +// destArray() -> 51, 52, 53, 54, true, 56, 57, 58, 59, true, 61, 62, 63, 64, true, 66, 67, 68, 69, true, 71, 72, 73, 74, true, 76, 77, 78, 79, true, 81, 82, 83, 84, true, 86, 87, 88, 89, true, 91, 92, 93, 94, true, 96, 97, 98, 99, true +// gas irOptimized: 113078 +// gas legacy: 120738 +// gas legacyOptimized: 112505 +// deleteBoundaryArray() +// gas irOptimized: 177968 +// gas legacy: 180997 +// gas legacyOptimized: 178175 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// gas irOptimized: 113169 +// gas legacy: 120742 +// gas legacyOptimized: 112518 +// destArray() -> 51, 52, 53, 54, true, 56, 57, 58, 59, true, 61, 62, 63, 64, true, 66, 67, 68, 69, true, 71, 72, 73, 74, true, 76, 77, 78, 79, true, 81, 82, 83, 84, true, 86, 87, 88, 89, true, 91, 92, 93, 94, true, 96, 97, 98, 99, true +// gas irOptimized: 113078 +// gas legacy: 120738 +// gas legacyOptimized: 112505 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_multislot.sol b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_multislot.sol new file mode 100644 index 0000000000..55e36cd08e --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_multislot.sol @@ -0,0 +1,128 @@ +pragma abicoder v2; + +contract C { + struct S { + uint256 a; + uint256 b; + uint256 c; + // 3 slots per struct + } + + struct Canary { + uint256 value; + } + + function getBoundaryArray() internal pure returns (S[10][1] storage arr) { + // 10 structs, each 3 slots = 30 slots total + assembly { + arr.slot := sub(0, 15) + } + } + + function getDest() internal pure returns (S[10][1] storage arr) { + assembly { + arr.slot := 16 + } + } + + function getCanary() internal pure returns (Canary storage canary) { + assembly { + canary.slot := 15 + } + } + + constructor() { + Canary storage canary = getCanary(); + canary.value = type(uint256).max; + } + + function fillBoundaryArray() public { + S[10][1] storage arr = getBoundaryArray(); + for (uint i = 0; i < 10; i++) { + arr[0][i] = S({ + a: 1 + i * 3, + b: 2 + i * 3, + c: 3 + i * 3 + }); + } + } + + function deleteBoundaryArray() public { + S[10][1] storage arr = getBoundaryArray(); + delete arr[0]; + } + + function copyFromBoundary() public { + S[10][1] storage source = getBoundaryArray(); + S[10][1] storage dest = getDest(); + dest[0] = source[0]; + } + + function copyToBoundary() public { + S[10][1] storage source = getDest(); + S[10][1] storage dest = getBoundaryArray(); + dest[0] = source[0]; + } + + function fillDestArray() public { + S[10][1] storage dest = getDest(); + for (uint i = 0; i < 10; i++) { + dest[0][i] = S({ + a: 31 + i * 3, + b: 32 + i * 3, + c: 33 + i * 3 + }); + } + } + + function boundaryArray() public view returns (S[10] memory) { + return getBoundaryArray()[0]; + } + + function destArray() public view returns (S[10] memory) { + return getDest()[0]; + } + + function canaryValue() public view returns (uint256) { + return getCanary().value; + } +} +// ---- +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// fillBoundaryArray() +// gas irOptimized: 688719 +// gas legacy: 700075 +// gas legacyOptimized: 691605 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 +// destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// copyFromBoundary() +// gas irOptimized: 748779 +// gas legacy: 767297 +// gas legacyOptimized: 748756 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 +// destArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 +// fillDestArray() +// gas irOptimized: 175623 +// gas legacy: 187093 +// gas legacyOptimized: 178625 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 +// destArray() -> 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 +// copyToBoundary() +// gas irOptimized: 235823 +// gas legacy: 254252 +// gas legacyOptimized: 235722 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 +// destArray() -> 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 +// deleteBoundaryArray() +// gas irOptimized: 137824 +// gas legacy: 137973 +// gas legacyOptimized: 137743 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// destArray() -> 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_packed.sol b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_packed.sol new file mode 100644 index 0000000000..5b4085acf8 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_packed.sol @@ -0,0 +1,128 @@ +pragma abicoder v2; + +contract C { + struct S { + uint64 a; + uint64 b; + uint64 c; + uint64 d; + // All fit in one slot (4 * 64 = 256 bits) + } + + struct Canary { + uint256 value; + } + + function getBoundaryArray() internal pure returns (S[10][1] storage arr) { + // 10 structs, each 1 slot = 10 slots total + assembly { + arr.slot := sub(0, 5) + } + } + + function getDest() internal pure returns (S[10][1] storage arr) { + assembly { + arr.slot := 6 + } + } + + function getCanary() internal pure returns (Canary storage canary) { + // Array ends at slot 4, canary at slot 5 + assembly { + canary.slot := 5 + } + } + + constructor() { + Canary storage canary = getCanary(); + canary.value = type(uint256).max; + } + + function fillBoundaryArray() public { + S[10][1] storage arr = getBoundaryArray(); + for (uint i = 0; i < 10; i++) { + arr[0][i] = S({ + a: uint64(1 + i * 4), + b: uint64(2 + i * 4), + c: uint64(3 + i * 4), + d: uint64(4 + i * 4) + }); + } + } + + function deleteBoundaryArray() public { + S[10][1] storage arr = getBoundaryArray(); + delete arr[0]; + } + + function copyFromBoundary() public { + S[10][1] storage source = getBoundaryArray(); + S[10][1] storage dest = getDest(); + dest[0] = source[0]; + } + + function copyToBoundary() public { + S[10][1] storage source = getDest(); + S[10][1] storage dest = getBoundaryArray(); + dest[0] = source[0]; + } + + function fillDestArray() public { + S[10][1] storage dest = getDest(); + for (uint i = 0; i < 10; i++) { + dest[0][i] = S({ + a: uint64(41 + i * 4), + b: uint64(42 + i * 4), + c: uint64(43 + i * 4), + d: uint64(44 + i * 4) + }); + } + } + + function boundaryArray() public view returns (S[10] memory) { + return getBoundaryArray()[0]; + } + + function destArray() public view returns (S[10] memory) { + return getDest()[0]; + } + + function canaryValue() public view returns (uint256) { + return getCanary().value; + } +} +// ---- +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// fillBoundaryArray() +// gas irOptimized: 248990 +// gas legacy: 272856 +// gas legacyOptimized: 253856 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 +// destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// copyFromBoundary() +// gas irOptimized: 274279 +// gas legacy: 298927 +// gas legacyOptimized: 272256 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 +// destArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 +// fillDestArray() +// gas legacy: 101874 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 +// destArray() -> 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 +// gas legacy: 59729 +// copyToBoundary() +// gas irOptimized: 103323 +// gas legacy: 127882 +// gas legacyOptimized: 101222 +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 +// destArray() -> 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 +// deleteBoundaryArray() +// canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +// destArray() -> 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 diff --git a/test/libsolidity/semanticTests/storage/storage_packed_array_copy.sol b/test/libsolidity/semanticTests/storage/storage_packed_array_copy.sol new file mode 100644 index 0000000000..d4edc5aecd --- /dev/null +++ b/test/libsolidity/semanticTests/storage/storage_packed_array_copy.sol @@ -0,0 +1,35 @@ +contract C { + bytes8[9] _x; // 4 per slot + bytes17[10] _y; // 1 per slot, no offset counter + + constructor() { + for (uint256 i = 0; i < _x.length; ++i) _x[i] = bytes8(uint64(i)); + _y[8] = _y[9] = bytes8(uint64(2)); + } + + function getXAsUint() public view returns (uint64[9] memory result) { + for (uint i = 0; i < 9; i++) { + result[i] = uint64(_x[i]); + } + } + + function getYAsUint() public view returns (uint64[10] memory result) { + for (uint i = 0; i < 10; i++) { + result[i] = uint64(bytes8(_y[i])); + } + } + + function copy() public { + _y = _x; + } +} + +// ---- +// getXAsUint() -> 0, 1, 2, 3, 4, 5, 6, 7, 8 +// getYAsUint() -> 0, 0, 0, 0, 0, 0, 0, 0, 2, 2 +// copy() +// gas irOptimized: 190810 +// gas legacy: 195580 +// gas legacyOptimized: 190906 +// getXAsUint() -> 0, 1, 2, 3, 4, 5, 6, 7, 8 +// getYAsUint() -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 0 diff --git a/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_dynamic_array.sol b/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_dynamic_array.sol index 1737c35bb8..bcad9609ee 100644 --- a/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_dynamic_array.sol +++ b/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_dynamic_array.sol @@ -41,9 +41,9 @@ contract C is A layout at 42 { // arrayALength() -> 3 // arrayCLength() -> 0 // initCFromAInReverse() -> 3, 2, 1 -// gas irOptimized: 121276 -// gas legacy: 121213 -// gas legacyOptimized: 120843 +// gas irOptimized: 121281 +// gas legacy: 121937 +// gas legacyOptimized: 120853 // clearA() -> // arrayC(uint256): 0 -> 3 // arrayALength() -> 0 diff --git a/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_mapping.sol b/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_mapping.sol index e33df29ea6..47c4786c84 100644 --- a/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_mapping.sol +++ b/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_mapping.sol @@ -27,7 +27,7 @@ contract C layout at 42 is A { // setup() -> // gas irOptimized: 159082 // gas legacy: 161738 -// gas legacyOptimized: 160222 +// gas legacyOptimized: 160218 // open(uint256): 3 -> 0x20, 5, "Empty" // open(uint256): 2 -> 0x20, 6, "Locked" // open(uint256): 1 -> 0x20, 7, "Monster" diff --git a/test/libsolidity/semanticTests/structs/copy_from_mapping.sol b/test/libsolidity/semanticTests/structs/copy_from_mapping.sol index 037ff20679..fccdf0030f 100644 --- a/test/libsolidity/semanticTests/structs/copy_from_mapping.sol +++ b/test/libsolidity/semanticTests/structs/copy_from_mapping.sol @@ -37,7 +37,7 @@ contract C { // ---- // to_state() -> 0x20, 0x60, 0xa0, 7, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // gas irOptimized: 121282 -// gas legacy: 122977 -// gas legacyOptimized: 121652 +// gas legacy: 125480 +// gas legacyOptimized: 121695 // to_storage() -> 0x20, 0x60, 0xa0, 7, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // to_memory() -> 0x20, 0x60, 0xa0, 7, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 diff --git a/test/libsolidity/semanticTests/structs/copy_struct_array_from_storage.sol b/test/libsolidity/semanticTests/structs/copy_struct_array_from_storage.sol index 600eb35ab2..50babde562 100644 --- a/test/libsolidity/semanticTests/structs/copy_struct_array_from_storage.sol +++ b/test/libsolidity/semanticTests/structs/copy_struct_array_from_storage.sol @@ -88,8 +88,8 @@ contract Test { // ---- // test1() -> true // gas irOptimized: 152965 -// gas legacy: 153010 -// gas legacyOptimized: 152636 +// gas legacy: 155952 +// gas legacyOptimized: 152864 // test2() -> true // test3() -> true // test4() -> true diff --git a/test/libsolidity/semanticTests/structs/copy_substructures_from_mapping.sol b/test/libsolidity/semanticTests/structs/copy_substructures_from_mapping.sol index 7188e1befe..9f0b7cbe64 100644 --- a/test/libsolidity/semanticTests/structs/copy_substructures_from_mapping.sol +++ b/test/libsolidity/semanticTests/structs/copy_substructures_from_mapping.sol @@ -45,7 +45,7 @@ contract C { // ---- // to_state() -> 0x20, 0x60, 0xa0, 7, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // gas irOptimized: 121454 -// gas legacy: 123114 -// gas legacyOptimized: 121659 +// gas legacy: 125617 +// gas legacyOptimized: 121699 // to_storage() -> 0x20, 0x60, 0xa0, 7, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // to_memory() -> 0x20, 0x60, 0xa0, 7, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 diff --git a/test/libsolidity/semanticTests/structs/copy_substructures_to_mapping.sol b/test/libsolidity/semanticTests/structs/copy_substructures_to_mapping.sol index 54b659ba07..3e86495713 100644 --- a/test/libsolidity/semanticTests/structs/copy_substructures_to_mapping.sol +++ b/test/libsolidity/semanticTests/structs/copy_substructures_to_mapping.sol @@ -53,13 +53,13 @@ contract C { // ---- // from_memory() -> 0x20, 0x60, 0xa0, 0x15, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // gas irOptimized: 122720 -// gas legacy: 130131 -// gas legacyOptimized: 128648 +// gas legacy: 125558 +// gas legacyOptimized: 123322 // from_state() -> 0x20, 0x60, 0xa0, 21, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // gas irOptimized: 121424 -// gas legacy: 123190 -// gas legacyOptimized: 121758 +// gas legacy: 125693 +// gas legacyOptimized: 121804 // from_calldata((bytes,uint16[],uint16)): 0x20, 0x60, 0xa0, 21, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 -> 0x20, 0x60, 0xa0, 0x15, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // gas irOptimized: 114852 -// gas legacy: 122423 -// gas legacyOptimized: 120698 +// gas legacy: 117950 +// gas legacyOptimized: 115526 diff --git a/test/libsolidity/semanticTests/structs/copy_to_mapping.sol b/test/libsolidity/semanticTests/structs/copy_to_mapping.sol index d020998b47..b31d0e5804 100644 --- a/test/libsolidity/semanticTests/structs/copy_to_mapping.sol +++ b/test/libsolidity/semanticTests/structs/copy_to_mapping.sol @@ -46,16 +46,16 @@ contract C { // ---- // from_state() -> 0x20, 0x60, 0xa0, 21, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // gas irOptimized: 121515 -// gas legacy: 123051 -// gas legacyOptimized: 121704 +// gas legacy: 125554 +// gas legacyOptimized: 121747 // from_storage() -> 0x20, 0x60, 0xa0, 21, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // gas irOptimized: 121559 -// gas legacy: 123109 -// gas legacyOptimized: 121756 +// gas legacy: 125612 +// gas legacyOptimized: 121799 // from_memory() -> 0x20, 0x60, 0xa0, 21, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // gas irOptimized: 122740 -// gas legacy: 129996 -// gas legacyOptimized: 128644 +// gas legacy: 125423 +// gas legacyOptimized: 123323 // from_calldata((bytes,uint16[],uint16)): 0x20, 0x60, 0xa0, 21, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 -> 0x20, 0x60, 0xa0, 21, 3, 0x666f6f0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 // gas irOptimized: 114824 // gas legacy: 118207 diff --git a/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol b/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol index e50265e517..3912a46555 100644 --- a/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol +++ b/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol @@ -25,7 +25,7 @@ contract c { // set(uint256,bytes,uint256): 12, 0x60, 13, 33, "12345678901234567890123456789012", "3" -> true // gas irOptimized: 133557 // gas legacy: 134624 -// gas legacyOptimized: 133856 +// gas legacyOptimized: 133857 // test(uint256): 32 -> "3" // storageEmpty -> 0 // copy() -> true @@ -33,7 +33,7 @@ contract c { // set(uint256,bytes,uint256): 12, 0x60, 13, 33, "12345678901234567890123456789012", "3" -> true // gas irOptimized: 133557 // gas legacy: 134624 -// gas legacyOptimized: 133856 +// gas legacyOptimized: 133857 // storageEmpty -> 0 // del() -> true // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/structs/struct_delete_storage_nested_small.sol b/test/libsolidity/semanticTests/structs/struct_delete_storage_nested_small.sol index a8e29dcad8..12cbf0799e 100644 --- a/test/libsolidity/semanticTests/structs/struct_delete_storage_nested_small.sol +++ b/test/libsolidity/semanticTests/structs/struct_delete_storage_nested_small.sol @@ -33,4 +33,4 @@ contract C { // compileViaYul: true // ---- // f() -> 0, 0, 0 -// gas irOptimized: 93830 +// gas irOptimized: 93835 diff --git a/test/libsolidity/semanticTests/structs/struct_delete_storage_with_array.sol b/test/libsolidity/semanticTests/structs/struct_delete_storage_with_array.sol index cea1105669..cd49ea27ad 100644 --- a/test/libsolidity/semanticTests/structs/struct_delete_storage_with_array.sol +++ b/test/libsolidity/semanticTests/structs/struct_delete_storage_with_array.sol @@ -42,10 +42,10 @@ contract C { } // ---- // f() -> -// gas irOptimized: 113465 -// gas legacy: 113591 -// gas legacyOptimized: 113098 +// gas irOptimized: 113388 +// gas legacy: 113588 +// gas legacyOptimized: 113104 // g() -> -// gas irOptimized: 118828 -// gas legacy: 118764 -// gas legacyOptimized: 118168 +// gas irOptimized: 118768 +// gas legacy: 118766 +// gas legacyOptimized: 118188 diff --git a/test/libsolidity/semanticTests/structs/struct_delete_storage_with_arrays_small.sol b/test/libsolidity/semanticTests/structs/struct_delete_storage_with_arrays_small.sol index 871c614f56..1a047049f2 100644 --- a/test/libsolidity/semanticTests/structs/struct_delete_storage_with_arrays_small.sol +++ b/test/libsolidity/semanticTests/structs/struct_delete_storage_with_arrays_small.sol @@ -27,4 +27,4 @@ contract C { // compileViaYul: true // ---- // f() -> 0 -// gas irOptimized: 89435 +// gas irOptimized: 89468 diff --git a/test/libsolidity/semanticTests/types/mapping/copy_from_mapping_to_mapping.sol b/test/libsolidity/semanticTests/types/mapping/copy_from_mapping_to_mapping.sol index 26e8294e3a..47fa27fe3c 100644 --- a/test/libsolidity/semanticTests/types/mapping/copy_from_mapping_to_mapping.sol +++ b/test/libsolidity/semanticTests/types/mapping/copy_from_mapping_to_mapping.sol @@ -30,5 +30,5 @@ contract C { // ---- // f() -> 0x20, 7, 8, 9, 0xa0, 13, 2, 0x40, 0xa0, 2, 3, 4, 2, 3, 4 // gas irOptimized: 197102 -// gas legacy: 199887 -// gas legacyOptimized: 196845 +// gas legacy: 205706 +// gas legacyOptimized: 196739 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/calldata.sol b/test/libsolidity/semanticTests/userDefinedValueType/calldata.sol index 015d51c1f5..03c6e11bcc 100644 --- a/test/libsolidity/semanticTests/userDefinedValueType/calldata.sol +++ b/test/libsolidity/semanticTests/userDefinedValueType/calldata.sol @@ -49,13 +49,13 @@ contract C { } // ---- // test_f() -> true -// gas irOptimized: 122201 -// gas legacy: 125333 -// gas legacyOptimized: 122693 +// gas irOptimized: 122114 +// gas legacy: 126162 +// gas legacyOptimized: 122757 // test_g() -> true -// gas irOptimized: 106408 -// gas legacy: 111133 -// gas legacyOptimized: 106925 +// gas irOptimized: 106248 +// gas legacy: 111824 +// gas legacyOptimized: 106606 // addresses(uint256): 0 -> 0x18 // addresses(uint256): 1 -> 0x19 // addresses(uint256): 3 -> 0x1b diff --git a/test/libsolidity/semanticTests/userDefinedValueType/calldata_to_storage.sol b/test/libsolidity/semanticTests/userDefinedValueType/calldata_to_storage.sol index 292b9fbfe3..0815a2da7a 100644 --- a/test/libsolidity/semanticTests/userDefinedValueType/calldata_to_storage.sol +++ b/test/libsolidity/semanticTests/userDefinedValueType/calldata_to_storage.sol @@ -25,17 +25,17 @@ contract C { // f((uint8,uint16,bytes2,uint8)): 1, 0xff, "ab", 15 -> // gas irOptimized: 44237 // gas legacy: 47154 -// gas legacyOptimized: 45018 +// gas legacyOptimized: 44982 // s() -> 1, 0xff, 0x6162000000000000000000000000000000000000000000000000000000000000, 15 // g(uint16[]): 0x20, 3, 1, 2, 3 -> 0x20, 3, 1, 2, 3 // gas irOptimized: 68578 -// gas legacy: 75429 -// gas legacyOptimized: 74244 +// gas legacy: 71400 +// gas legacyOptimized: 69139 // small(uint256): 0 -> 1 // small(uint256): 1 -> 2 // h(bytes2[]): 0x20, 3, "ab", "cd", "ef" -> 0x20, 3, "ab", "cd", "ef" // gas irOptimized: 68635 -// gas legacy: 75125 -// gas legacyOptimized: 74331 +// gas legacy: 71231 +// gas legacyOptimized: 69289 // l(uint256): 0 -> 0x6162000000000000000000000000000000000000000000000000000000000000 // l(uint256): 1 -> 0x6364000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/memory_to_storage.sol b/test/libsolidity/semanticTests/userDefinedValueType/memory_to_storage.sol index 585249f2f5..6e07e74e9f 100644 --- a/test/libsolidity/semanticTests/userDefinedValueType/memory_to_storage.sol +++ b/test/libsolidity/semanticTests/userDefinedValueType/memory_to_storage.sol @@ -29,13 +29,13 @@ contract C { // s() -> 1, 0xff, 0x6162000000000000000000000000000000000000000000000000000000000000, 15 // g(uint16[]): 0x20, 3, 1, 2, 3 -> 0x20, 3, 1, 2, 3 // gas irOptimized: 68898 -// gas legacy: 76511 -// gas legacyOptimized: 74832 +// gas legacy: 72336 +// gas legacyOptimized: 69607 // small(uint256): 0 -> 1 // small(uint256): 1 -> 2 // h(bytes2[]): 0x20, 3, "ab", "cd", "ef" -> 0x20, 3, "ab", "cd", "ef" // gas irOptimized: 68937 -// gas legacy: 76198 -// gas legacyOptimized: 74919 +// gas legacy: 72167 +// gas legacyOptimized: 69802 // l(uint256): 0 -> 0x6162000000000000000000000000000000000000000000000000000000000000 // l(uint256): 1 -> 0x6364000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/various/address_code.sol b/test/libsolidity/semanticTests/various/address_code.sol index 4d810f9709..eda17b3c27 100644 --- a/test/libsolidity/semanticTests/various/address_code.sol +++ b/test/libsolidity/semanticTests/various/address_code.sol @@ -16,11 +16,11 @@ contract C { // bytecodeFormat: legacy // ---- // constructor() -> -// gas irOptimized: 70760 +// gas irOptimized: 70924 // gas irOptimized code: 94600 -// gas legacy: 82428 +// gas legacy: 82688 // gas legacy code: 153800 -// gas legacyOptimized: 69400 +// gas legacyOptimized: 69675 // gas legacyOptimized code: 79200 // initCode() -> 0x20, 0 // f() -> true diff --git a/test/libsolidity/semanticTests/various/create_calldata.sol b/test/libsolidity/semanticTests/various/create_calldata.sol index 7e3e326228..c4d17d4299 100644 --- a/test/libsolidity/semanticTests/various/create_calldata.sol +++ b/test/libsolidity/semanticTests/various/create_calldata.sol @@ -10,10 +10,10 @@ contract C { // bytecodeFormat: legacy // ---- // constructor(): 42 -> -// gas irOptimized: 68239 +// gas irOptimized: 68387 // gas irOptimized code: 69000 -// gas legacy: 78076 +// gas legacy: 78348 // gas legacy code: 90200 -// gas legacyOptimized: 68321 +// gas legacyOptimized: 68548 // gas legacyOptimized code: 64600 // s() -> 0x20, 0 diff --git a/test/libsolidity/semanticTests/various/destructuring_assignment.sol b/test/libsolidity/semanticTests/various/destructuring_assignment.sol index a961870ee5..44fcd0f6aa 100644 --- a/test/libsolidity/semanticTests/various/destructuring_assignment.sol +++ b/test/libsolidity/semanticTests/various/destructuring_assignment.sol @@ -33,6 +33,6 @@ contract C { } // ---- // f(bytes): 0x20, 0x5, "abcde" -> 0 -// gas irOptimized: 242027 -// gas legacy: 243281 -// gas legacyOptimized: 242392 +// gas irOptimized: 242037 +// gas legacy: 246685 +// gas legacyOptimized: 242396 diff --git a/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol b/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol index 8430da4e8d..1af8a04195 100644 --- a/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol +++ b/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol @@ -20,5 +20,5 @@ contract C { // ---- // g() -> 2, 6 // gas irOptimized: 178195 -// gas legacy: 180653 -// gas legacyOptimized: 179144 +// gas legacy: 181357 +// gas legacyOptimized: 179055 diff --git a/test/libsolidity/syntaxTests/array/copy_calldata_struct_array_to_storage_legacy.sol b/test/libsolidity/syntaxTests/array/copy_calldata_struct_array_to_storage_legacy.sol new file mode 100644 index 0000000000..8c45b5b9f5 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/copy_calldata_struct_array_to_storage_legacy.sol @@ -0,0 +1,16 @@ +contract C { + struct S { + uint256 a; + uint256 b; + } + + S[] storageArray; + + function copyFromCalldata(S[] calldata calldataArray) public { + storageArray = calldataArray; + } +} +// ==== +// compileViaYul: false +// ---- +// UnimplementedFeatureError 1834: (0-208): Copying of type struct C.S calldata[] calldata to storage is not supported in legacy (only supported by the IR pipeline). Hint: try compiling with `--via-ir` (CLI) or the equivalent `viaIR: true` (Standard JSON). diff --git a/test/libsolidity/syntaxTests/array/copy_memory_struct_array_to_storage_legacy.sol b/test/libsolidity/syntaxTests/array/copy_memory_struct_array_to_storage_legacy.sol new file mode 100644 index 0000000000..86e8bf1f63 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/copy_memory_struct_array_to_storage_legacy.sol @@ -0,0 +1,17 @@ +contract C { + struct S { + uint256 a; + uint256 b; + } + + S[] storageArray; + + function copyFromMemory() public { + S[] memory memArray = new S[](3); + storageArray = memArray; + } +} +// ==== +// compileViaYul: false +// ---- +// UnimplementedFeatureError 1834: (0-217): Copying of type struct C.S memory[] memory to storage is not supported in legacy (only supported by the IR pipeline). Hint: try compiling with `--via-ir` (CLI) or the equivalent `viaIR: true` (Standard JSON). From 1ebf5395e9ccc46ea239a2cdbd2a83e0a9379753 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Wed, 17 Dec 2025 13:48:39 -0300 Subject: [PATCH 115/243] Update Changelog.md and add bug entry for LostStorageArrayWriteOnSlotOverflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kamil Śliwak --- Changelog.md | 3 + docs/bugs.json | 10 +++ docs/bugs_by_version.json | 142 +++++++++++++++++++++++++++++++++++--- 3 files changed, 146 insertions(+), 9 deletions(-) diff --git a/Changelog.md b/Changelog.md index 7ed3d7ef64..27e1cb33a2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,8 @@ ### 0.8.32 (unreleased) +Important Bugfixes: +* Code Generator: Fix a bug in clearing/copying of arrays that straddle the end of storage, potentially resulting in assignment/initialization/`delete`/`push()`/`pop()` skipping some or all of the intended storage writes. + Language Features: Compiler Features: diff --git a/docs/bugs.json b/docs/bugs.json index 8005246690..0c0b0493d7 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -1,4 +1,14 @@ [ + { + "uid": "SOL-2025-1", + "name": "LostStorageArrayWriteOnSlotOverflow", + "summary": "Operations that involve clearing or copying from arrays that straddle the end of storage could result in silent data retention.", + "description": "Solidity makes it possible to define variables that extend past the last (2**256-th) slot of storage, which results in wrap-around back to slot zero. Since EVM uses 256-bit integer arithmetic, most operations on such variables just work. The only situation which requires special attention is iteration against absolute slot addresses: the invariant that the last slot belonging to a variable has the highest address does not hold. When implemented incorrectly, a loop over an array will immediately terminate if the container spans the end of storage - due to the initial position already being greater than the end position. This affected storage array clearing loops generated by both evmasm and IR pipelines. Additionally, (only in the evmasm pipeline) copying operations whose source was an array straddling the end of storage were also affected. At the language level, the buggy code would be generated for array assignment, array initialization, delete operator, .pop() and .push(). Note that a clearing loop is inserted by the compiler not only for invocations of the delete operator, but also to zero storage when overwriting a longer array with a shorter one, popping an element or even pushing an empty element to a dynamic array. Since clearing is a separate loop, it is possible for the bug to only affect it and not the copy operation it follows (which is always the case in the IR pipeline). The bug is extremely unlikely to be triggered accidentally due to the probabilistic impossibility of a short dynamic array being allocated right at the storage boundary. On the other hand, scenarios in which a user may place a static array there intentionally do not seem realistic and are limited to unusual layouts, in which a contract does not place any storage variables at slot zero (otherwise they would overlap the array).", + "link": "https://blog.soliditylang.org/2025/12/18/lost-storage-array-write-on-slot-overflow-bug/", + "introduced": "0.1.0", + "fixed": "0.8.32", + "severity": "low" + }, { "uid": "SOL-2023-3", "name": "VerbatimInvalidDeduplication", diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 881f5eabee..94e814892d 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1,6 +1,7 @@ { "0.1.0": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -23,6 +24,7 @@ }, "0.1.1": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -45,6 +47,7 @@ }, "0.1.2": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -67,6 +70,7 @@ }, "0.1.3": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -89,6 +93,7 @@ }, "0.1.4": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -112,6 +117,7 @@ }, "0.1.5": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -135,6 +141,7 @@ }, "0.1.6": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -160,6 +167,7 @@ }, "0.1.7": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -185,6 +193,7 @@ }, "0.2.0": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -211,6 +220,7 @@ }, "0.2.1": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -237,6 +247,7 @@ }, "0.2.2": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -263,6 +274,7 @@ }, "0.3.0": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -291,6 +303,7 @@ }, "0.3.1": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -318,6 +331,7 @@ }, "0.3.2": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -345,6 +359,7 @@ }, "0.3.3": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -371,6 +386,7 @@ }, "0.3.4": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -397,6 +413,7 @@ }, "0.3.5": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -423,6 +440,7 @@ }, "0.3.6": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -447,6 +465,7 @@ }, "0.4.0": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -471,6 +490,7 @@ }, "0.4.1": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -495,6 +515,7 @@ }, "0.4.10": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -518,6 +539,7 @@ }, "0.4.11": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -540,6 +562,7 @@ }, "0.4.12": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -561,6 +584,7 @@ }, "0.4.13": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -582,6 +606,7 @@ }, "0.4.14": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -602,6 +627,7 @@ }, "0.4.15": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -621,6 +647,7 @@ }, "0.4.16": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -643,6 +670,7 @@ }, "0.4.17": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -666,6 +694,7 @@ }, "0.4.18": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -688,6 +717,7 @@ }, "0.4.19": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -711,6 +741,7 @@ }, "0.4.2": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -734,6 +765,7 @@ }, "0.4.20": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -757,6 +789,7 @@ }, "0.4.21": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -780,6 +813,7 @@ }, "0.4.22": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -803,6 +837,7 @@ }, "0.4.23": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -825,6 +860,7 @@ }, "0.4.24": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -847,6 +883,7 @@ }, "0.4.25": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -867,6 +904,7 @@ }, "0.4.26": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -884,6 +922,7 @@ }, "0.4.3": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -906,6 +945,7 @@ }, "0.4.4": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -927,6 +967,7 @@ }, "0.4.5": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -951,6 +992,7 @@ }, "0.4.6": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -974,6 +1016,7 @@ }, "0.4.7": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -997,6 +1040,7 @@ }, "0.4.8": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -1020,6 +1064,7 @@ }, "0.4.9": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "KeccakCaching", "EmptyByteArrayCopy", @@ -1043,6 +1088,7 @@ }, "0.5.0": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1063,6 +1109,7 @@ }, "0.5.1": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1083,6 +1130,7 @@ }, "0.5.10": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1101,6 +1149,7 @@ }, "0.5.11": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1118,6 +1167,7 @@ }, "0.5.12": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1135,6 +1185,7 @@ }, "0.5.13": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1152,6 +1203,7 @@ }, "0.5.14": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1171,6 +1223,7 @@ }, "0.5.15": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1189,6 +1242,7 @@ }, "0.5.16": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1206,6 +1260,7 @@ }, "0.5.17": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1222,6 +1277,7 @@ }, "0.5.2": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1242,6 +1298,7 @@ }, "0.5.3": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1262,6 +1319,7 @@ }, "0.5.4": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1282,6 +1340,7 @@ }, "0.5.5": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1304,6 +1363,7 @@ }, "0.5.6": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1326,6 +1386,7 @@ }, "0.5.7": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "DirtyBytesArrayToStorage", "ABIDecodeTwoDimensionalArrayMemory", "KeccakCaching", @@ -1346,6 +1407,7 @@ }, "0.5.8": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1367,6 +1429,7 @@ }, "0.5.9": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1387,6 +1450,7 @@ }, "0.6.0": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1405,6 +1469,7 @@ }, "0.6.1": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", "NestedCalldataArrayAbiReencodingSizeValidation", @@ -1422,6 +1487,7 @@ }, "0.6.10": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1438,6 +1504,7 @@ }, "0.6.11": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1454,6 +1521,7 @@ }, "0.6.12": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1470,6 +1538,7 @@ }, "0.6.2": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", @@ -1488,6 +1557,7 @@ }, "0.6.3": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", @@ -1506,6 +1576,7 @@ }, "0.6.4": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", @@ -1524,6 +1595,7 @@ }, "0.6.5": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", @@ -1542,6 +1614,7 @@ }, "0.6.6": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", "DirtyBytesArrayToStorage", @@ -1559,6 +1632,7 @@ }, "0.6.7": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1577,6 +1651,7 @@ }, "0.6.8": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1592,6 +1667,7 @@ }, "0.6.9": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1609,6 +1685,7 @@ }, "0.7.0": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1625,6 +1702,7 @@ }, "0.7.1": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1642,6 +1720,7 @@ }, "0.7.2": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1658,6 +1737,7 @@ }, "0.7.3": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1673,6 +1753,7 @@ }, "0.7.4": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1687,6 +1768,7 @@ }, "0.7.5": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1701,6 +1783,7 @@ }, "0.7.6": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1715,6 +1798,7 @@ }, "0.8.0": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1729,6 +1813,7 @@ }, "0.8.1": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1743,6 +1828,7 @@ }, "0.8.10": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1755,6 +1841,7 @@ }, "0.8.11": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1768,6 +1855,7 @@ }, "0.8.12": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1781,6 +1869,7 @@ }, "0.8.13": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1795,6 +1884,7 @@ }, "0.8.14": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1807,6 +1897,7 @@ }, "0.8.15": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1817,6 +1908,7 @@ }, "0.8.16": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1826,6 +1918,7 @@ }, "0.8.17": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" @@ -1834,6 +1927,7 @@ }, "0.8.18": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" @@ -1842,6 +1936,7 @@ }, "0.8.19": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" @@ -1850,6 +1945,7 @@ }, "0.8.2": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1864,6 +1960,7 @@ }, "0.8.20": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" @@ -1872,46 +1969,63 @@ }, "0.8.21": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication" ], "released": "2023-07-19" }, "0.8.22": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication" ], "released": "2023-10-25" }, "0.8.23": { - "bugs": [], + "bugs": [ + "LostStorageArrayWriteOnSlotOverflow" + ], "released": "2023-11-08" }, "0.8.24": { - "bugs": [], + "bugs": [ + "LostStorageArrayWriteOnSlotOverflow" + ], "released": "2024-01-25" }, "0.8.25": { - "bugs": [], + "bugs": [ + "LostStorageArrayWriteOnSlotOverflow" + ], "released": "2024-03-14" }, "0.8.26": { - "bugs": [], + "bugs": [ + "LostStorageArrayWriteOnSlotOverflow" + ], "released": "2024-05-21" }, "0.8.27": { - "bugs": [], + "bugs": [ + "LostStorageArrayWriteOnSlotOverflow" + ], "released": "2024-09-04" }, "0.8.28": { - "bugs": [], + "bugs": [ + "LostStorageArrayWriteOnSlotOverflow" + ], "released": "2024-10-09" }, "0.8.29": { - "bugs": [], + "bugs": [ + "LostStorageArrayWriteOnSlotOverflow" + ], "released": "2025-03-12" }, "0.8.3": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1924,15 +2038,20 @@ "released": "2021-03-23" }, "0.8.30": { - "bugs": [], + "bugs": [ + "LostStorageArrayWriteOnSlotOverflow" + ], "released": "2025-05-07" }, "0.8.31": { - "bugs": [], + "bugs": [ + "LostStorageArrayWriteOnSlotOverflow" + ], "released": "2025-12-03" }, "0.8.4": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1945,6 +2064,7 @@ }, "0.8.5": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1958,6 +2078,7 @@ }, "0.8.6": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1971,6 +2092,7 @@ }, "0.8.7": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1984,6 +2106,7 @@ }, "0.8.8": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", @@ -1998,6 +2121,7 @@ }, "0.8.9": { "bugs": [ + "LostStorageArrayWriteOnSlotOverflow", "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", From b34a0545eef0e9424e4e5bf6d6bf07c65a94dbc6 Mon Sep 17 00:00:00 2001 From: rodiazet Date: Wed, 17 Dec 2025 13:13:33 +0100 Subject: [PATCH 116/243] Fix `string|bytes.concat` assignment to a constant variable. --- libsolidity/analysis/TypeChecker.cpp | 17 +++++++++++++++++ libsolidity/ast/Types.cpp | 4 +++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index f5c093dbd4..8556487ff4 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -3271,11 +3271,28 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) // TODO some members might be pure, but for example `address(0x123).balance` is not pure // although every subexpression is, so leaving this limited for now. if (auto tt = dynamic_cast(exprType)) + { if ( tt->actualType()->category() == Type::Category::Enum || tt->actualType()->category() == Type::Category::UserDefinedValueType ) annotation.isPure = true; + + // `concat` purity depends also on its arguments, but this is checked later, in visit(FunctionCall...) + // This covers `bytes.concat` and `string.concat`. + if (tt->actualType()->category() == Type::Category::Array) + { + if ( + auto const* funcType = dynamic_cast(annotation.type); + funcType && + ( + funcType->kind() == FunctionType::Kind::StringConcat || + funcType->kind() == FunctionType::Kind::BytesConcat + ) + ) + annotation.isPure = true; + } + } if ( auto const* functionType = dynamic_cast(exprType); functionType && diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index f5c83b0cf9..316d99ba3e 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3713,7 +3713,9 @@ bool FunctionType::isPure() const m_kind == Kind::ABIDecode || m_kind == Kind::MetaType || m_kind == Kind::Wrap || - m_kind == Kind::Unwrap; + m_kind == Kind::Unwrap || + m_kind == Kind::BytesConcat || + m_kind == Kind::StringConcat; } TypePointers FunctionType::parseElementaryTypeVector(strings const& _types) From f4b7f98893f570365319673ba64339b61e5e7497 Mon Sep 17 00:00:00 2001 From: rodiazet Date: Wed, 17 Dec 2025 13:14:08 +0100 Subject: [PATCH 117/243] Add syntax tests. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kamil Śliwak --- .../initialization/abi_decode_const_args.sol | 7 ++ .../abi_decode_non_const_args.sol | 9 +++ .../abi_encode_call_const_args.sol | 5 ++ .../abi_encode_call_non_const_args.sol | 11 +++ .../abi_encoding_builtin_const_args.sol} | 0 .../abi_encoding_builtin_non_const_args.sol | 13 +++ .../addmod_by_zero.sol} | 0 .../addmod_mulmod_rational_arg.sol} | 0 .../initialization/block_tx_msg_property.sol | 80 +++++++++++++++++++ .../bytes_concat_const_args.sol | 11 +++ .../bytes_concat_non_const_args.sol | 17 ++++ .../constant_with_cyclic_dependency_1.sol} | 0 .../constant_with_cyclic_dependency_2.sol} | 0 .../constant_with_cyclic_dependency_3.sol} | 0 .../constant_with_cyclic_dependency_4.sol} | 0 .../constant_with_cyclic_dependency_5.sol} | 0 .../constant_with_cyclic_dependency_file.sol} | 0 ...th_cyclic_dependency_file_and_library.sol} | 0 ...nstant_with_cyclic_dependency_library.sol} | 0 .../{ => initialization}/division_by_zero.sol | 0 .../function_pointer_call.sol} | 0 .../math_builtin_opcode_based_const_args.sol | 14 ++++ ...th_builtin_opcode_based_non_const_args.sol | 12 +++ ...th_builtin_precompile_based_const_args.sol | 16 ++++ ...uiltin_precompile_based_non_const_args.sol | 10 +++ .../modulo_by_zero.sol} | 0 .../mulmod_by_zero.sol} | 0 .../string_concat_const_args.sol | 9 +++ .../string_concat_non_const_args.sol | 14 ++++ .../constants/initialization/type_info.sol | 23 ++++++ 30 files changed, 251 insertions(+) create mode 100644 test/libsolidity/syntaxTests/constants/initialization/abi_decode_const_args.sol create mode 100644 test/libsolidity/syntaxTests/constants/initialization/abi_decode_non_const_args.sol create mode 100644 test/libsolidity/syntaxTests/constants/initialization/abi_encode_call_const_args.sol create mode 100644 test/libsolidity/syntaxTests/constants/initialization/abi_encode_call_non_const_args.sol rename test/libsolidity/syntaxTests/constants/{abi_encoding_constant.sol => initialization/abi_encoding_builtin_const_args.sol} (100%) create mode 100644 test/libsolidity/syntaxTests/constants/initialization/abi_encoding_builtin_non_const_args.sol rename test/libsolidity/syntaxTests/constants/{addmod_zero.sol => initialization/addmod_by_zero.sol} (100%) rename test/libsolidity/syntaxTests/constants/{addmod_mulmod_rational.sol => initialization/addmod_mulmod_rational_arg.sol} (100%) create mode 100644 test/libsolidity/syntaxTests/constants/initialization/block_tx_msg_property.sol create mode 100644 test/libsolidity/syntaxTests/constants/initialization/bytes_concat_const_args.sol create mode 100644 test/libsolidity/syntaxTests/constants/initialization/bytes_concat_non_const_args.sol rename test/libsolidity/syntaxTests/constants/{cyclic_dependency_1.sol => initialization/constant_with_cyclic_dependency_1.sol} (100%) rename test/libsolidity/syntaxTests/constants/{cyclic_dependency_2.sol => initialization/constant_with_cyclic_dependency_2.sol} (100%) rename test/libsolidity/syntaxTests/constants/{cyclic_dependency_3.sol => initialization/constant_with_cyclic_dependency_3.sol} (100%) rename test/libsolidity/syntaxTests/constants/{cyclic_dependency_4.sol => initialization/constant_with_cyclic_dependency_4.sol} (100%) rename test/libsolidity/syntaxTests/constants/{cyclic_dependency_5.sol => initialization/constant_with_cyclic_dependency_5.sol} (100%) rename test/libsolidity/syntaxTests/constants/{cyclic_dependency_file_constants.sol => initialization/constant_with_cyclic_dependency_file.sol} (100%) rename test/libsolidity/syntaxTests/constants/{cyclic_dependency_file_and_library_constants.sol => initialization/constant_with_cyclic_dependency_file_and_library.sol} (100%) rename test/libsolidity/syntaxTests/constants/{cyclic_dependency_library_constants.sol => initialization/constant_with_cyclic_dependency_library.sol} (100%) rename test/libsolidity/syntaxTests/constants/{ => initialization}/division_by_zero.sol (100%) rename test/libsolidity/syntaxTests/constants/{assign_constant_function_value.sol => initialization/function_pointer_call.sol} (100%) create mode 100644 test/libsolidity/syntaxTests/constants/initialization/math_builtin_opcode_based_const_args.sol create mode 100644 test/libsolidity/syntaxTests/constants/initialization/math_builtin_opcode_based_non_const_args.sol create mode 100644 test/libsolidity/syntaxTests/constants/initialization/math_builtin_precompile_based_const_args.sol create mode 100644 test/libsolidity/syntaxTests/constants/initialization/math_builtin_precompile_based_non_const_args.sol rename test/libsolidity/syntaxTests/constants/{mod_zero.sol => initialization/modulo_by_zero.sol} (100%) rename test/libsolidity/syntaxTests/constants/{mulmod_zero.sol => initialization/mulmod_by_zero.sol} (100%) create mode 100644 test/libsolidity/syntaxTests/constants/initialization/string_concat_const_args.sol create mode 100644 test/libsolidity/syntaxTests/constants/initialization/string_concat_non_const_args.sol create mode 100644 test/libsolidity/syntaxTests/constants/initialization/type_info.sol diff --git a/test/libsolidity/syntaxTests/constants/initialization/abi_decode_const_args.sol b/test/libsolidity/syntaxTests/constants/initialization/abi_decode_const_args.sol new file mode 100644 index 0000000000..6f597ae73a --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/initialization/abi_decode_const_args.sol @@ -0,0 +1,7 @@ +bytes constant aEncoded = abi.encode( + hex"aaaa" +); + +contract A { + bytes constant a = abi.decode(aEncoded, (bytes)); +} diff --git a/test/libsolidity/syntaxTests/constants/initialization/abi_decode_non_const_args.sol b/test/libsolidity/syntaxTests/constants/initialization/abi_decode_non_const_args.sol new file mode 100644 index 0000000000..e5772333a0 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/initialization/abi_decode_non_const_args.sol @@ -0,0 +1,9 @@ +contract A { + function encoded() private view returns (bytes memory) { + return abi.encode(hex"aaaa"); + } + + bytes constant a = abi.decode(encoded(), (bytes)); +} +// ---- +// TypeError 8349: (142-172): Initial value for constant variable has to be compile-time constant. diff --git a/test/libsolidity/syntaxTests/constants/initialization/abi_encode_call_const_args.sol b/test/libsolidity/syntaxTests/constants/initialization/abi_encode_call_const_args.sol new file mode 100644 index 0000000000..e716694cc3 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/initialization/abi_encode_call_const_args.sol @@ -0,0 +1,5 @@ +contract A { + bool constant FLAG = true; + function f(uint, bool) external {} + bytes constant fCallA = abi.encodeCall(A.f, (123, FLAG)); +} diff --git a/test/libsolidity/syntaxTests/constants/initialization/abi_encode_call_non_const_args.sol b/test/libsolidity/syntaxTests/constants/initialization/abi_encode_call_non_const_args.sol new file mode 100644 index 0000000000..b49213742e --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/initialization/abi_encode_call_non_const_args.sol @@ -0,0 +1,11 @@ +contract A { + function f(uint a) external {} + + function getA() private view returns(uint) { + return 1; + } + + bytes constant fCallA = abi.encodeCall(A.f, (getA())); +} +// ---- +// TypeError 8349: (151-180): Initial value for constant variable has to be compile-time constant. diff --git a/test/libsolidity/syntaxTests/constants/abi_encoding_constant.sol b/test/libsolidity/syntaxTests/constants/initialization/abi_encoding_builtin_const_args.sol similarity index 100% rename from test/libsolidity/syntaxTests/constants/abi_encoding_constant.sol rename to test/libsolidity/syntaxTests/constants/initialization/abi_encoding_builtin_const_args.sol diff --git a/test/libsolidity/syntaxTests/constants/initialization/abi_encoding_builtin_non_const_args.sol b/test/libsolidity/syntaxTests/constants/initialization/abi_encoding_builtin_non_const_args.sol new file mode 100644 index 0000000000..7e399a0d00 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/initialization/abi_encoding_builtin_non_const_args.sol @@ -0,0 +1,13 @@ +contract C { + uint k = 1; + + bytes32 constant a = keccak256(abi.encode(1, k)); + bytes32 constant b = keccak256(abi.encodePacked(uint(1), k)); + bytes32 constant c = keccak256(abi.encodeWithSelector(0x12345678, k, 2)); + bytes32 constant d = keccak256(abi.encodeWithSignature("f()", 1, k)); +} +// ---- +// TypeError 8349: (55-82): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (109-148): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (175-226): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (253-300): Initial value for constant variable has to be compile-time constant. diff --git a/test/libsolidity/syntaxTests/constants/addmod_zero.sol b/test/libsolidity/syntaxTests/constants/initialization/addmod_by_zero.sol similarity index 100% rename from test/libsolidity/syntaxTests/constants/addmod_zero.sol rename to test/libsolidity/syntaxTests/constants/initialization/addmod_by_zero.sol diff --git a/test/libsolidity/syntaxTests/constants/addmod_mulmod_rational.sol b/test/libsolidity/syntaxTests/constants/initialization/addmod_mulmod_rational_arg.sol similarity index 100% rename from test/libsolidity/syntaxTests/constants/addmod_mulmod_rational.sol rename to test/libsolidity/syntaxTests/constants/initialization/addmod_mulmod_rational_arg.sol diff --git a/test/libsolidity/syntaxTests/constants/initialization/block_tx_msg_property.sol b/test/libsolidity/syntaxTests/constants/initialization/block_tx_msg_property.sol new file mode 100644 index 0000000000..54381a2573 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/initialization/block_tx_msg_property.sol @@ -0,0 +1,80 @@ +bytes32 constant blockhGlobal = blockhash(1); +bytes32 constant blobhGlobal = blobhash(1); +uint constant bfGlobal = block.basefee; +uint constant blobbfGlobal = block.blobbasefee; +uint constant chainIdGlobal = block.chainid; +address constant coinbaseGlobal = block.coinbase; +uint constant diffGlobal = block.difficulty; +uint constant gaslimitGlobal = block.gaslimit; +uint constant numberGlobal = block.number; +uint constant prevrandaoGlobal = block.prevrandao; +uint constant timestampGlobal = block.timestamp; +uint constant gGlobal = gasleft(); +bytes constant dataGlobal = msg.data; +address constant senderGlobal = msg.sender; +bytes4 constant sigGlobal = msg.sig; +uint constant valueGlobal = msg.value; +uint constant gaspriceGlobal = tx.gasprice; +address constant originGlobal = tx.origin; + +contract A { + bytes32 constant blockh = blockhash(1); + bytes32 constant blobh = blobhash(1); + uint constant bf = block.basefee; + uint constant blobbf = block.blobbasefee; + uint constant chainId = block.chainid; + address constant coinbase = block.coinbase; + uint constant diff = block.difficulty; + uint constant gaslimit = block.gaslimit; + uint constant number = block.number; + uint constant prevrandao = block.prevrandao; + uint constant timestamp = block.timestamp; + uint constant g = gasleft(); + bytes constant data = msg.data; + address constant sender = msg.sender; + bytes4 constant sig = msg.sig; + uint constant value = msg.value; + uint constant gasprice = tx.gasprice; + address constant origin = tx.origin; +} +// ==== +// EVMVersion: >=cancun +// ---- +// TypeError 8349: (32-44): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (77-88): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (115-128): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (159-176): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (208-221): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (257-271): Initial value for constant variable has to be compile-time constant. +// Warning 8417: (300-316): Since the VM version paris, "difficulty" was replaced by "prevrandao", which now returns a random number based on the beacon chain. +// TypeError 8349: (300-316): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (349-363): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (394-406): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (441-457): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (491-506): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (532-541): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (571-579): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (613-623): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (653-660): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (690-699): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (732-743): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (777-786): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (832-844): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (875-886): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (911-924): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (953-970): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (1000-1013): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (1047-1061): Initial value for constant variable has to be compile-time constant. +// Warning 8417: (1088-1104): Since the VM version paris, "difficulty" was replaced by "prevrandao", which now returns a random number based on the beacon chain. +// TypeError 8349: (1088-1104): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (1135-1149): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (1178-1190): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (1223-1239): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (1271-1286): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (1310-1319): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (1347-1355): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (1387-1397): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (1425-1432): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (1460-1469): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (1500-1511): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (1543-1552): Initial value for constant variable has to be compile-time constant. diff --git a/test/libsolidity/syntaxTests/constants/initialization/bytes_concat_const_args.sol b/test/libsolidity/syntaxTests/constants/initialization/bytes_concat_const_args.sol new file mode 100644 index 0000000000..2d87ba56c4 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/initialization/bytes_concat_const_args.sol @@ -0,0 +1,11 @@ +bytes constant aaaa = hex"aaaa"; + +bytes constant abcGlobal = bytes.concat(aaaa, hex"bbbb", hex"cccc"); + +contract A { + bytes public constant abc = bytes.concat(aaaa, hex"bbbb", hex"cccc"); + + bytes public constant abcCopy = abc; + bytes public constant abcGlobalCopy = abcGlobal; + bytes public constant abcabc = bytes.concat(abc, abcGlobal); +} diff --git a/test/libsolidity/syntaxTests/constants/initialization/bytes_concat_non_const_args.sol b/test/libsolidity/syntaxTests/constants/initialization/bytes_concat_non_const_args.sol new file mode 100644 index 0000000000..225731c1ff --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/initialization/bytes_concat_non_const_args.sol @@ -0,0 +1,17 @@ +contract A { + function getData() public view returns (bytes memory) { + return msg.data; + } + + function getDataPure() public pure returns (bytes memory) { + return hex"ffff"; + } + + bytes constant abData = bytes.concat(hex"aaaa", hex"bbbb", msg.data); + bytes constant abgetData = bytes.concat(hex"aaaa", hex"bbbb", getData()); + bytes constant abgetDataPure = bytes.concat(hex"aaaa", hex"bbbb", getDataPure()); +} +// ---- +// TypeError 8349: (230-274): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (307-352): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (389-438): Initial value for constant variable has to be compile-time constant. diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol b/test/libsolidity/syntaxTests/constants/initialization/constant_with_cyclic_dependency_1.sol similarity index 100% rename from test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol rename to test/libsolidity/syntaxTests/constants/initialization/constant_with_cyclic_dependency_1.sol diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol b/test/libsolidity/syntaxTests/constants/initialization/constant_with_cyclic_dependency_2.sol similarity index 100% rename from test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol rename to test/libsolidity/syntaxTests/constants/initialization/constant_with_cyclic_dependency_2.sol diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol b/test/libsolidity/syntaxTests/constants/initialization/constant_with_cyclic_dependency_3.sol similarity index 100% rename from test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol rename to test/libsolidity/syntaxTests/constants/initialization/constant_with_cyclic_dependency_3.sol diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol b/test/libsolidity/syntaxTests/constants/initialization/constant_with_cyclic_dependency_4.sol similarity index 100% rename from test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol rename to test/libsolidity/syntaxTests/constants/initialization/constant_with_cyclic_dependency_4.sol diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_5.sol b/test/libsolidity/syntaxTests/constants/initialization/constant_with_cyclic_dependency_5.sol similarity index 100% rename from test/libsolidity/syntaxTests/constants/cyclic_dependency_5.sol rename to test/libsolidity/syntaxTests/constants/initialization/constant_with_cyclic_dependency_5.sol diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_file_constants.sol b/test/libsolidity/syntaxTests/constants/initialization/constant_with_cyclic_dependency_file.sol similarity index 100% rename from test/libsolidity/syntaxTests/constants/cyclic_dependency_file_constants.sol rename to test/libsolidity/syntaxTests/constants/initialization/constant_with_cyclic_dependency_file.sol diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_file_and_library_constants.sol b/test/libsolidity/syntaxTests/constants/initialization/constant_with_cyclic_dependency_file_and_library.sol similarity index 100% rename from test/libsolidity/syntaxTests/constants/cyclic_dependency_file_and_library_constants.sol rename to test/libsolidity/syntaxTests/constants/initialization/constant_with_cyclic_dependency_file_and_library.sol diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_library_constants.sol b/test/libsolidity/syntaxTests/constants/initialization/constant_with_cyclic_dependency_library.sol similarity index 100% rename from test/libsolidity/syntaxTests/constants/cyclic_dependency_library_constants.sol rename to test/libsolidity/syntaxTests/constants/initialization/constant_with_cyclic_dependency_library.sol diff --git a/test/libsolidity/syntaxTests/constants/division_by_zero.sol b/test/libsolidity/syntaxTests/constants/initialization/division_by_zero.sol similarity index 100% rename from test/libsolidity/syntaxTests/constants/division_by_zero.sol rename to test/libsolidity/syntaxTests/constants/initialization/division_by_zero.sol diff --git a/test/libsolidity/syntaxTests/constants/assign_constant_function_value.sol b/test/libsolidity/syntaxTests/constants/initialization/function_pointer_call.sol similarity index 100% rename from test/libsolidity/syntaxTests/constants/assign_constant_function_value.sol rename to test/libsolidity/syntaxTests/constants/initialization/function_pointer_call.sol diff --git a/test/libsolidity/syntaxTests/constants/initialization/math_builtin_opcode_based_const_args.sol b/test/libsolidity/syntaxTests/constants/initialization/math_builtin_opcode_based_const_args.sol new file mode 100644 index 0000000000..aa13e02b4d --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/initialization/math_builtin_opcode_based_const_args.sol @@ -0,0 +1,14 @@ +uint256 constant k = 7; +bytes constant data = hex"ffff"; + +uint256 constant amodGlobal = addmod(1, 8, k); +uint256 constant mmodGlobal = mulmod(1, 8, k); +bytes32 constant keccakGlobal = keccak256(hex"ffff"); +bytes32 constant keccakConstArgGlobal = keccak256(data); + +contract A { + uint256 constant amod = addmod(1, 8, k); + uint256 constant mmod = mulmod(1, 8, k); + bytes32 constant keccak = keccak256(hex"ffff"); + bytes32 constant keccakConstArg = keccak256(data); +} diff --git a/test/libsolidity/syntaxTests/constants/initialization/math_builtin_opcode_based_non_const_args.sol b/test/libsolidity/syntaxTests/constants/initialization/math_builtin_opcode_based_non_const_args.sol new file mode 100644 index 0000000000..208437ab70 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/initialization/math_builtin_opcode_based_non_const_args.sol @@ -0,0 +1,12 @@ +contract A { + uint256 k = 7; + uint256 constant amod = addmod(1, 8, k); + uint256 constant mmod = mulmod(1, 8, k); + + bytes data = hex"ffff"; + bytes32 constant keccak = keccak256(data); +} +// ---- +// TypeError 8349: (60-75): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (105-120): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (181-196): Initial value for constant variable has to be compile-time constant. diff --git a/test/libsolidity/syntaxTests/constants/initialization/math_builtin_precompile_based_const_args.sol b/test/libsolidity/syntaxTests/constants/initialization/math_builtin_precompile_based_const_args.sol new file mode 100644 index 0000000000..57626ec2f3 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/initialization/math_builtin_precompile_based_const_args.sol @@ -0,0 +1,16 @@ +uint8 constant k = 1; +bytes constant data = hex"ffff"; + +bytes32 constant sGlobal = sha256(hex"ffff"); +bytes32 constant sConstArgGlobal = sha256(data); +address constant addrGlobal = ecrecover("1234", k, "0", abi.decode("", (bytes2))); +bytes20 constant ripemdGlobal = ripemd160(hex"ffff"); +bytes20 constant ripemdConstArgGlobal = ripemd160(data); + +contract A { + bytes32 constant s = sha256(hex"ffff"); + bytes32 constant sConstArg = sha256(data); + address constant addr = ecrecover("1234", k, "0", abi.decode("", (bytes2))); + bytes20 constant ripemd = ripemd160(hex"ffff"); + bytes20 constant ripemdConstArg = ripemd160(data); +} diff --git a/test/libsolidity/syntaxTests/constants/initialization/math_builtin_precompile_based_non_const_args.sol b/test/libsolidity/syntaxTests/constants/initialization/math_builtin_precompile_based_non_const_args.sol new file mode 100644 index 0000000000..fef5ccc88c --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/initialization/math_builtin_precompile_based_non_const_args.sol @@ -0,0 +1,10 @@ +contract A { + bytes data = hex"ffff"; + bytes32 constant sha = sha256(data); + bytes20 constant ripemd = ripemd160(data); + address constant addr = ecrecover("1234", 1, "0", abi.decode(data, (bytes2))); +} +// ---- +// TypeError 8349: (68-80): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (112-127): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (157-210): Initial value for constant variable has to be compile-time constant. diff --git a/test/libsolidity/syntaxTests/constants/mod_zero.sol b/test/libsolidity/syntaxTests/constants/initialization/modulo_by_zero.sol similarity index 100% rename from test/libsolidity/syntaxTests/constants/mod_zero.sol rename to test/libsolidity/syntaxTests/constants/initialization/modulo_by_zero.sol diff --git a/test/libsolidity/syntaxTests/constants/mulmod_zero.sol b/test/libsolidity/syntaxTests/constants/initialization/mulmod_by_zero.sol similarity index 100% rename from test/libsolidity/syntaxTests/constants/mulmod_zero.sol rename to test/libsolidity/syntaxTests/constants/initialization/mulmod_by_zero.sol diff --git a/test/libsolidity/syntaxTests/constants/initialization/string_concat_const_args.sol b/test/libsolidity/syntaxTests/constants/initialization/string_concat_const_args.sol new file mode 100644 index 0000000000..bc676d1015 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/initialization/string_concat_const_args.sol @@ -0,0 +1,9 @@ +string constant abcGlobal = string.concat("aaaa", "bbbb", "cccc"); + +contract A { + string public constant abc = string.concat("aaaa", "bbbb","cccc"); + + string public constant abcCopy = abc; + string public constant abcGlobalCopy = abcGlobal; + string public constant abcabc = string.concat(abc, abcGlobal); +} diff --git a/test/libsolidity/syntaxTests/constants/initialization/string_concat_non_const_args.sol b/test/libsolidity/syntaxTests/constants/initialization/string_concat_non_const_args.sol new file mode 100644 index 0000000000..f8b4aa6e29 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/initialization/string_concat_non_const_args.sol @@ -0,0 +1,14 @@ +contract A { + string name = "name"; + + function getName() public view returns (string memory) { + return name; + } + + string public constant abName = string.concat("aaaa", "bbbb", name); + + string public constant abgetName = string.concat("aaaa", "bbbb",getName()); +} +// ---- +// TypeError 8349: (165-200): Initial value for constant variable has to be compile-time constant. +// TypeError 8349: (242-281): Initial value for constant variable has to be compile-time constant. diff --git a/test/libsolidity/syntaxTests/constants/initialization/type_info.sol b/test/libsolidity/syntaxTests/constants/initialization/type_info.sol new file mode 100644 index 0000000000..51cfaa3a56 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/initialization/type_info.sol @@ -0,0 +1,23 @@ +string constant aNameGlobal = type(A).name; +bytes4 constant iNameGlobal = type(I).interfaceId; +uint256 constant minGlobal = type(uint256).min; +uint256 constant maxGlobal = type(uint256).max; +bytes constant creationCodeBGlobal = type(B).creationCode; +bytes constant runtimeCodeBGlobal = type(B).runtimeCode; + +contract B { +} + +interface I { + function hello() external pure; + function world(int) external pure; +} + +contract A { + string constant aName = type(A).name; + bytes4 constant iName = type(I).interfaceId; + uint256 constant min = type(uint256).min; + uint256 constant max = type(uint256).max; + bytes constant creationCodeB = type(B).creationCode; + bytes constant runtimeCodeB = type(B).runtimeCode; +} From a4bd1b760aef7e8deaec6c4303a36cf9fed5ac1c Mon Sep 17 00:00:00 2001 From: rodiazet Date: Wed, 17 Dec 2025 13:14:21 +0100 Subject: [PATCH 118/243] Add semantic test --- .../semanticTests/constants/assign_type_info.sol | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 test/libsolidity/semanticTests/constants/assign_type_info.sol diff --git a/test/libsolidity/semanticTests/constants/assign_type_info.sol b/test/libsolidity/semanticTests/constants/assign_type_info.sol new file mode 100644 index 0000000000..0fca83d547 --- /dev/null +++ b/test/libsolidity/semanticTests/constants/assign_type_info.sol @@ -0,0 +1,13 @@ +contract A { +} + +contract B { + bytes constant creationCode = type(A).creationCode; + bytes constant runtimeCode = type(A).runtimeCode; + + function nonEmptyCode() public pure returns (bool) { + return creationCode.length > 0 && runtimeCode.length > 0; + } +} +// ---- +// nonEmptyCode() -> true From ba32d8b5ab93219ff343e7162d619c0e66f011f0 Mon Sep 17 00:00:00 2001 From: rodiazet Date: Wed, 17 Dec 2025 13:14:50 +0100 Subject: [PATCH 119/243] Update tests. Change error message. --- .../storageLayoutSpecifier/layout_specified_by_bytes_concat.sol | 2 +- .../storageLayoutSpecifier/layout_specified_by_kecak256.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_bytes_concat.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_bytes_concat.sol index d341a5a764..77860af8ec 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_bytes_concat.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_bytes_concat.sol @@ -1,3 +1,3 @@ contract C layout at uint64(bytes8(bytes.concat("ABCD", "EFGH"))) {} // ---- -// TypeError 1139: (21-65): The base slot of the storage layout must be a compile-time constant expression. +// TypeError 1505: (21-65): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_kecak256.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_kecak256.sol index 539a59c11c..0b35d60d89 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_kecak256.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/layout_specified_by_kecak256.sol @@ -1,3 +1,3 @@ contract C layout at uint(keccak256(bytes.concat("ABCD"))) {} // ---- -// TypeError 1139: (21-58): The base slot of the storage layout must be a compile-time constant expression. +// TypeError 1505: (21-58): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. From 91a49db7188cba31dc272726e57d17255ca837e4 Mon Sep 17 00:00:00 2001 From: rodiazet Date: Wed, 17 Dec 2025 13:15:12 +0100 Subject: [PATCH 120/243] Update tests. Modify to avoid the no effect warning. --- .../array/concat/bytes_concat_valid_type_literal.sol | 4 ++-- .../concat/bytes_concat_wrong_type_empty_string_literal.sol | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/libsolidity/syntaxTests/array/concat/bytes_concat_valid_type_literal.sol b/test/libsolidity/syntaxTests/array/concat/bytes_concat_valid_type_literal.sol index f39e903963..ff003ca8a0 100644 --- a/test/libsolidity/syntaxTests/array/concat/bytes_concat_valid_type_literal.sol +++ b/test/libsolidity/syntaxTests/array/concat/bytes_concat_valid_type_literal.sol @@ -1,6 +1,6 @@ contract C { - function f() public pure { - bytes.concat( + function f() public pure returns (bytes memory) { + return bytes.concat( hex"00", hex"aabbcc", unicode"abc", diff --git a/test/libsolidity/syntaxTests/array/concat/bytes_concat_wrong_type_empty_string_literal.sol b/test/libsolidity/syntaxTests/array/concat/bytes_concat_wrong_type_empty_string_literal.sol index afba249f8d..a58cf9f3dd 100644 --- a/test/libsolidity/syntaxTests/array/concat/bytes_concat_wrong_type_empty_string_literal.sol +++ b/test/libsolidity/syntaxTests/array/concat/bytes_concat_wrong_type_empty_string_literal.sol @@ -1,6 +1,6 @@ contract C { - function f() public pure { - bytes.concat(hex"", unicode"", ""); + function f() public pure returns (bytes memory) { + return bytes.concat(hex"", unicode"", ""); } } // ---- From 2c0acc50c4fac51e6d2ee3ea876061b7721ac663 Mon Sep 17 00:00:00 2001 From: rodiazet Date: Wed, 17 Dec 2025 13:15:25 +0100 Subject: [PATCH 121/243] Update changelog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kamil Śliwak --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 27e1cb33a2..26f28d21d9 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Compiler Features: Bugfixes: * Codegen: Fix internal compiler error when emitting events via module member access. * TypeChecker: Fix error and event selectors not being considered compile-time constant. +* TypeChecker: Fix `string.concat` and `bytes.concat` with constant arguments not being considered compile-time constant. ### 0.8.31 (2025-12-03) From eb6a45904a3e7e3c7e35eff7cc5d813658848034 Mon Sep 17 00:00:00 2001 From: rodiazet Date: Wed, 17 Dec 2025 14:19:37 +0100 Subject: [PATCH 122/243] Add sanity check in the end of `TypeChecker::visit(MemberAccess const& _memberAccess)` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kamil Śliwak --- libsolidity/analysis/TypeChecker.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 8556487ff4..ab8f00a308 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -3409,6 +3409,16 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) if (!annotation.isPure.set()) annotation.isPure = false; + if ( + auto const* funcType = dynamic_cast(annotation.type); + funcType && + funcType->kind() != FunctionType::Kind::Declaration && + funcType->kind() != FunctionType::Kind::Internal && + funcType->kind() != FunctionType::Kind::Error && + funcType->kind() != FunctionType::Kind::Event + ) + solAssert(funcType->isPure() == *annotation.isPure); + return false; } From b28145c60e490a7504ca1c2e4edac16f5ec2e5e7 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Thu, 11 Dec 2025 10:10:47 -0300 Subject: [PATCH 123/243] Update docs about solc-bin.ethereum.org deprecation in favor of binaries.soliditylang.org MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kamil Śliwak --- docs/installing-solidity.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 01a2f38204..97a754eaaa 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -270,10 +270,11 @@ This means that: if you want to be sure whether you are downloading a wasm or an asm.js binary. - Use ``list.json`` instead of ``list.js`` and ``list.txt``. The JSON list format contains all the information from the old ones and more. - - Use https://binaries.soliditylang.org instead of https://solc-bin.ethereum.org. To keep things - simple we moved almost everything related to the compiler under the new ``soliditylang.org`` - domain and this applies to ``solc-bin`` too. While the new domain is recommended, the old one - is still fully supported and guaranteed to point at the same location. + +.. warning:: + - The solc-bin.ethereum.org domain is no longer supported. Going forward, + we recommend any tools which are still using it as the source of Solidity binaries + to switch to binaries.soliditylang.org. .. warning:: From 1c61064d0851acc5dc565e6bac10bf327ed79a2d Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 18 Dec 2025 06:02:37 +0100 Subject: [PATCH 124/243] Fix changelog for 0.8.32 --- Changelog.md | 5 +---- docs/bugs_by_version.json | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 26f28d21d9..ad0916815d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,11 +1,8 @@ -### 0.8.32 (unreleased) +### 0.8.32 (2025-12-18) Important Bugfixes: * Code Generator: Fix a bug in clearing/copying of arrays that straddle the end of storage, potentially resulting in assignment/initialization/`delete`/`push()`/`pop()` skipping some or all of the intended storage writes. -Language Features: - -Compiler Features: Bugfixes: * Codegen: Fix internal compiler error when emitting events via module member access. diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 94e814892d..2ecc3c2356 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -2049,6 +2049,10 @@ ], "released": "2025-12-03" }, + "0.8.32": { + "bugs": [], + "released": "2025-12-18" + }, "0.8.4": { "bugs": [ "LostStorageArrayWriteOnSlotOverflow", From 1ebf887357df5afc6e31ff19825eb9f4784a56a9 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Wed, 17 Dec 2025 17:28:00 -0300 Subject: [PATCH 125/243] set version to 0.8.33 --- CMakeLists.txt | 2 +- Changelog.md | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f4d67e243b..5792563eb3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.8.32") +set(PROJECT_VERSION "0.8.33") # OSX target needed in order to support std::visit set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) diff --git a/Changelog.md b/Changelog.md index ad0916815d..9f533332e1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,12 @@ +### 0.8.33 (unreleased) + +Language Features: + +Compiler Features: + +Bugfixes: + + ### 0.8.32 (2025-12-18) Important Bugfixes: From c6aecabaddc313dd81d28bc2a4e47434d4c1b95f Mon Sep 17 00:00:00 2001 From: rodiazet Date: Thu, 18 Dec 2025 14:51:27 +0100 Subject: [PATCH 126/243] Remove the sanity check. --- libsolidity/analysis/TypeChecker.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index ab8f00a308..8556487ff4 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -3409,16 +3409,6 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) if (!annotation.isPure.set()) annotation.isPure = false; - if ( - auto const* funcType = dynamic_cast(annotation.type); - funcType && - funcType->kind() != FunctionType::Kind::Declaration && - funcType->kind() != FunctionType::Kind::Internal && - funcType->kind() != FunctionType::Kind::Error && - funcType->kind() != FunctionType::Kind::Event - ) - solAssert(funcType->isPure() == *annotation.isPure); - return false; } From 27af4dcfb9247707efe002d0ce6aac9185fdbd72 Mon Sep 17 00:00:00 2001 From: rodiazet Date: Thu, 18 Dec 2025 14:57:57 +0100 Subject: [PATCH 127/243] Add tests. --- .../syntaxTests/constants/access_via_getter.sol | 8 ++++++++ .../syntaxTests/immutable/access_via_getter.sol | 8 ++++++++ 2 files changed, 16 insertions(+) create mode 100644 test/libsolidity/syntaxTests/constants/access_via_getter.sol create mode 100644 test/libsolidity/syntaxTests/immutable/access_via_getter.sol diff --git a/test/libsolidity/syntaxTests/constants/access_via_getter.sol b/test/libsolidity/syntaxTests/constants/access_via_getter.sol new file mode 100644 index 0000000000..686c5a0392 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/access_via_getter.sol @@ -0,0 +1,8 @@ +contract Counter { + uint256 public constant MIN_LIQUIDITY = 1000; + + function run() view public { + this.MIN_LIQUIDITY(); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/immutable/access_via_getter.sol b/test/libsolidity/syntaxTests/immutable/access_via_getter.sol new file mode 100644 index 0000000000..6ce9e42c43 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/access_via_getter.sol @@ -0,0 +1,8 @@ +contract Counter { + uint256 public immutable MIN_LIQUIDITY = 1000; + + function run() view public { + this.MIN_LIQUIDITY(); + } +} +// ---- From 07bf6e13a88a6f831b2b0055214aa8bc6cf75f09 Mon Sep 17 00:00:00 2001 From: rodiazet Date: Thu, 18 Dec 2025 15:04:37 +0100 Subject: [PATCH 128/243] Update changelog. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 9f533332e1..61bde4e259 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Language Features: Compiler Features: Bugfixes: +* TypeChecker: Fix internal error when accessing getters of constant variables. ### 0.8.32 (2025-12-18) From c8ebb1104691ddcb463bcdbc5f9a5fa743de33ab Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 18 Dec 2025 17:36:49 +0100 Subject: [PATCH 129/243] Sort changelog and date for 0.8.33 --- Changelog.md | 6 +----- docs/bugs_by_version.json | 4 ++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index 61bde4e259..cb78695bc5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,8 +1,4 @@ -### 0.8.33 (unreleased) - -Language Features: - -Compiler Features: +### 0.8.33 (2025-12-18) Bugfixes: * TypeChecker: Fix internal error when accessing getters of constant variables. diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 2ecc3c2356..8a4d2fe23b 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -2053,6 +2053,10 @@ "bugs": [], "released": "2025-12-18" }, + "0.8.33": { + "bugs": [], + "released": "2025-12-18" + }, "0.8.4": { "bugs": [ "LostStorageArrayWriteOnSlotOverflow", From a50affba1afa11cbd0b4a656dc5a753fc5be0137 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Thu, 18 Dec 2025 12:36:09 -0300 Subject: [PATCH 130/243] Set version to 0.8.34 --- CMakeLists.txt | 2 +- Changelog.md | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5792563eb3..82afc5b67d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.8.33") +set(PROJECT_VERSION "0.8.34") # OSX target needed in order to support std::visit set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) diff --git a/Changelog.md b/Changelog.md index cb78695bc5..655e66e541 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,12 @@ +### 0.8.34 (unreleased) + +Language Features: + +Compiler Features: + +Bugfixes: + + ### 0.8.33 (2025-12-18) Bugfixes: From 779f590beb82d11e39dcec821507fb4d4b660c60 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Thu, 18 Dec 2025 00:19:12 -0300 Subject: [PATCH 131/243] Add Constantinople EVM version requirement to long byte array cleanup test due to the use of shl --- .../array/long_byte_array_cleanup_after_overwrite_with_long.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_overwrite_with_long.sol b/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_overwrite_with_long.sol index edb00809bc..c796881c4c 100644 --- a/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_overwrite_with_long.sol +++ b/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_overwrite_with_long.sol @@ -82,6 +82,8 @@ contract C { return result; } } +// ==== +// EVMVersion: >=constantinople // ---- // arrayLength() ->0 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff From 7e371ddc0561033ccca7ffa1cee8f29ffee12ab5 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Sun, 21 Dec 2025 17:06:57 -0300 Subject: [PATCH 132/243] Enables pipefail globally so parsing errors fail CI instead of being silently ignored --- scripts/externalTests/common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/externalTests/common.sh b/scripts/externalTests/common.sh index 0465a4fec5..c6f5ef6d39 100644 --- a/scripts/externalTests/common.sh +++ b/scripts/externalTests/common.sh @@ -18,7 +18,7 @@ # # (c) 2019 solidity contributors. #------------------------------------------------------------------------------ -set -e +set -eo pipefail # Requires $REPO_ROOT to be defined and "${REPO_ROOT}/scripts/common.sh" to be included before. From e882f5343565183a3b290e60df2f9b7ce907d6b4 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Sun, 21 Dec 2025 17:07:15 -0300 Subject: [PATCH 133/243] Fix external tests for hardhat-gas-reporter v2 compatibility --- scripts/externalTests/common.sh | 4 ++-- scripts/externalTests/parse_eth_gas_report.py | 17 +++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/scripts/externalTests/common.sh b/scripts/externalTests/common.sh index c6f5ef6d39..7e480343ac 100644 --- a/scripts/externalTests/common.sh +++ b/scripts/externalTests/common.sh @@ -434,9 +434,9 @@ function eth_gas_reporter_settings echo " enabled: true," echo " gasPrice: 1," # Gas price does not matter to us at all. Set to whatever to avoid API call. echo " noColors: true," - echo " showTimeSpent: false," # We're not interested in test timing - echo " onlyCalledMethods: true," # Exclude entries with no gas for shorter report + echo " showUncalledMethods: false," # Exclude entries with no gas for shorter report echo " showMethodSig: true," # Should make diffs more stable if there are overloaded functions + echo " reportFormat: 'legacy'," # Use v1.x format for compatibility with parse_eth_gas_report.py echo " outputFile: \"$(gas_report_path "$preset")\"" echo "}" } diff --git a/scripts/externalTests/parse_eth_gas_report.py b/scripts/externalTests/parse_eth_gas_report.py index ea8cbba66a..d7dbe45fa8 100755 --- a/scripts/externalTests/parse_eth_gas_report.py +++ b/scripts/externalTests/parse_eth_gas_report.py @@ -10,8 +10,8 @@ REPORT_HEADER_REGEX = re.compile(r''' ^[|\s]+ Solc[ ]version:\s*(?P[\w\d.]+) [|\s]+ Optimizer[ ]enabled:\s*(?P[\w]+) - [|\s]+ Runs:\s*(?P[\d]+) - [|\s]+ Block[ ]limit:\s*(?P[\d]+)\s*gas + [|\s]+ Runs:\s*(?P[\d,]+) + [|\s]+ Block[ ]limit:\s*(?P[\d,]+)\s*gas [|\s]+$ ''', re.VERBOSE) METHOD_HEADER_REGEX = re.compile(r'^[|\s]+Methods[|\s]+$') @@ -122,10 +122,11 @@ def parse_bool(input_string: str) -> bool: def parse_optional_int(input_string: str, default: Optional[int] = None) -> Optional[int]: - if input_string.strip() == '-': + stripped = input_string.strip().replace(',', '') + if stripped == '-': return default - return int(input_string) + return int(stripped) def parse_report_header(line: str) -> Optional[dict]: @@ -136,8 +137,8 @@ def parse_report_header(line: str) -> Optional[dict]: return { 'solc_version': match.group('solc_version'), 'optimize': parse_bool(match.group('optimize')), - 'runs': int(match.group('runs')), - 'block_limit': int(match.group('block_limit')), + 'runs': int(match.group('runs').replace(',', '')), + 'block_limit': int(match.group('block_limit').replace(',', '')), } @@ -147,7 +148,7 @@ def parse_method_row(line: str, line_number: int) -> Optional[Tuple[str, str, Me raise ReportParsingError("Expected a table row with method details.", line, line_number) avg_gas = parse_optional_int(match['avg']) - call_count = int(match['call_count']) + call_count = int(match['call_count'].strip().replace(',', '')) if avg_gas is None and call_count == 0: # No calls, no gas values. Uninteresting. Skip the row. @@ -174,7 +175,7 @@ def parse_deployment_row(line: str, line_number: int) -> Tuple[str, int, int, in match['contract'].strip(), parse_optional_int(match['min'].strip()), parse_optional_int(match['max'].strip()), - int(match['avg'].strip()), + int(match['avg'].strip().replace(',', '')), ) From 1cd9bc2ff5f1d2a12c01980d5a9cb9bf322f6370 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Mon, 22 Dec 2025 10:43:52 -0300 Subject: [PATCH 134/243] Bump hardhat-gas-reporter to v2 in gp2 external test --- test/externalTests/gp2.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/externalTests/gp2.sh b/test/externalTests/gp2.sh index cc8b56b665..78f21e8e28 100755 --- a/test/externalTests/gp2.sh +++ b/test/externalTests/gp2.sh @@ -68,7 +68,8 @@ function gp2_test yarn # Hardhat 3.0+ breaks the test suite # v2.27.1 is the last v2 Hardhat (introduces Osaka support) - yarn add hardhat@2.27.1 + # hardhat-gas-reporter v2 is required for compatibility with our injected gas reporter settings + yarn add hardhat@2.27.1 hardhat-gas-reporter@^2 # Ignore bench directory which fails to compile with current hardhat and ethers versions. # bench/trace/gas.ts:123:19 - error TS2339: Property 'equals' does not exist on type 'Uint8Array'. From d2711509d64320129b7b8fd0535fde4f28cf0e85 Mon Sep 17 00:00:00 2001 From: Manuel Wedler Date: Wed, 17 Dec 2025 09:48:09 +0100 Subject: [PATCH 135/243] ci: Replace `t_ubu_soltest_all` with `t_ubu_soltest` --- .circleci/config.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3cab7fbadb..12fab84cd0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1292,6 +1292,18 @@ jobs: destination: docs-html - matrix_notify_failure_unless_pr + t_ubu_soltest: &t_ubu_soltest + <<: *base_ubuntu2404_large + parallelism: 20 + environment: + <<: *base_ubuntu2404_large_env + EVM: << pipeline.parameters.evm-version >> + EOF_VERSION: 0 + OPTIMIZE: 0 + SOLTEST_FLAGS: --no-smt + steps: + - soltest + t_ubu_soltest_all: &t_ubu_soltest_all <<: *base_ubuntu2404_large parallelism: 50 @@ -2038,7 +2050,7 @@ workflows: - t_ubu_cli: *requires_b_ubu - t_ubu_arm_cli: *requires_b_ubu_static_arm - t_ubu_locale: *requires_b_ubu - - t_ubu_soltest_all: *requires_b_ubu + - t_ubu_soltest: *requires_b_ubu - t_ubu_arm_soltest: *requires_b_ubu_static_arm - b_ubu_clang: *requires_nothing - t_ubu_clang_soltest: *requires_b_ubu_clang From 78dd4d15d0ebbc53246e4460d305cceaa5e74727 Mon Sep 17 00:00:00 2001 From: Manuel Wedler Date: Mon, 22 Dec 2025 09:21:13 +0100 Subject: [PATCH 136/243] ci: Remove `t_ubu_soltest_all` job --- .circleci/config.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 12fab84cd0..557b08f8f3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1304,12 +1304,6 @@ jobs: steps: - soltest - t_ubu_soltest_all: &t_ubu_soltest_all - <<: *base_ubuntu2404_large - parallelism: 50 - steps: - - soltest_all - t_ubu_arm_soltest: &t_ubu_arm_soltest <<: *base_ubuntu2404_arm_medium parallelism: 20 @@ -1384,9 +1378,10 @@ jobs: - soltest t_ubu_force_release_soltest_all: &t_ubu_force_release_soltest_all - # NOTE: This definition is identical to t_ubu_soltest_all but in the workflow we make it depend on - # a different job (b_ubu_force_release) so the workspace it attaches contains a different executable. - <<: *t_ubu_soltest_all + <<: *base_ubuntu2404_large + parallelism: 50 + steps: + - soltest_all t_ubu_cli: &t_ubu_cli <<: *base_ubuntu2404_small From 41b20a7e64d56ccb8c0ad54062967d99bee613e6 Mon Sep 17 00:00:00 2001 From: Manuel Wedler Date: Mon, 22 Dec 2025 09:24:32 +0100 Subject: [PATCH 137/243] ci: Remove `--no-smt` flag from `t_ubu_soltest` job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kamil Śliwak --- .circleci/config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 557b08f8f3..2276350e04 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1300,7 +1300,6 @@ jobs: EVM: << pipeline.parameters.evm-version >> EOF_VERSION: 0 OPTIMIZE: 0 - SOLTEST_FLAGS: --no-smt steps: - soltest From 2b7dedf518d5221d566ca73d3322646267a2546c Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Tue, 6 Jan 2026 09:08:48 +0100 Subject: [PATCH 138/243] Add ccache to buildpack-deps images --- .circleci/osx_install_dependencies.sh | 1 + scripts/ci/docker_upgrade.sh | 1 + scripts/docker/buildpack-deps/Dockerfile.emscripten | 3 ++- scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz | 3 ++- scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 | 3 ++- scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.arm | 3 ++- scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang | 3 ++- 7 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh index dd5a8397b1..0666211b76 100755 --- a/.circleci/osx_install_dependencies.sh +++ b/.circleci/osx_install_dependencies.sh @@ -51,6 +51,7 @@ then brew update brew upgrade brew install cmake + brew install ccache brew install wget brew install coreutils brew install diffutils diff --git a/scripts/ci/docker_upgrade.sh b/scripts/ci/docker_upgrade.sh index c2077239a5..533982a375 100755 --- a/scripts/ci/docker_upgrade.sh +++ b/scripts/ci/docker_upgrade.sh @@ -55,6 +55,7 @@ docker run \ --rm \ --volume "${PWD}:/project" \ -u "$(id -u "${USER}"):$(id -g "${USER}")" \ + -e CCACHE_DIR=/tmp/ccache \ "${IMAGE_NAME}" \ bash -c "/project/scripts/ci/${IMAGE_NAME}_test_${IMAGE_VARIANT}.sh" diff --git a/scripts/docker/buildpack-deps/Dockerfile.emscripten b/scripts/docker/buildpack-deps/Dockerfile.emscripten index 007729d411..67c9ec61a1 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.emscripten +++ b/scripts/docker/buildpack-deps/Dockerfile.emscripten @@ -33,7 +33,7 @@ # Using $(em-config CACHE)/sysroot/usr seems to work, though, and still has cmake find the # dependencies automatically. FROM emscripten/emsdk:3.1.19 AS base -LABEL version="21" +LABEL version="22" ADD emscripten.jam /usr/src RUN <<-EOF @@ -41,6 +41,7 @@ RUN <<-EOF apt-get update apt-get install -qqy --no-install-recommends \ lsof \ + ccache \ lz4 \ python3 \ python3-pip \ diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz b/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz index 5a96e6889d..08c091cd77 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz @@ -22,7 +22,7 @@ # (c) 2016-2021 solidity contributors. #------------------------------------------------------------------------------ FROM gcr.io/oss-fuzz-base/base-builder AS base -LABEL version="12" +LABEL version="13" ARG DEBIAN_FRONTEND=noninteractive @@ -31,6 +31,7 @@ RUN apt-get update; \ automake \ bison \ build-essential \ + ccache \ curl \ git \ jq \ diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 index 9cf381f760..8f5c1aec84 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404 @@ -22,7 +22,7 @@ # (c) 2016-2024 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:noble AS base -LABEL version="6" +LABEL version="7" ARG DEBIAN_FRONTEND=noninteractive @@ -39,6 +39,7 @@ RUN set -ex; \ apt-get install -qqy --no-install-recommends \ build-essential \ cmake \ + ccache \ jq \ libboost-filesystem-dev \ libboost-program-options-dev \ diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.arm b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.arm index 663928220a..cc58ed6796 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.arm +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.arm @@ -22,7 +22,7 @@ # (c) 2016-2025 solidity contributors. #------------------------------------------------------------------------------ FROM --platform=linux/arm64 buildpack-deps:noble AS base -LABEL version="2" +LABEL version="3" ARG DEBIAN_FRONTEND=noninteractive @@ -40,6 +40,7 @@ RUN <<-EOF apt-get install --quiet=2 --no-install-recommends \ build-essential \ cmake \ + ccache \ jq \ libboost-filesystem-dev \ libboost-program-options-dev \ diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang index aec6fe6e26..9a99285140 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2404.clang @@ -22,7 +22,7 @@ # (c) 2016-2024 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:noble AS base -LABEL version="7" +LABEL version="8" ARG DEBIAN_FRONTEND=noninteractive @@ -40,6 +40,7 @@ RUN set -ex; \ build-essential \ clang \ cmake \ + ccache \ jq \ libboost-filesystem-dev \ libboost-program-options-dev \ From 009ea626be2df0724e5df876f2cc831e40c72651 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Tue, 6 Jan 2026 13:29:43 +0100 Subject: [PATCH 139/243] CI: Batched antlr check --- scripts/AntlrBatchTestRig.java | 104 +++++++++++++++++++++++++++++ scripts/test_antlr_grammar.sh | 118 ++++++++++++++------------------- 2 files changed, 155 insertions(+), 67 deletions(-) create mode 100644 scripts/AntlrBatchTestRig.java diff --git a/scripts/AntlrBatchTestRig.java b/scripts/AntlrBatchTestRig.java new file mode 100644 index 0000000000..91a924c335 --- /dev/null +++ b/scripts/AntlrBatchTestRig.java @@ -0,0 +1,104 @@ +import org.antlr.v4.runtime.*; +import java.io.*; +import java.nio.file.*; + +public class AntlrBatchTestRig { + public static void main(String[] args) throws Exception { + if (args.length < 1) { + System.err.println("Usage: AntlrBatchTestRig [file2] ..."); + System.exit(1); + } + + int exitCode = 0; + for (String filePath: args) { + try { + if (!testFile(filePath)) + exitCode = 1; + } catch (Exception e) { + System.err.println("ERROR:" + filePath + ":" + e.getMessage()); + exitCode = 1; + } + } + System.exit(exitCode); + } + + private static boolean testFile(String filePath) throws Exception { + // Detect mode based on file extension + String mode = filePath.endsWith(".yul") ? "yul" : "sol"; + String content = new String(Files.readAllBytes(Paths.get(filePath))); + + // Check if file expects parser error + boolean expectsError = content.contains("// ParserError"); + + CharStream input; + if (mode.equals("sol")) { + // Remove ExternalSource lines for Solidity files + content = content.replaceAll("(?m)^==== ExternalSource:.*$", ""); + input = CharStreams.fromString(content); + } else { + // Wrap Yul in assembly statement + input = CharStreams.fromString("assembly " + content); + } + + SolidityLexer lexer = new SolidityLexer(input); + CommonTokenStream tokens = new CommonTokenStream(lexer); + SolidityParser parser = new SolidityParser(tokens); + + // Remove default error listeners and add custom one + parser.removeErrorListeners(); + lexer.removeErrorListeners(); + + ErrorCollector errorCollector = new ErrorCollector(); + parser.addErrorListener(errorCollector); + lexer.addErrorListener(errorCollector); + + // Parse based on mode + if (mode.equals("sol")) { + parser.sourceUnit(); + } else { + parser.assemblyStatement(); + } + + boolean hasErrors = errorCollector.hasErrors(); + + // Output result + if (expectsError) { + if (hasErrors) { + System.out.println("PASS:" + filePath + ":FAILED_AS_EXPECTED"); + } else { + System.out.println("FAIL:" + filePath + ":SUCCEEDED_DESPITE_PARSER_ERROR"); + return false; + } + } else { + if (!hasErrors) { + System.out.println("PASS:" + filePath + ":OK"); + } else { + System.out.println("FAIL:" + filePath + ":" + errorCollector.getErrors()); + return false; + } + } + return true; + } + + static class ErrorCollector extends BaseErrorListener { + private StringBuilder errors = new StringBuilder(); + private boolean hasErrors = false; + + @Override + public void syntaxError(Recognizer recognizer, Object offendingSymbol, + int line, int charPositionInLine, + String msg, RecognitionException e) { + hasErrors = true; + errors.append("line ").append(line).append(":").append(charPositionInLine) + .append(" ").append(msg).append("\n"); + } + + public boolean hasErrors() { + return hasErrors; + } + + public String getErrors() { + return errors.toString(); + } + } +} diff --git a/scripts/test_antlr_grammar.sh b/scripts/test_antlr_grammar.sh index 02e9a634f9..c8e008c06d 100755 --- a/scripts/test_antlr_grammar.sh +++ b/scripts/test_antlr_grammar.sh @@ -10,6 +10,7 @@ ROOT_DIR=$(${READLINK} -f "$(dirname "$0")"/..) WORKDIR="${ROOT_DIR}/build/antlr" ANTLR_JAR="${ROOT_DIR}/build/deps/antlr4.jar" ANTLR_JAR_URI="https://www.antlr.org/download/antlr-4.8-complete.jar" +BATCH_SIZE=100 # Process 100 files per JVM invocation SGR_RESET="\033[0m" SGR_BOLD="\033[1m" @@ -17,9 +18,6 @@ SGR_GREEN="\033[32m" SGR_RED="\033[31m" SGR_BLUE="\033[34m" -vt_cursor_up() { echo -ne "\033[A"; } -vt_cursor_begin_of_line() { echo -ne "\r"; } - function download_antlr4 { if [[ ! -e "$ANTLR_JAR" ]] @@ -47,61 +45,10 @@ java -jar "${ANTLR_JAR}" SolidityParser.g4 SolidityLexer.g4 -o "${WORKDIR}/src/" # Compile lexer/parser sources javac -classpath "${ANTLR_JAR}" "${WORKDIR}/src/"*.java -d "${WORKDIR}/target/" -) -# Run tests -failed_count=0 -function test_file -{ - local SOL_FILE - SOL_FILE="$(${READLINK} -m "${1}")" - local cur=${2} - local max=${3} - local solOrYul=${4} - - echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ..." - local output - if [[ "${solOrYul}" == "sol" ]]; then - output=$( - grep -v "^==== ExternalSource:" "${SOL_FILE}" | java \ - -classpath "${ANTLR_JAR}:${WORKDIR}/target/" \ - "org.antlr.v4.gui.TestRig" \ - Solidity \ - sourceUnit 2>&1 - ) - else - output=$( - echo "assembly $(cat "${SOL_FILE}")" | java \ - -classpath "${ANTLR_JAR}:${WORKDIR}/target/" \ - "org.antlr.v4.gui.TestRig" \ - Solidity \ - assemblyStatement 2>&1 - ) - fi - vt_cursor_up - vt_cursor_begin_of_line - if grep -qE "^\/\/ ParserError" "${SOL_FILE}"; then - if [[ "${output}" != "" ]] - then - echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}FAILED AS EXPECTED${SGR_RESET}" - else - echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_RED}SUCCEEDED DESPITE PARSER ERROR${SGR_RESET}" - echo "${output}" - failed_count=$((failed_count + 1)) - exit 1 - fi - else - if [[ "${output}" == "" ]] - then - echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}OK${SGR_RESET}" - else - echo -e "${SGR_BLUE}[${cur}/${max}] Testing ${SOL_FILE}${SGR_RESET} ${SGR_BOLD}${SGR_RED}FAILED${SGR_RESET}" - echo "${output}" - failed_count=$((failed_count + 1)) - exit 1 - fi - fi -} +# Compile AntlrBatchTestRig +javac -classpath "${ANTLR_JAR}:${WORKDIR}/target/" "${ROOT_DIR}/scripts/AntlrBatchTestRig.java" -d "${WORKDIR}/target/" +) # we only want to use files that do not contain excluded parser errors, analysis errors or multi-source files. SOL_FILES=() @@ -154,17 +101,54 @@ done < <( "${ROOT_DIR}/test/libyul/yulOptimizerTests" ) -num_tests=$((${#SOL_FILES[*]} + ${#YUL_FILES[*]})) +# Combine all files into one array +ALL_FILES=("${SOL_FILES[@]}" "${YUL_FILES[@]}") +num_tests=${#ALL_FILES[@]} +failed_count=0 test_count=0 -for SOL_FILE in "${SOL_FILES[@]}" -do - test_count=$((test_count + 1)) - test_file "${SOL_FILE}" ${test_count} $num_tests "sol" -done -for YUL_FILE in "${YUL_FILES[@]}" -do - test_count=$((test_count + 1)) - test_file "${YUL_FILE}" ${test_count} $num_tests "yul" + +echo "Testing $num_tests files..." + +# Process all files in batches +for ((i=0; i<${#ALL_FILES[@]}; i+=BATCH_SIZE)); do + batch=("${ALL_FILES[@]:i:BATCH_SIZE}") + batch_size=${#batch[@]} + + echo "Processing batch $((i/BATCH_SIZE + 1)) (${batch_size} files)..." + + # Run batch + output=$(java -classpath "${ANTLR_JAR}:${WORKDIR}/target/" AntlrBatchTestRig "${batch[@]}" 2>&1) || true + + # Parse output + while IFS= read -r line; do + if [[ -z "$line" ]]; then + continue + fi + + test_count=$((test_count + 1)) + + if [[ "$line" =~ ^PASS:(.+):(.*) ]]; then + file="${BASH_REMATCH[1]}" + status="${BASH_REMATCH[2]}" + if [[ "$status" == "FAILED_AS_EXPECTED" ]]; then + echo -e "${SGR_BLUE}[${test_count}/${num_tests}] ${file}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}FAILED AS EXPECTED${SGR_RESET}" + else + echo -e "${SGR_BLUE}[${test_count}/${num_tests}] ${file}${SGR_RESET} ${SGR_BOLD}${SGR_GREEN}OK${SGR_RESET}" + fi + elif [[ "$line" =~ ^FAIL:(.+):(.*) ]]; then + file="${BASH_REMATCH[1]}" + error="${BASH_REMATCH[2]}" + echo -e "${SGR_BLUE}[${test_count}/${num_tests}] ${file}${SGR_RESET} ${SGR_BOLD}${SGR_RED}FAILED${SGR_RESET}" + echo "$error" + failed_count=$((failed_count + 1)) + elif [[ "$line" =~ ^ERROR:(.+):(.*) ]]; then + file="${BASH_REMATCH[1]}" + error="${BASH_REMATCH[2]}" + echo -e "${SGR_BLUE}[${test_count}/${num_tests}] ${file}${SGR_RESET} ${SGR_BOLD}${SGR_RED}ERROR${SGR_RESET}" + echo "$error" + failed_count=$((failed_count + 1)) + fi + done <<< "$output" done echo "Summary: ${failed_count} of $num_tests sources failed." From 98af07f1e46cff0eeadd134caf4f13f2bd31aed7 Mon Sep 17 00:00:00 2001 From: Franco Victorio Date: Fri, 5 Sep 2025 19:03:33 +0200 Subject: [PATCH 140/243] Add Slippy to the list of Solidity tools --- docs/resources.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/resources.rst b/docs/resources.rst index 3ba0620f95..52df87fdd5 100644 --- a/docs/resources.rst +++ b/docs/resources.rst @@ -133,6 +133,9 @@ Solidity Tools * `Scaffold-ETH 2 `_ Forkable Ethereum development stack focused on fast product iterations. +* `Slippy `_ + A simple and powerful linter for Solidity. + * `sol2uml `_ Unified Modeling Language (UML) class diagram generator for Solidity contracts. From fb900032cc341b3bd0aaad032232ea598741b778 Mon Sep 17 00:00:00 2001 From: Franco Victorio Date: Fri, 19 Dec 2025 10:53:12 +0000 Subject: [PATCH 141/243] Add 'slippy' to codespell's whitelist --- scripts/codespell_whitelist.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/codespell_whitelist.txt b/scripts/codespell_whitelist.txt index 7bcbf6ad14..e3a45b9472 100644 --- a/scripts/codespell_whitelist.txt +++ b/scripts/codespell_whitelist.txt @@ -2,3 +2,4 @@ nd compilability keypair wast +slippy From 1d6e22d2f03873a8333ebfc26d395f22b917170b Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Wed, 7 Jan 2026 10:55:07 +0100 Subject: [PATCH 142/243] Speed up CI builds with ccache (seeded from develop) --- .circleci/config.yml | 210 +++++++++++++++++++++++--- .circleci/osx_install_dependencies.sh | 1 + scripts/ci/build.sh | 16 +- scripts/ci/build_emscripten.sh | 12 ++ scripts/ci/build_ossfuzz.sh | 17 ++- scripts/ci/build_win.sh | 26 +++- scripts/install_deps.ps1 | 11 ++ 7 files changed, 265 insertions(+), 28 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2276350e04..1c141d6474 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,25 +13,25 @@ parameters: default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:1f387a77be889f65a2a25986a5c5eccc88cec23fabe6aeaf351790751145c81e" ubuntu-2404-docker-image: type: string - # ghcr.io/argotorg/solidity-buildpack-deps:ubuntu2404-6 - default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:c0412c53e59ce0c96bde4c08e7332ea12e4cadba9bbac829621947897fa21272" + # ghcr.io/argotorg/solidity-buildpack-deps:ubuntu2404-7 + default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:e52bd8fbb38908f203c6866e39562548faf26e5b7a5174f9860b44aae2b8f0cd" ubuntu-2404-arm-docker-image: type: string - # ghcr.io/argotorg/solidity-buildpack-deps:ubuntu2404.arm-2 - default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:27b03c1c4688e5d69b10e0539460346a3dee3b10b0e04fb406b9707c6f0dd95e" + # ghcr.io/argotorg/solidity-buildpack-deps:ubuntu2404.arm-3 + default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:1e6dbf9a9173f2645449281a1e11918a95ca6dc04e59aa9d70824cffc44697ed" ubuntu-2404-clang-docker-image: type: string - # ghcr.io/argotorg/solidity-buildpack-deps:ubuntu2404.clang-7 - default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:7466ed23590cf14e60da884894723bcdab064742f017dcfcce015999b4f9143b" + # ghcr.io/argotorg/solidity-buildpack-deps:ubuntu2404.clang-8 + default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:e283c8ec6a59c76565c89b856d8019af2fa5beb96f27d3064fc4dda42bf3524d" ubuntu-clang-ossfuzz-docker-image: type: string - # ghcr.io/argotorg/solidity-buildpack-deps:ubuntu.clang.ossfuzz-12 - default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:c7088af5082bf764244a6251ecf3dd29d280d2cd2cd071f011f7f32e0326f426" + # ghcr.io/argotorg/solidity-buildpack-deps:ubuntu.clang.ossfuzz-13 + default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:2acb5d6f254ece25a96096f26960b5fe114eb86ceca92312cd20d59c096bf6e4" emscripten-docker-image: type: string # NOTE: Please remember to update the `scripts/build_emscripten.sh` whenever the hash of this image changes. - # ghcr.io/argotorg/solidity-buildpack-deps:emscripten-21 - default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:fc53d68a4680ffa7d5f70164e13a903478964f15bcc07434d74833a05f4fbc19" + # ghcr.io/argotorg/solidity-buildpack-deps:emscripten-22 + default: "ghcr.io/argotorg/solidity-buildpack-deps@sha256:b9c953144d82cca5553f65626bc43b8af456dbc8966f8732ff9522f33ca8d722" evm-version: type: string default: osaka @@ -208,15 +208,98 @@ commands: run_build: steps: - - run: - name: Build - command: scripts/ci/build.sh + # TODO: remove the line below + # Before merging this PR we need to use an alternative develop branch to test the functionality, `ci-ccache` + + # On `ci-ccache` and `breaking` we intentionally do NOT restore any ccache, so every run starts from scratch. + # Tags always skip ccache. Other branches restore the most recent cache produced on `ci-ccache`. + - when: + condition: + and: + - equal: ["", << pipeline.git.tag >>] + - not: + or: + - equal: ["ci-ccache", << pipeline.git.branch >>] + - equal: ["breaking", << pipeline.git.branch >>] + steps: + - restore_cache: + keys: + # Reuse cache produced on ci-ccache (prefix match; restores the most recent). + - v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache- + # WARNING! If you edit anything between here and save_cache, remember to bump version or invalidate the cache manually. + - when: + condition: + and: + - equal: ["", << pipeline.git.tag >>] + steps: + - run: + name: Build (ccache) + command: CCACHE_ENABLED=1 scripts/ci/build.sh + - when: + condition: + or: + - not: + equal: ["", << pipeline.git.tag >>] + steps: + - run: + name: Build (no ccache) + command: scripts/ci/build.sh + + # On `ci-ccache` we always generate a fresh cache by never restoring and by saving under a unique key. + - when: + condition: + equal: ["ci-ccache", << pipeline.git.branch >>] + steps: + - save_cache: + key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache-{{ .Revision }} + paths: + - ~/.ccache run_build_ossfuzz: steps: - - run: - name: Build_ossfuzz - command: scripts/ci/build_ossfuzz.sh + # On `ci-ccache` and `breaking` we intentionally do NOT restore any ccache, so every run starts from scratch. + # Tags always skip ccache. Other branches restore the most recent cache produced on `ci-ccache`. + - when: + condition: + and: + - equal: ["", << pipeline.git.tag >>] + - not: + or: + - equal: ["ci-ccache", << pipeline.git.branch >>] + - equal: ["breaking", << pipeline.git.branch >>] + steps: + - restore_cache: + keys: + # Reuse cache produced on ci-ccache (prefix match; restores the most recent). + - v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache- + # WARNING! If you edit anything between here and save_cache, remember to bump version or invalidate the cache manually. + - when: + condition: + and: + - equal: ["", << pipeline.git.tag >>] + steps: + - run: + name: Build_ossfuzz (ccache) + command: CCACHE_ENABLED=1 scripts/ci/build_ossfuzz.sh + - when: + condition: + or: + - not: + equal: ["", << pipeline.git.tag >>] + steps: + - run: + name: Build_ossfuzz (no ccache) + command: scripts/ci/build_ossfuzz.sh + + # On `ci-ccache` we always generate a fresh cache by never restoring and by saving under a unique key. + - when: + condition: + equal: ["ci-ccache", << pipeline.git.branch >>] + steps: + - save_cache: + key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache-{{ .Revision }} + paths: + - ~/.ccache run_proofs: steps: @@ -1197,7 +1280,7 @@ jobs: - run: name: Install build dependencies command: | - pacman --noconfirm -Syu --noprogressbar --needed base-devel boost cmake git openssh tar + pacman --noconfirm -Syu --noprogressbar --needed base-devel boost cmake ccache git openssh tar - checkout - run_build - store_artifacts_solc @@ -1257,10 +1340,50 @@ jobs: MAKEFLAGS: -j 10 steps: - checkout - - run: - name: Build - command: | - scripts/ci/build_emscripten.sh + # On `ci-ccache` and `breaking` we intentionally do NOT restore any ccache, so every run starts from scratch. + # Tags always skip ccache. Other branches restore the most recent cache produced on `ci-ccache`. + - when: + condition: + and: + - equal: ["", << pipeline.git.tag >>] + - not: + or: + - equal: ["ci-ccache", << pipeline.git.branch >>] + - equal: ["breaking", << pipeline.git.branch >>] + steps: + - restore_cache: + keys: + # Reuse cache produced on ci-ccache (prefix match; restores the most recent). + - v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache- + # WARNING! If you edit anything between here and save_cache, remember to bump version or invalidate the cache manually. + - when: + condition: + and: + - equal: ["", << pipeline.git.tag >>] + steps: + - run: + name: Build (ccache) + command: CCACHE_ENABLED=1 scripts/ci/build_emscripten.sh + - when: + condition: + or: + - not: + equal: ["", << pipeline.git.tag >>] + steps: + - run: + name: Build (no ccache) + command: | + scripts/ci/build_emscripten.sh + + # On `ci-ccache` we always generate a fresh cache by never restoring and by saving under a unique key. + - when: + condition: + equal: ["ci-ccache", << pipeline.git.branch >>] + steps: + - save_cache: + key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache-{{ .Revision }} + paths: + - ~/.ccache - store_artifacts: path: upload/soljson.js destination: soljson.js @@ -1737,9 +1860,48 @@ jobs: key: dependencies-win-{{ arch }}-{{ checksum "scripts/install_deps.ps1" }} paths: - .\deps - - run: - name: "Building solidity" - command: scripts/ci/build_win.sh + # On `ci-ccache` and `breaking` we intentionally do NOT restore any ccache, so every run starts from scratch. + # Tags always skip ccache. Other branches restore the most recent cache produced on `ci-ccache`. + - when: + condition: + and: + - equal: ["", << pipeline.git.tag >>] + - not: + or: + - equal: ["ci-ccache", << pipeline.git.branch >>] + - equal: ["breaking", << pipeline.git.branch >>] + steps: + - restore_cache: + keys: + # Reuse cache produced on ci-ccache (prefix match; restores the most recent). + - v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache- + # WARNING! If you edit anything between here and save_cache, remember to bump version or invalidate the cache manually. + - when: + condition: + and: + - equal: ["", << pipeline.git.tag >>] + steps: + - run: + name: "Building solidity (ccache)" + command: CCACHE_ENABLED=1 scripts/ci/build_win.sh + - when: + condition: + or: + - not: + equal: ["", << pipeline.git.tag >>] + steps: + - run: + name: "Building solidity (no ccache)" + command: scripts/ci/build_win.sh + # On `ci-ccache` we always generate a fresh cache by never restoring and by saving under a unique key. + - when: + condition: + equal: ["ci-ccache", << pipeline.git.branch >>] + steps: + - save_cache: + key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache-{{ .Revision }} + paths: + - ~/.ccache - run: name: "Run solc.exe to make sure build was successful." command: .\build\solc\Release\solc.exe --version diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh index dd5a8397b1..0666211b76 100755 --- a/.circleci/osx_install_dependencies.sh +++ b/.circleci/osx_install_dependencies.sh @@ -51,6 +51,7 @@ then brew update brew upgrade brew install cmake + brew install ccache brew install wget brew install coreutils brew install diffutils diff --git a/scripts/ci/build.sh b/scripts/ci/build.sh index b7f7e92f57..769455903f 100755 --- a/scripts/ci/build.sh +++ b/scripts/ci/build.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -ex -ROOTDIR="$(dirname "$0")/../.." +ROOTDIR="$(realpath "$(dirname "$0")/../..")" # shellcheck source=scripts/common.sh source "${ROOTDIR}/scripts/common.sh" @@ -16,7 +16,21 @@ cd build [[ -n $COVERAGE && -z $CIRCLE_TAG ]] && CMAKE_OPTIONS="$CMAKE_OPTIONS -DCOVERAGE=ON" +if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then + export CCACHE_BASEDIR="$ROOTDIR" + export CCACHE_NOHASHDIR=1 + CMAKE_OPTIONS="${CMAKE_OPTIONS:-} -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache" +fi + # shellcheck disable=SC2086 cmake .. -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Release}" $CMAKE_OPTIONS +if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then + ccache -z +fi + cmake --build . + +if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then + ccache -s +fi diff --git a/scripts/ci/build_emscripten.sh b/scripts/ci/build_emscripten.sh index 50c851af84..bf19bad360 100755 --- a/scripts/ci/build_emscripten.sh +++ b/scripts/ci/build_emscripten.sh @@ -54,6 +54,15 @@ function build() { # TODO: This can be removed if and when all usages of `move()` in our codebase use the `std::` qualifier. CMAKE_CXX_FLAGS="-Wno-unqualified-std-cast-call" + if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then + export CCACHE_DIR="$HOME/.ccache" + CCACHE_BASEDIR="$(pwd)" + export CCACHE_BASEDIR + export CCACHE_NOHASHDIR=1 + mkdir -p "$CCACHE_DIR" + ccache -z + fi + mkdir -p "$build_dir" cd "$build_dir" emcmake cmake \ @@ -64,6 +73,9 @@ function build() { -DTESTS=0 \ .. make soljson + if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then + ccache -s + fi cd .. mkdir -p upload diff --git a/scripts/ci/build_ossfuzz.sh b/scripts/ci/build_ossfuzz.sh index 64d992994e..729b54d1d7 100755 --- a/scripts/ci/build_ossfuzz.sh +++ b/scripts/ci/build_ossfuzz.sh @@ -18,9 +18,22 @@ function generate_protobuf_bindings function build_fuzzers { cd "${BUILDDIR}" - cmake .. -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Release}" -DCCACHE=OFF \ - -DCMAKE_TOOLCHAIN_FILE="${ROOTDIR}"/cmake/toolchains/libfuzzer.cmake + if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then + export CCACHE_BASEDIR="$ROOTDIR" + export CCACHE_NOHASHDIR=1 + CMAKE_OPTIONS="${CMAKE_OPTIONS:-} -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache" + fi + # shellcheck disable=SC2086 + cmake .. -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Release}" \ + -DCMAKE_TOOLCHAIN_FILE="${ROOTDIR}"/cmake/toolchains/libfuzzer.cmake \ + $CMAKE_OPTIONS + if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then + ccache -z + fi make ossfuzz ossfuzz_proto ossfuzz_abiv2 -j 4 + if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then + ccache -s + fi } generate_protobuf_bindings diff --git a/scripts/ci/build_win.sh b/scripts/ci/build_win.sh index 64d63c58f4..8233f3aa61 100755 --- a/scripts/ci/build_win.sh +++ b/scripts/ci/build_win.sh @@ -10,18 +10,39 @@ CIRCLE_TAG="${CIRCLE_TAG:-}" cd "$ROOTDIR" "${ROOTDIR}/scripts/prerelease_suffix.sh" "$prerelease_source" "$CIRCLE_TAG" > prerelease.txt +if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then + export CCACHE_BASEDIR="$ROOTDIR" + export CCACHE_NOHASHDIR=1 + export CCACHE_COMPILERTYPE=msvc + # Hard-coded MSVC cl.exe path + CCACHE_COMPILER="C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.29.30133/bin/Hostx64/x64/cl.exe" + export CCACHE_COMPILER + PATH="$ROOTDIR/deps/ccache:$(dirname "$CCACHE_COMPILER"):$PATH" + export PATH + # Use a Windows-style path for Visual Studio globals. + WIN_PWD="$(pwd -W)" + # Visual Studio generator doesn't use compiler launchers; use ccache masquerade. + # See https://github.com/ccache/ccache/wiki/MS-Visual-Studio + CMAKE_OPTIONS="${CMAKE_OPTIONS:-} -DCMAKE_VS_GLOBALS=CLToolPath=${WIN_PWD}/deps/ccache;UseMultiToolTask=true" +fi + mkdir -p build/ cd build/ # NOTE: Using an array to force Bash to do wildcard expansion boost_dir=("${ROOTDIR}/deps/boost/lib/cmake/Boost-"*) -"${ROOTDIR}/deps/cmake/bin/cmake" \ + # shellcheck disable=SC2086 + "${ROOTDIR}/deps/cmake/bin/cmake" \ -G "Visual Studio 16 2019" \ -DBoost_DIR="${boost_dir[*]}" \ -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded \ -DCMAKE_INSTALL_PREFIX="${ROOTDIR}/uploads/" \ + ${CMAKE_OPTIONS:-} \ .. +if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then + ccache -z +fi MSBuild.exe solidity.sln \ -property:Configuration=Release \ -maxCpuCount:10 \ @@ -31,3 +52,6 @@ MSBuild.exe solidity.sln \ -j 10 \ --target install \ --config Release +if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then + ccache -s +fi diff --git a/scripts/install_deps.ps1 b/scripts/install_deps.ps1 index ad4f5bdadc..bf86735811 100644 --- a/scripts/install_deps.ps1 +++ b/scripts/install_deps.ps1 @@ -14,6 +14,17 @@ if ( -not (Test-Path "$PSScriptRoot\..\deps\boost") ) { mv cmake-3.27.4-windows-x86_64 "$PSScriptRoot\..\deps\cmake" Remove-Item cmake.zip + Invoke-WebRequest -URI "https://github.com/ccache/ccache/releases/download/v4.12.2/ccache-4.12.2-windows-x86_64.zip" -OutFile ccache.zip + if ((Get-FileHash ccache.zip).Hash -ne "82c1b130b25cc162531dc7a062dc5ea99349cd536bc9eba8a66d976802d66516") { + throw 'Downloaded ccache package has wrong checksum.' + } + tar -xf ccache.zip + mv ccache-4.12.2-windows-x86_64 "$PSScriptRoot\..\deps\ccache" + # ccache MSVC guide: https://github.com/ccache/ccache/wiki/MS-Visual-Studio + # Replace ccache.exe as cl.exe so MSBuild still sees "cl.exe" while ccache wraps the real compiler. + Copy-Item -Force "$PSScriptRoot\..\deps\ccache\ccache.exe" "$PSScriptRoot\..\deps\ccache\cl.exe" + Remove-Item ccache.zip + # FIXME: The default user agent results in Artifactory treating Invoke-WebRequest as a browser # and serving it a page that requires JavaScript. Invoke-WebRequest -URI "https://archives.boost.io/release/1.83.0/source/boost_1_83_0.zip" -OutFile boost.zip -UserAgent "" From d68a7cdf89808f68c0566b021fc12c4ab5816f80 Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Wed, 7 Jan 2026 11:39:08 +0100 Subject: [PATCH 143/243] Set CCACHE_DIR and create directory if CCACHE_ENABLED --- scripts/ci/build.sh | 2 ++ scripts/ci/build_ossfuzz.sh | 2 ++ scripts/ci/build_win.sh | 2 ++ 3 files changed, 6 insertions(+) diff --git a/scripts/ci/build.sh b/scripts/ci/build.sh index 769455903f..385d2d8989 100755 --- a/scripts/ci/build.sh +++ b/scripts/ci/build.sh @@ -17,9 +17,11 @@ cd build [[ -n $COVERAGE && -z $CIRCLE_TAG ]] && CMAKE_OPTIONS="$CMAKE_OPTIONS -DCOVERAGE=ON" if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then + export CCACHE_DIR="$HOME/.ccache" export CCACHE_BASEDIR="$ROOTDIR" export CCACHE_NOHASHDIR=1 CMAKE_OPTIONS="${CMAKE_OPTIONS:-} -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache" + mkdir -p "$CCACHE_DIR" fi # shellcheck disable=SC2086 diff --git a/scripts/ci/build_ossfuzz.sh b/scripts/ci/build_ossfuzz.sh index 729b54d1d7..883c939b89 100755 --- a/scripts/ci/build_ossfuzz.sh +++ b/scripts/ci/build_ossfuzz.sh @@ -19,9 +19,11 @@ function build_fuzzers { cd "${BUILDDIR}" if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then + export CCACHE_DIR="$HOME/.ccache" export CCACHE_BASEDIR="$ROOTDIR" export CCACHE_NOHASHDIR=1 CMAKE_OPTIONS="${CMAKE_OPTIONS:-} -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache" + mkdir -p "$CCACHE_DIR" fi # shellcheck disable=SC2086 cmake .. -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Release}" \ diff --git a/scripts/ci/build_win.sh b/scripts/ci/build_win.sh index 8233f3aa61..ea820705d2 100755 --- a/scripts/ci/build_win.sh +++ b/scripts/ci/build_win.sh @@ -11,6 +11,7 @@ cd "$ROOTDIR" "${ROOTDIR}/scripts/prerelease_suffix.sh" "$prerelease_source" "$CIRCLE_TAG" > prerelease.txt if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then + export CCACHE_DIR="$HOME/.ccache" export CCACHE_BASEDIR="$ROOTDIR" export CCACHE_NOHASHDIR=1 export CCACHE_COMPILERTYPE=msvc @@ -24,6 +25,7 @@ if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then # Visual Studio generator doesn't use compiler launchers; use ccache masquerade. # See https://github.com/ccache/ccache/wiki/MS-Visual-Studio CMAKE_OPTIONS="${CMAKE_OPTIONS:-} -DCMAKE_VS_GLOBALS=CLToolPath=${WIN_PWD}/deps/ccache;UseMultiToolTask=true" + mkdir -p "$CCACHE_DIR" fi mkdir -p build/ From 2243156ebd88193bff483fa6b5495d474948a46f Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Thu, 8 Jan 2026 14:22:56 +0100 Subject: [PATCH 144/243] enforce CCACHE_KEY and fix Windows ccache CMake globals --- .circleci/config.yml | 55 ++++++++++++++++++++++++++++++++--------- scripts/ci/build_win.sh | 5 +++- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1c141d6474..8d9081beba 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -75,6 +75,17 @@ commands: event: release condition: on_success + ensure_ccache_key: + description: "Fail fast when CCACHE_KEY is missing." + steps: + - run: + name: Check CCACHE_KEY + command: | + if [ -z "${CCACHE_KEY:-}" ]; then + echo "CCACHE_KEY is not set. Please set it in CircleCI environment settings." + exit 1 + fi + prepare_bytecode_report: description: "Generate bytecode report and upload it as an artifact." parameters: @@ -208,6 +219,11 @@ commands: run_build: steps: + - when: + condition: + equal: ["", << pipeline.git.tag >>] + steps: + - ensure_ccache_key # TODO: remove the line below # Before merging this PR we need to use an alternative develop branch to test the functionality, `ci-ccache` @@ -226,7 +242,7 @@ commands: keys: # Reuse cache produced on ci-ccache (prefix match; restores the most recent). - v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache- - # WARNING! If you edit anything between here and save_cache, remember to bump version or invalidate the cache manually. + # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings - when: condition: and: @@ -234,7 +250,7 @@ commands: steps: - run: name: Build (ccache) - command: CCACHE_ENABLED=1 scripts/ci/build.sh + command: CCACHE_ENABLED=${CCACHE_ENABLED:-1} scripts/ci/build.sh - when: condition: or: @@ -251,12 +267,17 @@ commands: equal: ["ci-ccache", << pipeline.git.branch >>] steps: - save_cache: - key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache-{{ .Revision }} + key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache paths: - ~/.ccache run_build_ossfuzz: steps: + - when: + condition: + equal: ["", << pipeline.git.tag >>] + steps: + - ensure_ccache_key # On `ci-ccache` and `breaking` we intentionally do NOT restore any ccache, so every run starts from scratch. # Tags always skip ccache. Other branches restore the most recent cache produced on `ci-ccache`. - when: @@ -272,7 +293,7 @@ commands: keys: # Reuse cache produced on ci-ccache (prefix match; restores the most recent). - v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache- - # WARNING! If you edit anything between here and save_cache, remember to bump version or invalidate the cache manually. + # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings - when: condition: and: @@ -280,7 +301,7 @@ commands: steps: - run: name: Build_ossfuzz (ccache) - command: CCACHE_ENABLED=1 scripts/ci/build_ossfuzz.sh + command: CCACHE_ENABLED=${CCACHE_ENABLED:-1} scripts/ci/build_ossfuzz.sh - when: condition: or: @@ -297,7 +318,7 @@ commands: equal: ["ci-ccache", << pipeline.git.branch >>] steps: - save_cache: - key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache-{{ .Revision }} + key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache paths: - ~/.ccache @@ -1340,6 +1361,11 @@ jobs: MAKEFLAGS: -j 10 steps: - checkout + - when: + condition: + equal: ["", << pipeline.git.tag >>] + steps: + - ensure_ccache_key # On `ci-ccache` and `breaking` we intentionally do NOT restore any ccache, so every run starts from scratch. # Tags always skip ccache. Other branches restore the most recent cache produced on `ci-ccache`. - when: @@ -1355,7 +1381,7 @@ jobs: keys: # Reuse cache produced on ci-ccache (prefix match; restores the most recent). - v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache- - # WARNING! If you edit anything between here and save_cache, remember to bump version or invalidate the cache manually. + # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings - when: condition: and: @@ -1363,7 +1389,7 @@ jobs: steps: - run: name: Build (ccache) - command: CCACHE_ENABLED=1 scripts/ci/build_emscripten.sh + command: CCACHE_ENABLED=${CCACHE_ENABLED:-1} scripts/ci/build_emscripten.sh - when: condition: or: @@ -1381,7 +1407,7 @@ jobs: equal: ["ci-ccache", << pipeline.git.branch >>] steps: - save_cache: - key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache-{{ .Revision }} + key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache paths: - ~/.ccache - store_artifacts: @@ -1860,6 +1886,11 @@ jobs: key: dependencies-win-{{ arch }}-{{ checksum "scripts/install_deps.ps1" }} paths: - .\deps + - when: + condition: + equal: ["", << pipeline.git.tag >>] + steps: + - ensure_ccache_key # On `ci-ccache` and `breaking` we intentionally do NOT restore any ccache, so every run starts from scratch. # Tags always skip ccache. Other branches restore the most recent cache produced on `ci-ccache`. - when: @@ -1875,7 +1906,7 @@ jobs: keys: # Reuse cache produced on ci-ccache (prefix match; restores the most recent). - v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache- - # WARNING! If you edit anything between here and save_cache, remember to bump version or invalidate the cache manually. + # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings - when: condition: and: @@ -1883,7 +1914,7 @@ jobs: steps: - run: name: "Building solidity (ccache)" - command: CCACHE_ENABLED=1 scripts/ci/build_win.sh + command: CCACHE_ENABLED=${CCACHE_ENABLED:-1} scripts/ci/build_win.sh - when: condition: or: @@ -1899,7 +1930,7 @@ jobs: equal: ["ci-ccache", << pipeline.git.branch >>] steps: - save_cache: - key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache-{{ .Revision }} + key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache paths: - ~/.ccache - run: diff --git a/scripts/ci/build_win.sh b/scripts/ci/build_win.sh index ea820705d2..ee64243f47 100755 --- a/scripts/ci/build_win.sh +++ b/scripts/ci/build_win.sh @@ -6,6 +6,7 @@ prerelease_source="${1:-ci}" ROOTDIR="$(realpath "$(dirname "$0")/../../")" FORCE_RELEASE="${FORCE_RELEASE:-}" CIRCLE_TAG="${CIRCLE_TAG:-}" +CMAKE_VS_GLOBALS_ARG=() cd "$ROOTDIR" "${ROOTDIR}/scripts/prerelease_suffix.sh" "$prerelease_source" "$CIRCLE_TAG" > prerelease.txt @@ -24,7 +25,8 @@ if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then WIN_PWD="$(pwd -W)" # Visual Studio generator doesn't use compiler launchers; use ccache masquerade. # See https://github.com/ccache/ccache/wiki/MS-Visual-Studio - CMAKE_OPTIONS="${CMAKE_OPTIONS:-} -DCMAKE_VS_GLOBALS=CLToolPath=${WIN_PWD}/deps/ccache;UseMultiToolTask=true" + CMAKE_VS_GLOBALS="CLToolPath=${WIN_PWD}/deps/ccache;UseMultiToolTask=true" + CMAKE_VS_GLOBALS_ARG=(-DCMAKE_VS_GLOBALS="$CMAKE_VS_GLOBALS") mkdir -p "$CCACHE_DIR" fi @@ -40,6 +42,7 @@ boost_dir=("${ROOTDIR}/deps/boost/lib/cmake/Boost-"*) -DBoost_DIR="${boost_dir[*]}" \ -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded \ -DCMAKE_INSTALL_PREFIX="${ROOTDIR}/uploads/" \ + "${CMAKE_VS_GLOBALS_ARG[@]}" \ ${CMAKE_OPTIONS:-} \ .. if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then From e1e2ebe86da6502823b3eb9576ceeaf72c4ef137 Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Thu, 8 Jan 2026 15:07:35 +0100 Subject: [PATCH 145/243] Programmatically find the path of cl.exe --- scripts/ci/build_win.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/ci/build_win.sh b/scripts/ci/build_win.sh index ee64243f47..5a3baf3277 100755 --- a/scripts/ci/build_win.sh +++ b/scripts/ci/build_win.sh @@ -16,8 +16,12 @@ if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then export CCACHE_BASEDIR="$ROOTDIR" export CCACHE_NOHASHDIR=1 export CCACHE_COMPILERTYPE=msvc - # Hard-coded MSVC cl.exe path - CCACHE_COMPILER="C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.29.30133/bin/Hostx64/x64/cl.exe" + VSWHERE="C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe" + CCACHE_COMPILER="$("$VSWHERE" -latest -products "*" \ + -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 \ + -version "[16.0,17.0)" \ + -find "VC/Tools/MSVC/*/bin/Hostx64/x64/cl.exe" | sort -V | tail -n 1 | tr -d '\r')" + CCACHE_COMPILER="${CCACHE_COMPILER//\\//}" export CCACHE_COMPILER PATH="$ROOTDIR/deps/ccache:$(dirname "$CCACHE_COMPILER"):$PATH" export PATH From c2bf44142ab3057e1a731843ae0aef149e1f9c59 Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Thu, 8 Jan 2026 15:17:56 +0100 Subject: [PATCH 146/243] Remove the version filter in the command to find cl.exe --- scripts/ci/build_win.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/ci/build_win.sh b/scripts/ci/build_win.sh index 5a3baf3277..01c062ec5e 100755 --- a/scripts/ci/build_win.sh +++ b/scripts/ci/build_win.sh @@ -19,7 +19,6 @@ if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then VSWHERE="C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe" CCACHE_COMPILER="$("$VSWHERE" -latest -products "*" \ -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 \ - -version "[16.0,17.0)" \ -find "VC/Tools/MSVC/*/bin/Hostx64/x64/cl.exe" | sort -V | tail -n 1 | tr -d '\r')" CCACHE_COMPILER="${CCACHE_COMPILER//\\//}" export CCACHE_COMPILER From 9b8d22abb56ad379dfedea64f751cde3185f11aa Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Thu, 8 Jan 2026 15:24:56 +0100 Subject: [PATCH 147/243] Fix vswhere command --- scripts/ci/build_win.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/build_win.sh b/scripts/ci/build_win.sh index 01c062ec5e..b880519851 100755 --- a/scripts/ci/build_win.sh +++ b/scripts/ci/build_win.sh @@ -19,7 +19,7 @@ if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then VSWHERE="C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe" CCACHE_COMPILER="$("$VSWHERE" -latest -products "*" \ -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 \ - -find "VC/Tools/MSVC/*/bin/Hostx64/x64/cl.exe" | sort -V | tail -n 1 | tr -d '\r')" + -find "VC/Tools/MSVC/*/bin/Hostx64/x64/cl.exe" | head -n 1 | tr -d '\r')" CCACHE_COMPILER="${CCACHE_COMPILER//\\//}" export CCACHE_COMPILER PATH="$ROOTDIR/deps/ccache:$(dirname "$CCACHE_COMPILER"):$PATH" From 4bb922571121a97421d5470cfa2d6286ea311f6e Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Thu, 8 Jan 2026 15:39:31 +0100 Subject: [PATCH 148/243] Fix CCACHE_COMPILER extraction --- scripts/ci/build_win.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/ci/build_win.sh b/scripts/ci/build_win.sh index b880519851..07604c8eb8 100755 --- a/scripts/ci/build_win.sh +++ b/scripts/ci/build_win.sh @@ -16,10 +16,12 @@ if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then export CCACHE_BASEDIR="$ROOTDIR" export CCACHE_NOHASHDIR=1 export CCACHE_COMPILERTYPE=msvc - VSWHERE="C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe" - CCACHE_COMPILER="$("$VSWHERE" -latest -products "*" \ + VSWHERE="${VSWHERE:-C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe}" + VS_INSTALL="$("$VSWHERE" -latest -products "*" \ -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 \ - -find "VC/Tools/MSVC/*/bin/Hostx64/x64/cl.exe" | head -n 1 | tr -d '\r')" + -property installationPath | tr -d '\r')" + VCTOOLS_VERSION="$(tr -d '\r' < "$VS_INSTALL/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt")" + CCACHE_COMPILER="$VS_INSTALL/VC/Tools/MSVC/$VCTOOLS_VERSION/bin/Hostx64/x64/cl.exe" CCACHE_COMPILER="${CCACHE_COMPILER//\\//}" export CCACHE_COMPILER PATH="$ROOTDIR/deps/ccache:$(dirname "$CCACHE_COMPILER"):$PATH" From b6d0d788d49cf28ca14162893d03adac3fcb5f9b Mon Sep 17 00:00:00 2001 From: 0xcharry Date: Sun, 11 Jan 2026 16:55:23 +0100 Subject: [PATCH 149/243] Update JSON.cpp --- libsolutil/JSON.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libsolutil/JSON.cpp b/libsolutil/JSON.cpp index 548fdfe0b8..8633a44c4d 100644 --- a/libsolutil/JSON.cpp +++ b/libsolutil/JSON.cpp @@ -149,7 +149,6 @@ bool jsonParseStrict(std::string const& _input, Json& _json, std::string* _errs /* allow exceptions */ true, /* ignore_comments */true ); - _errs = {}; return true; } catch (Json::parse_error const& e) From 95caef252520fa52bfce38e69052da0945132588 Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Mon, 12 Jan 2026 09:24:16 +0100 Subject: [PATCH 150/243] Refactor config.yml to re-use ccache restore and save commands --- .circleci/config.yml | 135 +++++++++++++++---------------------------- 1 file changed, 45 insertions(+), 90 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8d9081beba..de63a363fa 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -86,6 +86,44 @@ commands: exit 1 fi + restore_ccache_if_allowed: + description: "Restore ccache unless on a tag or a skipped branch." + parameters: + # TODO: remove the line below + # Before merging this PR we need to use an alternative develop branch to test the functionality, `ci-ccache` + skip_branches_regex: + description: "Regex of branches that should skip restoring the cache." + type: string + default: "^(ci-ccache|breaking)$" + steps: + # Tags always skip ccache. Selected branches intentionally start from scratch. + - when: + condition: + and: + - equal: ["", << pipeline.git.tag >>] + - not: + matches: + pattern: "<< parameters.skip_branches_regex >>" + value: << pipeline.git.branch >> + steps: + - restore_cache: + keys: + # Reuse cache produced on ci-ccache (prefix match; restores the most recent). + - v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache- + + save_ccache_if_develop: + description: "Save a fresh ccache only on the ci-ccache branch." + steps: + # On `ci-ccache` we always generate a fresh cache by never restoring and by saving under a unique key. + - when: + condition: + equal: ["ci-ccache", << pipeline.git.branch >>] + steps: + - save_cache: + key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache + paths: + - ~/.ccache + prepare_bytecode_report: description: "Generate bytecode report and upload it as an artifact." parameters: @@ -224,24 +262,7 @@ commands: equal: ["", << pipeline.git.tag >>] steps: - ensure_ccache_key - # TODO: remove the line below - # Before merging this PR we need to use an alternative develop branch to test the functionality, `ci-ccache` - - # On `ci-ccache` and `breaking` we intentionally do NOT restore any ccache, so every run starts from scratch. - # Tags always skip ccache. Other branches restore the most recent cache produced on `ci-ccache`. - - when: - condition: - and: - - equal: ["", << pipeline.git.tag >>] - - not: - or: - - equal: ["ci-ccache", << pipeline.git.branch >>] - - equal: ["breaking", << pipeline.git.branch >>] - steps: - - restore_cache: - keys: - # Reuse cache produced on ci-ccache (prefix match; restores the most recent). - - v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache- + - restore_ccache_if_allowed # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings - when: condition: @@ -261,15 +282,7 @@ commands: name: Build (no ccache) command: scripts/ci/build.sh - # On `ci-ccache` we always generate a fresh cache by never restoring and by saving under a unique key. - - when: - condition: - equal: ["ci-ccache", << pipeline.git.branch >>] - steps: - - save_cache: - key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache - paths: - - ~/.ccache + - save_ccache_if_develop run_build_ossfuzz: steps: @@ -278,21 +291,7 @@ commands: equal: ["", << pipeline.git.tag >>] steps: - ensure_ccache_key - # On `ci-ccache` and `breaking` we intentionally do NOT restore any ccache, so every run starts from scratch. - # Tags always skip ccache. Other branches restore the most recent cache produced on `ci-ccache`. - - when: - condition: - and: - - equal: ["", << pipeline.git.tag >>] - - not: - or: - - equal: ["ci-ccache", << pipeline.git.branch >>] - - equal: ["breaking", << pipeline.git.branch >>] - steps: - - restore_cache: - keys: - # Reuse cache produced on ci-ccache (prefix match; restores the most recent). - - v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache- + - restore_ccache_if_allowed # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings - when: condition: @@ -312,15 +311,7 @@ commands: name: Build_ossfuzz (no ccache) command: scripts/ci/build_ossfuzz.sh - # On `ci-ccache` we always generate a fresh cache by never restoring and by saving under a unique key. - - when: - condition: - equal: ["ci-ccache", << pipeline.git.branch >>] - steps: - - save_cache: - key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache - paths: - - ~/.ccache + - save_ccache_if_develop run_proofs: steps: @@ -1366,21 +1357,7 @@ jobs: equal: ["", << pipeline.git.tag >>] steps: - ensure_ccache_key - # On `ci-ccache` and `breaking` we intentionally do NOT restore any ccache, so every run starts from scratch. - # Tags always skip ccache. Other branches restore the most recent cache produced on `ci-ccache`. - - when: - condition: - and: - - equal: ["", << pipeline.git.tag >>] - - not: - or: - - equal: ["ci-ccache", << pipeline.git.branch >>] - - equal: ["breaking", << pipeline.git.branch >>] - steps: - - restore_cache: - keys: - # Reuse cache produced on ci-ccache (prefix match; restores the most recent). - - v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache- + - restore_ccache_if_allowed # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings - when: condition: @@ -1401,15 +1378,7 @@ jobs: command: | scripts/ci/build_emscripten.sh - # On `ci-ccache` we always generate a fresh cache by never restoring and by saving under a unique key. - - when: - condition: - equal: ["ci-ccache", << pipeline.git.branch >>] - steps: - - save_cache: - key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache - paths: - - ~/.ccache + - save_ccache_if_develop - store_artifacts: path: upload/soljson.js destination: soljson.js @@ -1891,21 +1860,7 @@ jobs: equal: ["", << pipeline.git.tag >>] steps: - ensure_ccache_key - # On `ci-ccache` and `breaking` we intentionally do NOT restore any ccache, so every run starts from scratch. - # Tags always skip ccache. Other branches restore the most recent cache produced on `ci-ccache`. - - when: - condition: - and: - - equal: ["", << pipeline.git.tag >>] - - not: - or: - - equal: ["ci-ccache", << pipeline.git.branch >>] - - equal: ["breaking", << pipeline.git.branch >>] - steps: - - restore_cache: - keys: - # Reuse cache produced on ci-ccache (prefix match; restores the most recent). - - v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache- + - restore_ccache_if_allowed # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings - when: condition: From bd615db148d3e3a2b76a52af0202043e1c06346d Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Mon, 12 Jan 2026 11:27:04 +0100 Subject: [PATCH 151/243] Add revision to the cache key to avoid skipping cache generation for duplicate key --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index de63a363fa..1b6a309fc1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -120,7 +120,7 @@ commands: equal: ["ci-ccache", << pipeline.git.branch >>] steps: - save_cache: - key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache + key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache-{{ .Revision }} paths: - ~/.ccache From be1df0710601fda269610953b75c7bf92684f965 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Wed, 14 Jan 2026 00:54:35 +0100 Subject: [PATCH 152/243] CLI Parser: Use defaulted comparison operators for options --- solc/CommandLineParser.cpp | 57 -------------------------------------- solc/CommandLineParser.h | 57 ++++++++++++++++++++++++++++---------- 2 files changed, 42 insertions(+), 72 deletions(-) diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 50ce02bd14..534eb57d10 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -157,14 +157,6 @@ void CommandLineParser::checkMutuallyExclusive(std::vector const& _ } } -bool CompilerOutputs::operator==(CompilerOutputs const& _other) const noexcept -{ - for (bool CompilerOutputs::* member: componentMap() | ranges::views::values) - if (this->*member != _other.*member) - return false; - return true; -} - std::ostream& operator<<(std::ostream& _out, CompilerOutputs const& _selection) { std::vector serializedSelection; @@ -187,15 +179,6 @@ std::string const& CompilerOutputs::componentName(bool CompilerOutputs::* _compo solAssert(false, ""); } -bool CombinedJsonRequests::operator==(CombinedJsonRequests const& _other) const noexcept -{ - for (bool CombinedJsonRequests::* member: componentMap() | ranges::views::values) - if (this->*member != _other.*member) - return false; - return true; -} - - std::ostream& operator<<(std::ostream& _out, CombinedJsonRequests const& _requests) { std::vector serializedRequests; @@ -217,46 +200,6 @@ std::string const& CombinedJsonRequests::componentName(bool CombinedJsonRequests solAssert(false, ""); } -bool CommandLineOptions::operator==(CommandLineOptions const& _other) const noexcept -{ - return - input.paths == _other.input.paths && - input.remappings == _other.input.remappings && - input.addStdin == _other.input.addStdin && - input.basePath == _other.input.basePath && - input.includePaths == _other.input.includePaths && - input.allowedDirectories == _other.input.allowedDirectories && - input.ignoreMissingFiles == _other.input.ignoreMissingFiles && - input.noImportCallback == _other.input.noImportCallback && - output.dir == _other.output.dir && - output.overwriteFiles == _other.output.overwriteFiles && - output.evmVersion == _other.output.evmVersion && - output.viaIR == _other.output.viaIR && - output.revertStrings == _other.output.revertStrings && - output.debugInfoSelection == _other.output.debugInfoSelection && - output.stopAfter == _other.output.stopAfter && - output.eofVersion == _other.output.eofVersion && - input.mode == _other.input.mode && - assembly.targetMachine == _other.assembly.targetMachine && - assembly.inputLanguage == _other.assembly.inputLanguage && - linker.libraries == _other.linker.libraries && - formatting.json == _other.formatting.json && - formatting.coloredOutput == _other.formatting.coloredOutput && - formatting.withErrorIds == _other.formatting.withErrorIds && - compiler.outputs == _other.compiler.outputs && - compiler.estimateGas == _other.compiler.estimateGas && - compiler.combinedJsonRequests == _other.compiler.combinedJsonRequests && - metadata.format == _other.metadata.format && - metadata.hash == _other.metadata.hash && - metadata.literalSources == _other.metadata.literalSources && - optimizer.optimizeEvmasm == _other.optimizer.optimizeEvmasm && - optimizer.optimizeYul == _other.optimizer.optimizeYul && - optimizer.expectedExecutionsPerDeployment == _other.optimizer.expectedExecutionsPerDeployment && - optimizer.yulSteps == _other.optimizer.yulSteps && - modelChecker.initialize == _other.modelChecker.initialize && - modelChecker.settings == _other.modelChecker.settings; -} - OptimiserSettings CommandLineOptions::optimiserSettings() const { OptimiserSettings settings; diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index d8141cb4fc..46743232b4 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -62,8 +62,8 @@ enum class InputMode struct CompilerOutputs { - bool operator!=(CompilerOutputs const& _other) const noexcept { return !(*this == _other); } - bool operator==(CompilerOutputs const& _other) const noexcept; + bool operator!=(CompilerOutputs const& _other) const noexcept = default; + bool operator==(CompilerOutputs const& _other) const noexcept = default; friend std::ostream& operator<<(std::ostream& _out, CompilerOutputs const& _requests); static std::string const& componentName(bool CompilerOutputs::* _component); @@ -118,8 +118,8 @@ struct CompilerOutputs struct CombinedJsonRequests { - bool operator!=(CombinedJsonRequests const& _other) const noexcept { return !(*this == _other); } - bool operator==(CombinedJsonRequests const& _other) const noexcept; + bool operator!=(CombinedJsonRequests const& _other) const noexcept = default; + bool operator==(CombinedJsonRequests const& _other) const noexcept = default; friend std::ostream& operator<<(std::ostream& _out, CombinedJsonRequests const& _requests); static std::string const& componentName(bool CombinedJsonRequests::* _component); @@ -170,13 +170,16 @@ struct CombinedJsonRequests struct CommandLineOptions { - bool operator==(CommandLineOptions const& _other) const noexcept; - bool operator!=(CommandLineOptions const& _other) const noexcept { return !(*this == _other); } + bool operator==(CommandLineOptions const& _other) const noexcept = default; + bool operator!=(CommandLineOptions const& _other) const noexcept = default; OptimiserSettings optimiserSettings() const; - struct + struct Input { + bool operator==(Input const&) const noexcept = default; + bool operator!=(Input const&) const noexcept = default; + InputMode mode = InputMode::Compiler; std::set paths; std::vector remappings; @@ -188,8 +191,11 @@ struct CommandLineOptions bool noImportCallback = false; } input; - struct + struct Output { + bool operator==(Output const&) const noexcept = default; + bool operator!=(Output const&) const noexcept = default; + boost::filesystem::path dir; bool overwriteFiles = false; langutil::EVMVersion evmVersion; @@ -200,48 +206,69 @@ struct CommandLineOptions std::optional eofVersion; } output; - struct + struct Assembly { + bool operator==(Assembly const&) const noexcept = default; + bool operator!=(Assembly const&) const noexcept = default; + yul::YulStack::Machine targetMachine = yul::YulStack::Machine::EVM; yul::YulStack::Language inputLanguage = yul::YulStack::Language::StrictAssembly; } assembly; - struct + struct Linker { + bool operator==(Linker const&) const noexcept = default; + bool operator!=(Linker const&) const noexcept = default; + std::map libraries; // library name -> address } linker; - struct + struct Formatting { + bool operator==(Formatting const&) const noexcept = default; + bool operator!=(Formatting const&) const noexcept = default; + util::JsonFormat json; std::optional coloredOutput; bool withErrorIds = false; } formatting; - struct + struct Compiler { + bool operator==(Compiler const&) const noexcept = default; + bool operator!=(Compiler const&) const noexcept = default; + CompilerOutputs outputs; bool estimateGas = false; std::optional combinedJsonRequests; } compiler; - struct + struct Metadata { + bool operator==(Metadata const&) const noexcept = default; + bool operator!=(Metadata const&) const noexcept = default; + CompilerStack::MetadataFormat format = CompilerStack::defaultMetadataFormat(); CompilerStack::MetadataHash hash = CompilerStack::MetadataHash::IPFS; bool literalSources = false; } metadata; - struct + struct Optimizer { + bool operator==(Optimizer const&) const noexcept = default; + bool operator!=(Optimizer const&) const noexcept = default; + bool optimizeEvmasm = false; bool optimizeYul = false; std::optional expectedExecutionsPerDeployment; std::optional yulSteps; } optimizer; - struct + struct ModelChecker { + bool operator==(ModelChecker const&) const noexcept = default; + bool operator!=(ModelChecker const&) const noexcept = default; + bool initialize = false; ModelCheckerSettings settings; } modelChecker; From 6d538dd2056cd6dc9b8abfe15ebda1e1b5301f6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 20 Dec 2025 01:05:26 +0100 Subject: [PATCH 153/243] ReleaseChecklist: Split off Comms and Documentation sections from Post-release --- ReleaseChecklist.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 9c3b2f8968..f56ec55fef 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -89,12 +89,13 @@ At least a day before the release: Inspect the tarball to ensure that it contains an up-to-date compiler binary (`soljson.js`). - [ ] Run `npm publish solc-x.y.z.tgz` to publish the newly created tarball. -### Post-release +### Documentation - [ ] Make sure the documentation for the new release has been published successfully. Go to the [documentation status page at ReadTheDocs](https://readthedocs.org/projects/solidity/) and verify that the new version is listed, works and is marked as default. - [ ] Remove "still in progress" warning from the [release notes](https://github.com/argotorg/solidity/releases). + +### Comms - [ ] Merge the [blog posts](https://github.com/argotorg/solidity-website/pulls) related to the release. -- [ ] Create a commit to increase the version number on `develop` in `CMakeLists.txt` and add a new skeleton changelog entry. - [ ] Update the release information section [in the source of soliditylang.org](https://github.com/argotorg/solidity-website/blob/main/src/pages/index.tsx). - [ ] Announce on [Twitter](https://twitter.com/solidity_lang), including links to the release and the blog post. - [ ] Announce on [Fosstodon](https://fosstodon.org/@solidity/), including links to the release and the blog post. @@ -102,6 +103,9 @@ At least a day before the release: - [ ] Share the announcement on the [Solidity forum](https://forum.soliditylang.org) in the `Announcements` category. - [ ] Share the announcement on [`#solidity` channel on Matrix](https://matrix.to/#/#ethereum_solidity:gitter.im). - [ ] Share the announcement on [`#solc-tooling`](https://matrix.to/#/#solc-tooling:matrix.org). + +### Post-release +- [ ] Create a commit to increase the version number on `develop` in `CMakeLists.txt` and add a new skeleton changelog entry. - [ ] If anything went wrong this time, mention it in [Learning from Past Releases](https://notes.argot.org/@solidity-release-mistakes). - [ ] Bump vendored dependencies. - [ ] Lean back, wait for bug reports and repeat from step 1 :). From 5e28f2a5a1ec43f2b6951971f51398500317de90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 20 Dec 2025 01:10:28 +0100 Subject: [PATCH 154/243] ReleaseChecklist: New step to verify links in release notes --- ReleaseChecklist.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index f56ec55fef..637f8dc8a2 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -97,6 +97,7 @@ At least a day before the release: ### Comms - [ ] Merge the [blog posts](https://github.com/argotorg/solidity-website/pulls) related to the release. - [ ] Update the release information section [in the source of soliditylang.org](https://github.com/argotorg/solidity-website/blob/main/src/pages/index.tsx). +- [ ] Verify that the link to the blog post in [release notes](https://github.com/argotorg/solidity/releases) is not broken. - [ ] Announce on [Twitter](https://twitter.com/solidity_lang), including links to the release and the blog post. - [ ] Announce on [Fosstodon](https://fosstodon.org/@solidity/), including links to the release and the blog post. - [ ] Share the announcement on Reddit in [`/r/ethdev`](https://reddit.com/r/ethdev/), cross-posted to [`/r/ethereum`](https://reddit.com/r/ethereum/). From 4629368a8df56b84ca3b03ade67446b8e1634e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 20 Dec 2025 01:17:09 +0100 Subject: [PATCH 155/243] ReleaseChecklist: Emphasize that blog/website drafts should be created ahead of time and only merged after release --- ReleaseChecklist.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 637f8dc8a2..d9f6b84a80 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -36,10 +36,11 @@ At least a day before the release: - [ ] Create drafts of blog posts. - [ ] Prepare drafts of Twitter, Reddit and Solidity Forum announcements. -### Blog Post +### Website/Blog Updates - [ ] Create a post on [solidity-website](https://github.com/argotorg/solidity-website/tree/main/src/posts) in the `Releases` category and explain some of the new features or concepts. - [ ] Create a post on [solidity-website](https://github.com/argotorg/solidity-website/tree/main/src/posts) in the `Security Alerts` category in case of important bug(s). -- [ ] Get the posts reviewed and approved **before the release starts**. +- [ ] Update the release information section [in the source of soliditylang.org](https://github.com/argotorg/solidity-website/blob/main/src/pages/index.tsx). +- [ ] Get the PR(s) for the above reviewed and approved **before the release starts**, but don't merge them yet. ### Changelog - [ ] Ensure that all changelog entries are correctly classified as language or compiler features. @@ -95,8 +96,7 @@ At least a day before the release: - [ ] Remove "still in progress" warning from the [release notes](https://github.com/argotorg/solidity/releases). ### Comms -- [ ] Merge the [blog posts](https://github.com/argotorg/solidity-website/pulls) related to the release. -- [ ] Update the release information section [in the source of soliditylang.org](https://github.com/argotorg/solidity-website/blob/main/src/pages/index.tsx). +- [ ] Merge the blog posts and website updates prepared for the release. - [ ] Verify that the link to the blog post in [release notes](https://github.com/argotorg/solidity/releases) is not broken. - [ ] Announce on [Twitter](https://twitter.com/solidity_lang), including links to the release and the blog post. - [ ] Announce on [Fosstodon](https://fosstodon.org/@solidity/), including links to the release and the blog post. From 7bd6b273180c798d559163c4cdec52e627d45d58 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Thu, 11 Dec 2025 02:29:33 -0300 Subject: [PATCH 156/243] Replace transfer with call in smt tests --- .../model_checker_targets_assert_chc/err | 12 ++---- .../input.sol | 5 ++- .../err | 5 --- .../input.sol | 3 +- .../model_checker_targets_div_by_zero_chc/err | 7 +--- .../input.sol | 5 ++- .../model_checker_targets_error/input.sol | 5 ++- .../err | 12 ++---- .../input.sol | 5 ++- .../model_checker_targets_overflow_chc/err | 7 +--- .../input.sol | 5 ++- .../model_checker_targets_pop_empty_chc/err | 12 ++---- .../input.sol | 5 ++- .../model_checker_targets_underflow_chc/err | 7 +--- .../input.sol | 5 ++- .../err | 14 +++---- .../input.sol | 5 ++- .../err | 8 +--- .../input.sol | 5 ++- .../input.json | 3 +- .../output.json | 33 +++++------------ .../input.json | 3 +- .../output.json | 21 ----------- .../input.json | 3 +- .../output.json | 21 +---------- .../input.json | 3 +- .../input.json | 3 +- .../output.json | 33 +++++------------ .../input.json | 3 +- .../output.json | 21 +---------- .../input.json | 3 +- .../output.json | 33 +++++------------ .../input.json | 3 +- .../output.json | 21 +---------- .../input.json | 3 +- .../output.json | 37 +++++++------------ .../input.json | 3 +- .../output.json | 23 ++---------- .../input.json | 3 +- .../input.json | 3 +- .../input.json | 3 +- 41 files changed, 128 insertions(+), 286 deletions(-) delete mode 100644 test/cmdlineTests/model_checker_targets_constant_condition_chc/err diff --git a/test/cmdlineTests/model_checker_targets_assert_chc/err b/test/cmdlineTests/model_checker_targets_assert_chc/err index 096773e68e..fb4d9fcc59 100644 --- a/test/cmdlineTests/model_checker_targets_assert_chc/err +++ b/test/cmdlineTests/model_checker_targets_assert_chc/err @@ -1,20 +1,16 @@ -Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. - --> input.sol:10:3: - | -10 | a.transfer(x); - | ^^^^^^^^^^ - Warning: CHC: Assertion violation happens here. Counterexample: arr = [] a = 0x0 x = 0 +success = true Transaction trace: test.constructor() State: arr = [] test.f(0x0, 1) - --> input.sol:11:3: + a.call{value: x}("") -- untrusted external call + --> input.sol:12:3: | -11 | assert(x > 0); +12 | assert(x > 0); | ^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_targets_assert_chc/input.sol b/test/cmdlineTests/model_checker_targets_assert_chc/input.sol index 33b5bfab3f..0061441c0c 100644 --- a/test/cmdlineTests/model_checker_targets_assert_chc/input.sol +++ b/test/cmdlineTests/model_checker_targets_assert_chc/input.sol @@ -7,9 +7,10 @@ contract test { --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(""); + require(success); assert(x > 0); arr.pop(); arr[x]; } -} \ No newline at end of file +} diff --git a/test/cmdlineTests/model_checker_targets_constant_condition_chc/err b/test/cmdlineTests/model_checker_targets_constant_condition_chc/err deleted file mode 100644 index 4876e93661..0000000000 --- a/test/cmdlineTests/model_checker_targets_constant_condition_chc/err +++ /dev/null @@ -1,5 +0,0 @@ -Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. - --> input.sol:11:3: - | -11 | a.transfer(x); - | ^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_targets_constant_condition_chc/input.sol b/test/cmdlineTests/model_checker_targets_constant_condition_chc/input.sol index 4b521b3deb..4103252cad 100644 --- a/test/cmdlineTests/model_checker_targets_constant_condition_chc/input.sol +++ b/test/cmdlineTests/model_checker_targets_constant_condition_chc/input.sol @@ -8,7 +8,8 @@ contract test { --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(""); + require(success); assert(x > 0); arr.pop(); arr[x]; diff --git a/test/cmdlineTests/model_checker_targets_div_by_zero_chc/err b/test/cmdlineTests/model_checker_targets_div_by_zero_chc/err index 3e00272485..b9b84f2917 100644 --- a/test/cmdlineTests/model_checker_targets_div_by_zero_chc/err +++ b/test/cmdlineTests/model_checker_targets_div_by_zero_chc/err @@ -1,14 +1,9 @@ -Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. - --> input.sol:10:3: - | -10 | a.transfer(x); - | ^^^^^^^^^^ - Warning: CHC: Division by zero happens here. Counterexample: arr = [] a = 0x0 x = 0 +success = false Transaction trace: test.constructor() diff --git a/test/cmdlineTests/model_checker_targets_div_by_zero_chc/input.sol b/test/cmdlineTests/model_checker_targets_div_by_zero_chc/input.sol index 33b5bfab3f..0061441c0c 100644 --- a/test/cmdlineTests/model_checker_targets_div_by_zero_chc/input.sol +++ b/test/cmdlineTests/model_checker_targets_div_by_zero_chc/input.sol @@ -7,9 +7,10 @@ contract test { --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(""); + require(success); assert(x > 0); arr.pop(); arr[x]; } -} \ No newline at end of file +} diff --git a/test/cmdlineTests/model_checker_targets_error/input.sol b/test/cmdlineTests/model_checker_targets_error/input.sol index 33b5bfab3f..0061441c0c 100644 --- a/test/cmdlineTests/model_checker_targets_error/input.sol +++ b/test/cmdlineTests/model_checker_targets_error/input.sol @@ -7,9 +7,10 @@ contract test { --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(""); + require(success); assert(x > 0); arr.pop(); arr[x]; } -} \ No newline at end of file +} diff --git a/test/cmdlineTests/model_checker_targets_out_of_bounds_chc/err b/test/cmdlineTests/model_checker_targets_out_of_bounds_chc/err index 9026ef934d..cefc673499 100644 --- a/test/cmdlineTests/model_checker_targets_out_of_bounds_chc/err +++ b/test/cmdlineTests/model_checker_targets_out_of_bounds_chc/err @@ -1,20 +1,16 @@ -Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. - --> input.sol:10:3: - | -10 | a.transfer(x); - | ^^^^^^^^^^ - Warning: CHC: Out of bounds access happens here. Counterexample: arr = [] a = 0x0 x = 0 +success = true Transaction trace: test.constructor() State: arr = [] test.f(0x0, 1) - --> input.sol:13:3: + a.call{value: x}("") -- untrusted external call + --> input.sol:14:3: | -13 | arr[x]; +14 | arr[x]; | ^^^^^^ diff --git a/test/cmdlineTests/model_checker_targets_out_of_bounds_chc/input.sol b/test/cmdlineTests/model_checker_targets_out_of_bounds_chc/input.sol index 33b5bfab3f..0061441c0c 100644 --- a/test/cmdlineTests/model_checker_targets_out_of_bounds_chc/input.sol +++ b/test/cmdlineTests/model_checker_targets_out_of_bounds_chc/input.sol @@ -7,9 +7,10 @@ contract test { --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(""); + require(success); assert(x > 0); arr.pop(); arr[x]; } -} \ No newline at end of file +} diff --git a/test/cmdlineTests/model_checker_targets_overflow_chc/err b/test/cmdlineTests/model_checker_targets_overflow_chc/err index 178b59a44e..bbaee17783 100644 --- a/test/cmdlineTests/model_checker_targets_overflow_chc/err +++ b/test/cmdlineTests/model_checker_targets_overflow_chc/err @@ -1,14 +1,9 @@ -Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. - --> input.sol:10:3: - | -10 | a.transfer(x); - | ^^^^^^^^^^ - Warning: CHC: Overflow (resulting value larger than 2**256 - 1) happens here. Counterexample: arr = [] a = 0x0 x = 1 +success = false Transaction trace: test.constructor() diff --git a/test/cmdlineTests/model_checker_targets_overflow_chc/input.sol b/test/cmdlineTests/model_checker_targets_overflow_chc/input.sol index 33b5bfab3f..0061441c0c 100644 --- a/test/cmdlineTests/model_checker_targets_overflow_chc/input.sol +++ b/test/cmdlineTests/model_checker_targets_overflow_chc/input.sol @@ -7,9 +7,10 @@ contract test { --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(""); + require(success); assert(x > 0); arr.pop(); arr[x]; } -} \ No newline at end of file +} diff --git a/test/cmdlineTests/model_checker_targets_pop_empty_chc/err b/test/cmdlineTests/model_checker_targets_pop_empty_chc/err index e4c1bbc0ef..78524bd903 100644 --- a/test/cmdlineTests/model_checker_targets_pop_empty_chc/err +++ b/test/cmdlineTests/model_checker_targets_pop_empty_chc/err @@ -1,20 +1,16 @@ -Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. - --> input.sol:10:3: - | -10 | a.transfer(x); - | ^^^^^^^^^^ - Warning: CHC: Empty array "pop" happens here. Counterexample: arr = [] a = 0x0 x = 0 +success = true Transaction trace: test.constructor() State: arr = [] test.f(0x0, 1) - --> input.sol:12:3: + a.call{value: x}("") -- untrusted external call + --> input.sol:13:3: | -12 | arr.pop(); +13 | arr.pop(); | ^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_targets_pop_empty_chc/input.sol b/test/cmdlineTests/model_checker_targets_pop_empty_chc/input.sol index 33b5bfab3f..0061441c0c 100644 --- a/test/cmdlineTests/model_checker_targets_pop_empty_chc/input.sol +++ b/test/cmdlineTests/model_checker_targets_pop_empty_chc/input.sol @@ -7,9 +7,10 @@ contract test { --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(""); + require(success); assert(x > 0); arr.pop(); arr[x]; } -} \ No newline at end of file +} diff --git a/test/cmdlineTests/model_checker_targets_underflow_chc/err b/test/cmdlineTests/model_checker_targets_underflow_chc/err index f9fbf82eec..f245215584 100644 --- a/test/cmdlineTests/model_checker_targets_underflow_chc/err +++ b/test/cmdlineTests/model_checker_targets_underflow_chc/err @@ -1,14 +1,9 @@ -Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. - --> input.sol:10:3: - | -10 | a.transfer(x); - | ^^^^^^^^^^ - Warning: CHC: Underflow (resulting value less than 0) happens here. Counterexample: arr = [] a = 0x0 x = 0 +success = false Transaction trace: test.constructor() diff --git a/test/cmdlineTests/model_checker_targets_underflow_chc/input.sol b/test/cmdlineTests/model_checker_targets_underflow_chc/input.sol index 33b5bfab3f..0061441c0c 100644 --- a/test/cmdlineTests/model_checker_targets_underflow_chc/input.sol +++ b/test/cmdlineTests/model_checker_targets_underflow_chc/input.sol @@ -7,9 +7,10 @@ contract test { --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(""); + require(success); assert(x > 0); arr.pop(); arr[x]; } -} \ No newline at end of file +} diff --git a/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_chc/err b/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_chc/err index f91c067026..8388d76ede 100644 --- a/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_chc/err +++ b/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_chc/err @@ -1,14 +1,9 @@ -Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. - --> input.sol:10:3: - | -10 | a.transfer(x); - | ^^^^^^^^^^ - Warning: CHC: Underflow (resulting value less than 0) happens here. Counterexample: arr = [] a = 0x0 x = 0 +success = false Transaction trace: test.constructor() @@ -24,6 +19,7 @@ Counterexample: arr = [] a = 0x0 x = 1 +success = false Transaction trace: test.constructor() @@ -39,12 +35,14 @@ Counterexample: arr = [] a = 0x0 x = 0 +success = true Transaction trace: test.constructor() State: arr = [] test.f(0x0, 1) - --> input.sol:11:3: + a.call{value: x}("") -- untrusted external call + --> input.sol:12:3: | -11 | assert(x > 0); +12 | assert(x > 0); | ^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_chc/input.sol b/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_chc/input.sol index 33b5bfab3f..0061441c0c 100644 --- a/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_chc/input.sol +++ b/test/cmdlineTests/model_checker_targets_underflow_overflow_assert_chc/input.sol @@ -7,9 +7,10 @@ contract test { --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(""); + require(success); assert(x > 0); arr.pop(); arr[x]; } -} \ No newline at end of file +} diff --git a/test/cmdlineTests/model_checker_targets_underflow_overflow_chc/err b/test/cmdlineTests/model_checker_targets_underflow_overflow_chc/err index 44faa5ae83..5751c90722 100644 --- a/test/cmdlineTests/model_checker_targets_underflow_overflow_chc/err +++ b/test/cmdlineTests/model_checker_targets_underflow_overflow_chc/err @@ -1,14 +1,9 @@ -Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }("")' instead. - --> input.sol:10:3: - | -10 | a.transfer(x); - | ^^^^^^^^^^ - Warning: CHC: Underflow (resulting value less than 0) happens here. Counterexample: arr = [] a = 0x0 x = 0 +success = false Transaction trace: test.constructor() @@ -24,6 +19,7 @@ Counterexample: arr = [] a = 0x0 x = 1 +success = false Transaction trace: test.constructor() diff --git a/test/cmdlineTests/model_checker_targets_underflow_overflow_chc/input.sol b/test/cmdlineTests/model_checker_targets_underflow_overflow_chc/input.sol index 33b5bfab3f..0061441c0c 100644 --- a/test/cmdlineTests/model_checker_targets_underflow_overflow_chc/input.sol +++ b/test/cmdlineTests/model_checker_targets_underflow_overflow_chc/input.sol @@ -7,9 +7,10 @@ contract test { --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(""); + require(success); assert(x > 0); arr.pop(); arr[x]; } -} \ No newline at end of file +} diff --git a/test/cmdlineTests/standard_model_checker_targets_assert_chc/input.json b/test/cmdlineTests/standard_model_checker_targets_assert_chc/input.json index a9a7b3fcdb..e28a8d2259 100644 --- a/test/cmdlineTests/standard_model_checker_targets_assert_chc/input.json +++ b/test/cmdlineTests/standard_model_checker_targets_assert_chc/input.json @@ -11,7 +11,8 @@ --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(\"\"); + require(success); assert(x > 0); arr.pop(); arr[x]; diff --git a/test/cmdlineTests/standard_model_checker_targets_assert_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_assert_chc/output.json index d7cfd4a82c..6bdd27918b 100644 --- a/test/cmdlineTests/standard_model_checker_targets_assert_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_assert_chc/output.json @@ -1,24 +1,5 @@ { "errors": [ - { - "component": "general", - "errorCode": "9207", - "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. - --> A:11:7: - | -11 | \t\t\t\t\t\ta.transfer(x); - | \t\t\t\t\t\t^^^^^^^^^^ - -", - "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", - "severity": "warning", - "sourceLocation": { - "end": 234, - "file": "A", - "start": 224 - }, - "type": "Warning" - }, { "component": "general", "errorCode": "6328", @@ -27,14 +8,16 @@ Counterexample: arr = [] a = 0x0 x = 0 +success = true Transaction trace: test.constructor() State: arr = [] test.f(0x0, 1) - --> A:12:7: + a.call{value: x}(\"\") -- untrusted external call + --> A:13:7: | -12 | \t\t\t\t\t\tassert(x > 0); +13 | \t\t\t\t\t\tassert(x > 0); | \t\t\t\t\t\t^^^^^^^^^^^^^ ", @@ -43,16 +26,18 @@ Counterexample: arr = [] a = 0x0 x = 0 +success = true Transaction trace: test.constructor() State: arr = [] -test.f(0x0, 1)", +test.f(0x0, 1) + a.call{value: x}(\"\") -- untrusted external call", "severity": "warning", "sourceLocation": { - "end": 258, + "end": 308, "file": "A", - "start": 245 + "start": 295 }, "type": "Warning" } diff --git a/test/cmdlineTests/standard_model_checker_targets_constantCondition_chc/input.json b/test/cmdlineTests/standard_model_checker_targets_constantCondition_chc/input.json index 365ca122be..a7e2be4294 100644 --- a/test/cmdlineTests/standard_model_checker_targets_constantCondition_chc/input.json +++ b/test/cmdlineTests/standard_model_checker_targets_constantCondition_chc/input.json @@ -11,7 +11,8 @@ --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(\"\"); + require(success); assert(x > 0); arr.pop(); arr[x]; diff --git a/test/cmdlineTests/standard_model_checker_targets_constantCondition_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_constantCondition_chc/output.json index d6a7b5a4ef..f047ff70a9 100644 --- a/test/cmdlineTests/standard_model_checker_targets_constantCondition_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_constantCondition_chc/output.json @@ -1,25 +1,4 @@ { - "errors": [ - { - "component": "general", - "errorCode": "9207", - "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. - --> A:11:7: - | -11 | \t\t\t\t\t\ta.transfer(x); - | \t\t\t\t\t\t^^^^^^^^^^ - -", - "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", - "severity": "warning", - "sourceLocation": { - "end": 234, - "file": "A", - "start": 224 - }, - "type": "Warning" - } - ], "sources": { "A": { "id": 0 diff --git a/test/cmdlineTests/standard_model_checker_targets_div_by_zero_chc/input.json b/test/cmdlineTests/standard_model_checker_targets_div_by_zero_chc/input.json index 1c7af8d5c1..c7476eface 100644 --- a/test/cmdlineTests/standard_model_checker_targets_div_by_zero_chc/input.json +++ b/test/cmdlineTests/standard_model_checker_targets_div_by_zero_chc/input.json @@ -11,7 +11,8 @@ --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(\"\"); + require(success); assert(x > 0); arr.pop(); arr[x]; diff --git a/test/cmdlineTests/standard_model_checker_targets_div_by_zero_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_div_by_zero_chc/output.json index e5a54e4c87..867decb52a 100644 --- a/test/cmdlineTests/standard_model_checker_targets_div_by_zero_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_div_by_zero_chc/output.json @@ -1,24 +1,5 @@ { "errors": [ - { - "component": "general", - "errorCode": "9207", - "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. - --> A:11:7: - | -11 | \t\t\t\t\t\ta.transfer(x); - | \t\t\t\t\t\t^^^^^^^^^^ - -", - "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", - "severity": "warning", - "sourceLocation": { - "end": 234, - "file": "A", - "start": 224 - }, - "type": "Warning" - }, { "component": "general", "errorCode": "4281", @@ -27,6 +8,7 @@ Counterexample: arr = [] a = 0x0 x = 0 +success = false Transaction trace: test.constructor() @@ -43,6 +25,7 @@ Counterexample: arr = [] a = 0x0 x = 0 +success = false Transaction trace: test.constructor() diff --git a/test/cmdlineTests/standard_model_checker_targets_empty_array/input.json b/test/cmdlineTests/standard_model_checker_targets_empty_array/input.json index cb51e49058..40ccfcbeb9 100644 --- a/test/cmdlineTests/standard_model_checker_targets_empty_array/input.json +++ b/test/cmdlineTests/standard_model_checker_targets_empty_array/input.json @@ -11,7 +11,8 @@ --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(\"\"); + require(success); assert(x > 0); arr.pop(); arr[x]; diff --git a/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_chc/input.json b/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_chc/input.json index cd0ba6d135..63ccad902f 100644 --- a/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_chc/input.json +++ b/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_chc/input.json @@ -11,7 +11,8 @@ --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(\"\"); + require(success); assert(x > 0); arr.pop(); arr[x]; diff --git a/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_chc/output.json index ecb01eac35..80d9190663 100644 --- a/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_out_of_bounds_chc/output.json @@ -1,24 +1,5 @@ { "errors": [ - { - "component": "general", - "errorCode": "9207", - "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. - --> A:11:7: - | -11 | \t\t\t\t\t\ta.transfer(x); - | \t\t\t\t\t\t^^^^^^^^^^ - -", - "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", - "severity": "warning", - "sourceLocation": { - "end": 234, - "file": "A", - "start": 224 - }, - "type": "Warning" - }, { "component": "general", "errorCode": "6368", @@ -27,14 +8,16 @@ Counterexample: arr = [] a = 0x0 x = 0 +success = true Transaction trace: test.constructor() State: arr = [] test.f(0x0, 1) - --> A:14:7: + a.call{value: x}(\"\") -- untrusted external call + --> A:15:7: | -14 | \t\t\t\t\t\tarr[x]; +15 | \t\t\t\t\t\tarr[x]; | \t\t\t\t\t\t^^^^^^ ", @@ -43,16 +26,18 @@ Counterexample: arr = [] a = 0x0 x = 0 +success = true Transaction trace: test.constructor() State: arr = [] -test.f(0x0, 1)", +test.f(0x0, 1) + a.call{value: x}(\"\") -- untrusted external call", "severity": "warning", "sourceLocation": { - "end": 289, + "end": 339, "file": "A", - "start": 283 + "start": 333 }, "type": "Warning" } diff --git a/test/cmdlineTests/standard_model_checker_targets_overflow_chc/input.json b/test/cmdlineTests/standard_model_checker_targets_overflow_chc/input.json index 522d2480ca..0141cbdb09 100644 --- a/test/cmdlineTests/standard_model_checker_targets_overflow_chc/input.json +++ b/test/cmdlineTests/standard_model_checker_targets_overflow_chc/input.json @@ -11,7 +11,8 @@ --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(\"\"); + require(success); assert(x > 0); arr.pop(); arr[x]; diff --git a/test/cmdlineTests/standard_model_checker_targets_overflow_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_overflow_chc/output.json index 1b0078d579..6180cdef7a 100644 --- a/test/cmdlineTests/standard_model_checker_targets_overflow_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_overflow_chc/output.json @@ -1,24 +1,5 @@ { "errors": [ - { - "component": "general", - "errorCode": "9207", - "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. - --> A:11:7: - | -11 | \t\t\t\t\t\ta.transfer(x); - | \t\t\t\t\t\t^^^^^^^^^^ - -", - "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", - "severity": "warning", - "sourceLocation": { - "end": 234, - "file": "A", - "start": 224 - }, - "type": "Warning" - }, { "component": "general", "errorCode": "4984", @@ -27,6 +8,7 @@ Counterexample: arr = [] a = 0x0 x = 1 +success = false Transaction trace: test.constructor() @@ -43,6 +25,7 @@ Counterexample: arr = [] a = 0x0 x = 1 +success = false Transaction trace: test.constructor() diff --git a/test/cmdlineTests/standard_model_checker_targets_pop_empty_chc/input.json b/test/cmdlineTests/standard_model_checker_targets_pop_empty_chc/input.json index 7d2dc69e31..430201dbf6 100644 --- a/test/cmdlineTests/standard_model_checker_targets_pop_empty_chc/input.json +++ b/test/cmdlineTests/standard_model_checker_targets_pop_empty_chc/input.json @@ -11,7 +11,8 @@ --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(\"\"); + require(success); assert(x > 0); arr.pop(); arr[x]; diff --git a/test/cmdlineTests/standard_model_checker_targets_pop_empty_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_pop_empty_chc/output.json index 6ac6acd960..ee0e0a6efe 100644 --- a/test/cmdlineTests/standard_model_checker_targets_pop_empty_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_pop_empty_chc/output.json @@ -1,24 +1,5 @@ { "errors": [ - { - "component": "general", - "errorCode": "9207", - "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. - --> A:11:7: - | -11 | \t\t\t\t\t\ta.transfer(x); - | \t\t\t\t\t\t^^^^^^^^^^ - -", - "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", - "severity": "warning", - "sourceLocation": { - "end": 234, - "file": "A", - "start": 224 - }, - "type": "Warning" - }, { "component": "general", "errorCode": "2529", @@ -27,14 +8,16 @@ Counterexample: arr = [] a = 0x0 x = 0 +success = true Transaction trace: test.constructor() State: arr = [] test.f(0x0, 1) - --> A:13:7: + a.call{value: x}(\"\") -- untrusted external call + --> A:14:7: | -13 | \t\t\t\t\t\tarr.pop(); +14 | \t\t\t\t\t\tarr.pop(); | \t\t\t\t\t\t^^^^^^^^^ ", @@ -43,16 +26,18 @@ Counterexample: arr = [] a = 0x0 x = 0 +success = true Transaction trace: test.constructor() State: arr = [] -test.f(0x0, 1)", +test.f(0x0, 1) + a.call{value: x}(\"\") -- untrusted external call", "severity": "warning", "sourceLocation": { - "end": 275, + "end": 325, "file": "A", - "start": 266 + "start": 316 }, "type": "Warning" } diff --git a/test/cmdlineTests/standard_model_checker_targets_underflow_chc/input.json b/test/cmdlineTests/standard_model_checker_targets_underflow_chc/input.json index ca33d2fdcc..e590fcd4d8 100644 --- a/test/cmdlineTests/standard_model_checker_targets_underflow_chc/input.json +++ b/test/cmdlineTests/standard_model_checker_targets_underflow_chc/input.json @@ -11,7 +11,8 @@ --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(\"\"); + require(success); assert(x > 0); arr.pop(); arr[x]; diff --git a/test/cmdlineTests/standard_model_checker_targets_underflow_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_underflow_chc/output.json index 67218582ba..373646fa70 100644 --- a/test/cmdlineTests/standard_model_checker_targets_underflow_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_underflow_chc/output.json @@ -1,24 +1,5 @@ { "errors": [ - { - "component": "general", - "errorCode": "9207", - "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. - --> A:11:7: - | -11 | \t\t\t\t\t\ta.transfer(x); - | \t\t\t\t\t\t^^^^^^^^^^ - -", - "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", - "severity": "warning", - "sourceLocation": { - "end": 234, - "file": "A", - "start": 224 - }, - "type": "Warning" - }, { "component": "general", "errorCode": "3944", @@ -27,6 +8,7 @@ Counterexample: arr = [] a = 0x0 x = 0 +success = false Transaction trace: test.constructor() @@ -43,6 +25,7 @@ Counterexample: arr = [] a = 0x0 x = 0 +success = false Transaction trace: test.constructor() diff --git a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_chc/input.json b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_chc/input.json index 50f0e82dc5..6d4ad0c74a 100644 --- a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_chc/input.json +++ b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_chc/input.json @@ -11,7 +11,8 @@ --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(\"\"); + require(success); assert(x > 0); arr.pop(); arr[x]; diff --git a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_chc/output.json index 8f28e79615..0ab2d1d988 100644 --- a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_assert_chc/output.json @@ -1,24 +1,5 @@ { "errors": [ - { - "component": "general", - "errorCode": "9207", - "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. - --> A:11:7: - | -11 | \t\t\t\t\t\ta.transfer(x); - | \t\t\t\t\t\t^^^^^^^^^^ - -", - "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", - "severity": "warning", - "sourceLocation": { - "end": 234, - "file": "A", - "start": 224 - }, - "type": "Warning" - }, { "component": "general", "errorCode": "3944", @@ -27,6 +8,7 @@ Counterexample: arr = [] a = 0x0 x = 0 +success = false Transaction trace: test.constructor() @@ -43,6 +25,7 @@ Counterexample: arr = [] a = 0x0 x = 0 +success = false Transaction trace: test.constructor() @@ -64,6 +47,7 @@ Counterexample: arr = [] a = 0x0 x = 1 +success = false Transaction trace: test.constructor() @@ -80,6 +64,7 @@ Counterexample: arr = [] a = 0x0 x = 1 +success = false Transaction trace: test.constructor() @@ -101,14 +86,16 @@ Counterexample: arr = [] a = 0x0 x = 0 +success = true Transaction trace: test.constructor() State: arr = [] test.f(0x0, 1) - --> A:12:7: + a.call{value: x}(\"\") -- untrusted external call + --> A:13:7: | -12 | \t\t\t\t\t\tassert(x > 0); +13 | \t\t\t\t\t\tassert(x > 0); | \t\t\t\t\t\t^^^^^^^^^^^^^ ", @@ -117,16 +104,18 @@ Counterexample: arr = [] a = 0x0 x = 0 +success = true Transaction trace: test.constructor() State: arr = [] -test.f(0x0, 1)", +test.f(0x0, 1) + a.call{value: x}(\"\") -- untrusted external call", "severity": "warning", "sourceLocation": { - "end": 258, + "end": 308, "file": "A", - "start": 245 + "start": 295 }, "type": "Warning" } diff --git a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_chc/input.json b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_chc/input.json index 9fc834fd86..a034203058 100644 --- a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_chc/input.json +++ b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_chc/input.json @@ -11,7 +11,8 @@ --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(\"\"); + require(success); assert(x > 0); arr.pop(); arr[x]; diff --git a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_chc/output.json b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_chc/output.json index 97442455c0..44c3d23408 100644 --- a/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_chc/output.json +++ b/test/cmdlineTests/standard_model_checker_targets_underflow_overflow_chc/output.json @@ -1,24 +1,5 @@ { "errors": [ - { - "component": "general", - "errorCode": "9207", - "formattedMessage": "Warning: 'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead. - --> A:11:7: - | -11 | \t\t\t\t\t\ta.transfer(x); - | \t\t\t\t\t\t^^^^^^^^^^ - -", - "message": "'transfer' is deprecated and scheduled for removal. Use 'call{value: }(\"\")' instead.", - "severity": "warning", - "sourceLocation": { - "end": 234, - "file": "A", - "start": 224 - }, - "type": "Warning" - }, { "component": "general", "errorCode": "3944", @@ -27,6 +8,7 @@ Counterexample: arr = [] a = 0x0 x = 0 +success = false Transaction trace: test.constructor() @@ -43,6 +25,7 @@ Counterexample: arr = [] a = 0x0 x = 0 +success = false Transaction trace: test.constructor() @@ -64,6 +47,7 @@ Counterexample: arr = [] a = 0x0 x = 1 +success = false Transaction trace: test.constructor() @@ -80,6 +64,7 @@ Counterexample: arr = [] a = 0x0 x = 1 +success = false Transaction trace: test.constructor() diff --git a/test/cmdlineTests/standard_model_checker_targets_wrong_target_types/input.json b/test/cmdlineTests/standard_model_checker_targets_wrong_target_types/input.json index a89271975c..3e8b7414f0 100644 --- a/test/cmdlineTests/standard_model_checker_targets_wrong_target_types/input.json +++ b/test/cmdlineTests/standard_model_checker_targets_wrong_target_types/input.json @@ -11,7 +11,8 @@ --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(\"\"); + require(success); assert(x > 0); arr.pop(); arr[x]; diff --git a/test/cmdlineTests/standard_model_checker_targets_wrong_target_types_2/input.json b/test/cmdlineTests/standard_model_checker_targets_wrong_target_types_2/input.json index 136ce0978f..0f450668dd 100644 --- a/test/cmdlineTests/standard_model_checker_targets_wrong_target_types_2/input.json +++ b/test/cmdlineTests/standard_model_checker_targets_wrong_target_types_2/input.json @@ -11,7 +11,8 @@ --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(\"\"); + require(success); assert(x > 0); arr.pop(); arr[x]; diff --git a/test/cmdlineTests/standard_model_checker_targets_wrong_targets/input.json b/test/cmdlineTests/standard_model_checker_targets_wrong_targets/input.json index 1788fb6d55..a2f3606757 100644 --- a/test/cmdlineTests/standard_model_checker_targets_wrong_targets/input.json +++ b/test/cmdlineTests/standard_model_checker_targets_wrong_targets/input.json @@ -11,7 +11,8 @@ --x; x + type(uint).max; 2 / x; - a.transfer(x); + (bool success, ) = a.call{value: x}(\"\"); + require(success); assert(x > 0); arr.pop(); arr[x]; From 041b918885a642a28525a3d1f4a2dda3d26f8a7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 10 Oct 2025 20:45:01 +0200 Subject: [PATCH 157/243] Reword docs about linux packages and add info about Nix --- docs/installing-solidity.rst | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 97a754eaaa..f061829ecf 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -113,23 +113,34 @@ self-contained (i.e. it does not refer to any external files that would have to Linux Packages ============== -Binary packages of Solidity are available at -`solidity/releases `_. +We provide :ref:`standalone binaries ` of the compiler that should run on most +distributions without any additional installation steps. Ubuntu packages for versions up to 0.8.30 are available in the `ethereum/ethereum PPA `_. However, we have discontinued this distribution method and future versions will not be added there. -Furthermore, some Linux distributions provide their own packages. These packages are not directly -maintained by us but usually kept up-to-date by the respective package maintainers. +Some Linux distributions provide their own packages. +These packages are not directly maintained by us but usually kept up-to-date by the respective +package maintainers. -For example, Arch Linux has packages for the latest development version as AUR packages: `solidity `_ -and `solidity-bin `_. +Unofficial, community-maintained scripts for building and installing the compiler are also +available for some distributions: + +- Arch Linux / (AUR): + + - `solidity `_ (builds from source), + - `solidity-bin `_ (uses our standalone binaries). + +- Nix: + + - `solc.nix `_ (builds from source). .. note:: - Please be aware that `AUR `_ packages - are user-produced content and unofficial packages. Exercise caution when using them. + Please be aware that these scripts are produced and maintained by users and not vetted in any + way by the distro maintainers. + Exercise caution when using them. There is also a `snap package `_, however, it is **currently unmaintained**. It is installable in all the `supported Linux distros `_. To @@ -192,6 +203,8 @@ Install it using ``brew``: # eg. Install 0.4.8 brew install solidity.rb +.. _static-binaries: + Static Binaries =============== From c1975a232f5a77d9ccd38b1fafbb0b3251c88c2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 11 May 2024 17:33:03 +0200 Subject: [PATCH 158/243] Remove the prerequisite steps from the default optimizer sequence --- Changelog.md | 1 + docs/using-the-compiler.rst | 2 +- libsolidity/interface/OptimiserSettings.h | 8 ++++---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index 655e66e541..d1bc022e3a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Language Features: Compiler Features: +* Yul Optimizer: Remove redundant prerequisite steps from the default optimizer sequence. Bugfixes: diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 93d61af291..dbc7970794 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -349,7 +349,7 @@ Input Description // In particular if the whole value consists only of the delimiter, both sequences are empty. // Note that there are several hard-coded steps that always run, even when both sequences are empty. // For more information see "The Optimizer > Selecting Optimizations". - "optimizerSteps": "dhfoDgvulfnTUtnIf..." + "optimizerSteps": "dfDvulfnTUtnIf..." } } }, diff --git a/libsolidity/interface/OptimiserSettings.h b/libsolidity/interface/OptimiserSettings.h index d282e139d2..eec1e42b0d 100644 --- a/libsolidity/interface/OptimiserSettings.h +++ b/libsolidity/interface/OptimiserSettings.h @@ -42,7 +42,7 @@ enum class OptimisationPreset struct OptimiserSettings { static char constexpr DefaultYulOptimiserSteps[] = - "dhfoDgvulfnTUtnIf" // None of these can make stack problems worse + "dfDvulfnTUtnIf" // None of these can make stack problems worse "xa[r]EscLM" // Turn into SSA and simplify "Vcul [j]" // Reverse SSA @@ -51,13 +51,13 @@ struct OptimiserSettings "Trpeul" // Run functional expression inliner "xa[r]cL" // Turn into SSA again and simplify - "gvifM" // Run full inliner + "vifM" // Run full inliner "CTUca[r]LSsTFOtfDnca[r]Iulc" // SSA plus simplify "scCTUt" - "gvifM" // Run full inliner + "vifM" // Run full inliner "x[scCTUt] TOntnfDIul" // Perform structural simplification - "gvifM" // Run full inliner + "vifM" // Run full inliner "jmul[jul] VcTOcul jmul"; // Make source short and pretty From 2583eccd17cf70cc8ff984e6866b197d6259fe58 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Tue, 6 Jan 2026 12:59:30 +0100 Subject: [PATCH 159/243] EVM instruction interpreter: Add support for loadimmutable and setimmutable Theoretically, loadimmutable inserts a placeholder into the contract to be deployed which is then replaced by setimmutable once the runtime code is loaded into memory. The interpreter is mainly used for differential fuzzing so we should satisfy that correct code never fails and we can still detect bugs otherwise, most of the time. Therefore loadimmutable is decoupled from setimmutable and just returns a random value (hash of the identifier), setimmutable is treated as a no-op. --- .../isoltestTesting/loadimmutable.yul | 11 ++++++++++ .../isoltestTesting/setimmutable_stub.yul | 9 +++++++++ .../EVMInstructionInterpreter.cpp | 20 +++++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 test/libyul/yulInterpreterTests/isoltestTesting/loadimmutable.yul create mode 100644 test/libyul/yulInterpreterTests/isoltestTesting/setimmutable_stub.yul diff --git a/test/libyul/yulInterpreterTests/isoltestTesting/loadimmutable.yul b/test/libyul/yulInterpreterTests/isoltestTesting/loadimmutable.yul new file mode 100644 index 0000000000..3b13520f14 --- /dev/null +++ b/test/libyul/yulInterpreterTests/isoltestTesting/loadimmutable.yul @@ -0,0 +1,11 @@ +{ + // yields some value based on the hash of the argument literal + let value := loadimmutable("address") + mstore(0, value) +} +// ---- +// Trace: +// Memory dump: +// 0: 421683f821a0574472445355be6d2b769119e8515f8376a1d7878523dfdecf7b +// Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/isoltestTesting/setimmutable_stub.yul b/test/libyul/yulInterpreterTests/isoltestTesting/setimmutable_stub.yul new file mode 100644 index 0000000000..2becc61b6e --- /dev/null +++ b/test/libyul/yulInterpreterTests/isoltestTesting/setimmutable_stub.yul @@ -0,0 +1,9 @@ +{ + // implemented as no-op + setimmutable(42, "42", 42) +} +// ---- +// Trace: +// Memory dump: +// Storage dump: +// Transient storage dump: diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index cec5d55013..9d46c2b893 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -565,6 +565,26 @@ u256 EVMInstructionInterpreter::evalBuiltin( return 0; } + if (fun == "loadimmutable") + { + yulAssert(_arguments.size() == 1); + yulAssert(std::holds_alternative(_arguments[0])); + std::string const identifier = formatLiteral(std::get(_arguments[0])); + // Return a deterministic value based on the identifier. + // This is sufficient for differential fuzzing since the same identifier + // will always return the same value, maintaining trace equivalence. + return u256(h256(keccak256(identifier))); + } + + if (fun == "setimmutable") + { + yulAssert(_arguments.size() == 3); + // No-op: The real implementation patches placeholder bytes in memory-loaded runtime code. + // For differential fuzzing, this ensures correct code never fails (no false positives), though some + // bugs in setimmutable handling may not be detected (potential false negatives). + return 0; + } + yulAssert(false, "Unknown builtin: " + fun); } From 940aa92a382f210cc0b679fadbd5b8b264ec84ae Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 19 Jan 2026 17:46:35 +0100 Subject: [PATCH 160/243] EVMInstruction Interpreter: Improve linkersymbol to not just yield constant 0 --- .../isoltestTesting/linkersymbol.yul | 11 +++++++++++ .../yulInterpreter/EVMInstructionInterpreter.cpp | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 test/libyul/yulInterpreterTests/isoltestTesting/linkersymbol.yul diff --git a/test/libyul/yulInterpreterTests/isoltestTesting/linkersymbol.yul b/test/libyul/yulInterpreterTests/isoltestTesting/linkersymbol.yul new file mode 100644 index 0000000000..d7b896c14f --- /dev/null +++ b/test/libyul/yulInterpreterTests/isoltestTesting/linkersymbol.yul @@ -0,0 +1,11 @@ +{ + // yields some value based on the hash of the argument literal + let value := linkersymbol("address") + mstore(0, value) +} +// ---- +// Trace: +// Memory dump: +// 0: 421683f821a0574472445355be6d2b769119e8515f8376a1d7878523dfdecf7b +// Storage dump: +// Transient storage dump: diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index 9d46c2b893..8081a9df6e 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -562,7 +562,7 @@ u256 EVMInstructionInterpreter::evalBuiltin( std::string const placeholder = formatLiteral(std::get(_arguments[0])); h256 const identifier(keccak256(placeholder)); m_linkerSymbols.emplace(identifier, placeholder); - return 0; + return u256(identifier); } if (fun == "loadimmutable") From e80f7db293eb32e3a974753c40a45bf5c36c5c72 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Mon, 26 Jan 2026 18:39:25 +0100 Subject: [PATCH 161/243] Fix style check to detect misaligned pointer declarations --- scripts/check_style.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_style.sh b/scripts/check_style.sh index d67f7b60ca..5ea0e07d54 100755 --- a/scripts/check_style.sh +++ b/scripts/check_style.sh @@ -55,7 +55,7 @@ FORMATERROR=$( preparedGrep "^ [^*]|[^*] | [^*]" # uses spaces for indentation or mixes spaces and tabs preparedGrep "[a-zA-Z0-9_]\s*[&][a-zA-Z_]" | grep -E -v "return [&]" # right-aligned reference ampersand (needs to exclude return) # right-aligned reference pointer star (needs to exclude return and comments) - preparedGrep "[a-zA-Z0-9_]\s*[*][a-zA-Z_]" | grep -E -v -e "return [*]" -e "^* [*]" -e "^*//.*" + preparedGrep "[a-zA-Z0-9_]\s*[*][a-zA-Z_]" | grep -E -v -e "return [*]" -e ":[0-9]+:\s*\*\s" -e "//" # unqualified move()/forward() checks, i.e. make sure that std::move() and std::forward() are used instead of move() and forward() preparedGrep "move\(.+\)" | grep -v "std::move" | grep -E "[^a-z]move" preparedGrep "forward\(.+\)" | grep -v "std::forward" | grep -E "[^a-z]forward" From b8d14a5a251e20ec02df329c34519061b697f646 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Mon, 26 Jan 2026 18:41:46 +0100 Subject: [PATCH 162/243] Fix pointer declaration alignment style --- libsolidity/analysis/ControlFlowBuilder.h | 2 +- libsolidity/analysis/TypeChecker.cpp | 2 +- libsolidity/ast/Types.cpp | 2 +- libsolidity/codegen/CompilerContext.h | 2 +- libsolidity/codegen/ExpressionCompiler.cpp | 2 +- libyul/backends/evm/StackLayoutGenerator.cpp | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libsolidity/analysis/ControlFlowBuilder.h b/libsolidity/analysis/ControlFlowBuilder.h index 7af843fcae..92ebe4f88a 100644 --- a/libsolidity/analysis/ControlFlowBuilder.h +++ b/libsolidity/analysis/ControlFlowBuilder.h @@ -152,7 +152,7 @@ class ControlFlowBuilder: private ASTConstVisitor, private yul::ASTWalker CFGNode* newLabel(); CFGNode* createLabelHere(); - void placeAndConnectLabel(CFGNode *_node); + void placeAndConnectLabel(CFGNode* _node); CFG::NodeContainer& m_nodeContainer; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 8556487ff4..b54e591d20 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1447,7 +1447,7 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const& { bool isLocalOrReturn = false; if (auto const* identifier = dynamic_cast(&_expression)) - if (auto const *variableDeclaration = dynamic_cast(identifier->annotation().referencedDeclaration)) + if (auto const* variableDeclaration = dynamic_cast(identifier->annotation().referencedDeclaration)) if (variableDeclaration->isLocalOrReturn()) isLocalOrReturn = true; if (!isLocalOrReturn) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 316d99ba3e..8454fd1306 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3474,7 +3474,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const if (auto const* functionDefinition = dynamic_cast(m_declaration)) { solAssert(functionDefinition->visibility() > Visibility::Internal, ""); - auto const *contract = dynamic_cast(m_declaration->scope()); + auto const* contract = dynamic_cast(m_declaration->scope()); solAssert(contract, ""); solAssert(contract->isLibrary(), ""); return {{"selector", TypeProvider::fixedBytes(4)}}; diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 638ee6939c..49a996c6c8 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -374,7 +374,7 @@ class CompilerContext /// Stack of current visited AST nodes, used for location attachment std::stack m_visitedNodes; /// The runtime context if in Creation mode, this is used for generating tags that would be stored into the storage and then used at runtime. - CompilerContext *m_runtimeContext; + CompilerContext* m_runtimeContext; /// The index of the runtime subroutine. evmasm::SubAssemblyID m_runtimeSub{}; /// An index of low-level function labels by name. diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 2927444e2b..49313c8925 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -2298,7 +2298,7 @@ bool ExpressionCompiler::visit(IndexRangeAccess const& _indexAccess) Type const& baseType = *_indexAccess.baseExpression().annotation().type; - ArrayType const *arrayType = dynamic_cast(&baseType); + ArrayType const* arrayType = dynamic_cast(&baseType); if (!arrayType) if (ArraySliceType const* sliceType = dynamic_cast(&baseType)) arrayType = &sliceType->arrayType(); diff --git a/libyul/backends/evm/StackLayoutGenerator.cpp b/libyul/backends/evm/StackLayoutGenerator.cpp index f6d1108eeb..9a37adeb25 100644 --- a/libyul/backends/evm/StackLayoutGenerator.cpp +++ b/libyul/backends/evm/StackLayoutGenerator.cpp @@ -371,7 +371,7 @@ void StackLayoutGenerator::processEntryPoint(CFG::BasicBlock const& _entry, CFG: // entry layout of the backwards jump target as the initial exit layout of the backwards-jumping block. while (!toVisit.empty()) { - CFG::BasicBlock const *block = *toVisit.begin(); + CFG::BasicBlock const* block = *toVisit.begin(); toVisit.pop_front(); if (visited.count(block)) From 2eea2ae53c2ebe5e15238c19954d43768afc745e Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Mon, 26 Jan 2026 18:48:31 +0100 Subject: [PATCH 163/243] Fix unnecessary escaping in comment exclusion regex --- scripts/check_style.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_style.sh b/scripts/check_style.sh index 5ea0e07d54..7188f84f2b 100755 --- a/scripts/check_style.sh +++ b/scripts/check_style.sh @@ -59,7 +59,7 @@ FORMATERROR=$( # unqualified move()/forward() checks, i.e. make sure that std::move() and std::forward() are used instead of move() and forward() preparedGrep "move\(.+\)" | grep -v "std::move" | grep -E "[^a-z]move" preparedGrep "forward\(.+\)" | grep -v "std::forward" | grep -E "[^a-z]forward" -) | grep -E -v -e "^[a-zA-Z\./]*:[0-9]*:\s*\/(\/|\*)" -e "^test/" || true +) | grep -E -v -e "^[a-zA-Z./]*:[0-9]*:\s*/[/*]" -e "^test/" || true ) # Special error handling for `using namespace std;` exclusion, since said statement can be present in the test directory From 12004a1a5a3a5c1f0207a1762cb47002cbe2ef10 Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Wed, 28 Jan 2026 10:58:51 +0100 Subject: [PATCH 164/243] Refactor ccache build commands into a single `run_with_ccache_unless_tag` command - Remove CCACHE_ENABLED and use CCACHE_DISABLE instead --- .circleci/config.yml | 163 +++++++++++---------------------- scripts/ci/build.sh | 20 ++-- scripts/ci/build_emscripten.sh | 18 ++-- scripts/ci/build_ossfuzz.sh | 20 ++-- scripts/ci/build_win.sh | 52 +++++------ 5 files changed, 97 insertions(+), 176 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1b6a309fc1..2fd7fe83a5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -75,16 +75,20 @@ commands: event: release condition: on_success - ensure_ccache_key: - description: "Fail fast when CCACHE_KEY is missing." + ensure_ccache_key_unless_tag: + description: "Fail fast when CCACHE_KEY is missing (skipped on tags)." steps: - - run: - name: Check CCACHE_KEY - command: | - if [ -z "${CCACHE_KEY:-}" ]; then - echo "CCACHE_KEY is not set. Please set it in CircleCI environment settings." - exit 1 - fi + - when: + condition: + equal: ["", << pipeline.git.tag >>] + steps: + - run: + name: Check CCACHE_KEY + command: | + if [ -z "${CCACHE_KEY:-}" ]; then + echo "CCACHE_KEY is not set. Please set it in CircleCI environment settings." + exit 1 + fi restore_ccache_if_allowed: description: "Restore ccache unless on a tag or a skipped branch." @@ -123,6 +127,29 @@ commands: key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache-{{ .Revision }} paths: - ~/.ccache + run_with_ccache_unless_tag: + description: "Run a command with ccache enabled unless building a tag." + parameters: + command: + type: string + step_name: + type: string + steps: + - when: + condition: + equal: ["", << pipeline.git.tag >>] + steps: + - run: + name: << parameters.step_name >> (ccache) + command: << parameters.command >> + - when: + condition: + not: + equal: ["", << pipeline.git.tag >>] + steps: + - run: + name: << parameters.step_name >> (no ccache) + command: CCACHE_DISABLE=1 << parameters.command >> prepare_bytecode_report: description: "Generate bytecode report and upload it as an artifact." @@ -257,60 +284,22 @@ commands: run_build: steps: - - when: - condition: - equal: ["", << pipeline.git.tag >>] - steps: - - ensure_ccache_key + - ensure_ccache_key_unless_tag - restore_ccache_if_allowed # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings - - when: - condition: - and: - - equal: ["", << pipeline.git.tag >>] - steps: - - run: - name: Build (ccache) - command: CCACHE_ENABLED=${CCACHE_ENABLED:-1} scripts/ci/build.sh - - when: - condition: - or: - - not: - equal: ["", << pipeline.git.tag >>] - steps: - - run: - name: Build (no ccache) - command: scripts/ci/build.sh - + - run_with_ccache_unless_tag: + step_name: Build + command: scripts/ci/build.sh - save_ccache_if_develop run_build_ossfuzz: steps: - - when: - condition: - equal: ["", << pipeline.git.tag >>] - steps: - - ensure_ccache_key + - ensure_ccache_key_unless_tag - restore_ccache_if_allowed # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings - - when: - condition: - and: - - equal: ["", << pipeline.git.tag >>] - steps: - - run: - name: Build_ossfuzz (ccache) - command: CCACHE_ENABLED=${CCACHE_ENABLED:-1} scripts/ci/build_ossfuzz.sh - - when: - condition: - or: - - not: - equal: ["", << pipeline.git.tag >>] - steps: - - run: - name: Build_ossfuzz (no ccache) - command: scripts/ci/build_ossfuzz.sh - + - run_with_ccache_unless_tag: + step_name: Build_ossfuzz + command: scripts/ci/build_ossfuzz.sh - save_ccache_if_develop run_proofs: @@ -1352,32 +1341,12 @@ jobs: MAKEFLAGS: -j 10 steps: - checkout - - when: - condition: - equal: ["", << pipeline.git.tag >>] - steps: - - ensure_ccache_key + - ensure_ccache_key_unless_tag - restore_ccache_if_allowed # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings - - when: - condition: - and: - - equal: ["", << pipeline.git.tag >>] - steps: - - run: - name: Build (ccache) - command: CCACHE_ENABLED=${CCACHE_ENABLED:-1} scripts/ci/build_emscripten.sh - - when: - condition: - or: - - not: - equal: ["", << pipeline.git.tag >>] - steps: - - run: - name: Build (no ccache) - command: | - scripts/ci/build_emscripten.sh - + - run_with_ccache_unless_tag: + step_name: Build + command: scripts/ci/build_emscripten.sh - save_ccache_if_develop - store_artifacts: path: upload/soljson.js @@ -1855,39 +1824,13 @@ jobs: key: dependencies-win-{{ arch }}-{{ checksum "scripts/install_deps.ps1" }} paths: - .\deps - - when: - condition: - equal: ["", << pipeline.git.tag >>] - steps: - - ensure_ccache_key + - ensure_ccache_key_unless_tag - restore_ccache_if_allowed # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings - - when: - condition: - and: - - equal: ["", << pipeline.git.tag >>] - steps: - - run: - name: "Building solidity (ccache)" - command: CCACHE_ENABLED=${CCACHE_ENABLED:-1} scripts/ci/build_win.sh - - when: - condition: - or: - - not: - equal: ["", << pipeline.git.tag >>] - steps: - - run: - name: "Building solidity (no ccache)" - command: scripts/ci/build_win.sh - # On `ci-ccache` we always generate a fresh cache by never restoring and by saving under a unique key. - - when: - condition: - equal: ["ci-ccache", << pipeline.git.branch >>] - steps: - - save_cache: - key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache - paths: - - ~/.ccache + - run_with_ccache_unless_tag: + step_name: "Building solidity" + command: scripts/ci/build_win.sh + - save_ccache_if_develop - run: name: "Run solc.exe to make sure build was successful." command: .\build\solc\Release\solc.exe --version diff --git a/scripts/ci/build.sh b/scripts/ci/build.sh index 385d2d8989..e31b3f6f07 100755 --- a/scripts/ci/build.sh +++ b/scripts/ci/build.sh @@ -16,23 +16,17 @@ cd build [[ -n $COVERAGE && -z $CIRCLE_TAG ]] && CMAKE_OPTIONS="$CMAKE_OPTIONS -DCOVERAGE=ON" -if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then - export CCACHE_DIR="$HOME/.ccache" - export CCACHE_BASEDIR="$ROOTDIR" - export CCACHE_NOHASHDIR=1 - CMAKE_OPTIONS="${CMAKE_OPTIONS:-} -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache" - mkdir -p "$CCACHE_DIR" -fi +export CCACHE_DIR="$HOME/.ccache" +export CCACHE_BASEDIR="$ROOTDIR" +export CCACHE_NOHASHDIR=1 +CMAKE_OPTIONS="${CMAKE_OPTIONS:-} -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache" +mkdir -p "$CCACHE_DIR" # shellcheck disable=SC2086 cmake .. -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Release}" $CMAKE_OPTIONS -if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then - ccache -z -fi +ccache -z cmake --build . -if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then - ccache -s -fi +ccache -s diff --git a/scripts/ci/build_emscripten.sh b/scripts/ci/build_emscripten.sh index bf19bad360..035eedccb1 100755 --- a/scripts/ci/build_emscripten.sh +++ b/scripts/ci/build_emscripten.sh @@ -54,14 +54,12 @@ function build() { # TODO: This can be removed if and when all usages of `move()` in our codebase use the `std::` qualifier. CMAKE_CXX_FLAGS="-Wno-unqualified-std-cast-call" - if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then - export CCACHE_DIR="$HOME/.ccache" - CCACHE_BASEDIR="$(pwd)" - export CCACHE_BASEDIR - export CCACHE_NOHASHDIR=1 - mkdir -p "$CCACHE_DIR" - ccache -z - fi + export CCACHE_DIR="$HOME/.ccache" + CCACHE_BASEDIR="$(pwd)" + export CCACHE_BASEDIR + export CCACHE_NOHASHDIR=1 + mkdir -p "$CCACHE_DIR" + ccache -z mkdir -p "$build_dir" cd "$build_dir" @@ -73,9 +71,7 @@ function build() { -DTESTS=0 \ .. make soljson - if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then - ccache -s - fi + ccache -s cd .. mkdir -p upload diff --git a/scripts/ci/build_ossfuzz.sh b/scripts/ci/build_ossfuzz.sh index 883c939b89..f3e65d22dd 100755 --- a/scripts/ci/build_ossfuzz.sh +++ b/scripts/ci/build_ossfuzz.sh @@ -18,24 +18,18 @@ function generate_protobuf_bindings function build_fuzzers { cd "${BUILDDIR}" - if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then - export CCACHE_DIR="$HOME/.ccache" - export CCACHE_BASEDIR="$ROOTDIR" - export CCACHE_NOHASHDIR=1 - CMAKE_OPTIONS="${CMAKE_OPTIONS:-} -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache" - mkdir -p "$CCACHE_DIR" - fi + export CCACHE_DIR="$HOME/.ccache" + export CCACHE_BASEDIR="$ROOTDIR" + export CCACHE_NOHASHDIR=1 + CMAKE_OPTIONS="${CMAKE_OPTIONS:-} -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache" + mkdir -p "$CCACHE_DIR" # shellcheck disable=SC2086 cmake .. -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Release}" \ -DCMAKE_TOOLCHAIN_FILE="${ROOTDIR}"/cmake/toolchains/libfuzzer.cmake \ $CMAKE_OPTIONS - if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then - ccache -z - fi + ccache -z make ossfuzz ossfuzz_proto ossfuzz_abiv2 -j 4 - if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then - ccache -s - fi + ccache -s } generate_protobuf_bindings diff --git a/scripts/ci/build_win.sh b/scripts/ci/build_win.sh index 07604c8eb8..bd464647e7 100755 --- a/scripts/ci/build_win.sh +++ b/scripts/ci/build_win.sh @@ -11,29 +11,27 @@ CMAKE_VS_GLOBALS_ARG=() cd "$ROOTDIR" "${ROOTDIR}/scripts/prerelease_suffix.sh" "$prerelease_source" "$CIRCLE_TAG" > prerelease.txt -if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then - export CCACHE_DIR="$HOME/.ccache" - export CCACHE_BASEDIR="$ROOTDIR" - export CCACHE_NOHASHDIR=1 - export CCACHE_COMPILERTYPE=msvc - VSWHERE="${VSWHERE:-C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe}" - VS_INSTALL="$("$VSWHERE" -latest -products "*" \ - -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 \ - -property installationPath | tr -d '\r')" - VCTOOLS_VERSION="$(tr -d '\r' < "$VS_INSTALL/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt")" - CCACHE_COMPILER="$VS_INSTALL/VC/Tools/MSVC/$VCTOOLS_VERSION/bin/Hostx64/x64/cl.exe" - CCACHE_COMPILER="${CCACHE_COMPILER//\\//}" - export CCACHE_COMPILER - PATH="$ROOTDIR/deps/ccache:$(dirname "$CCACHE_COMPILER"):$PATH" - export PATH - # Use a Windows-style path for Visual Studio globals. - WIN_PWD="$(pwd -W)" - # Visual Studio generator doesn't use compiler launchers; use ccache masquerade. - # See https://github.com/ccache/ccache/wiki/MS-Visual-Studio - CMAKE_VS_GLOBALS="CLToolPath=${WIN_PWD}/deps/ccache;UseMultiToolTask=true" - CMAKE_VS_GLOBALS_ARG=(-DCMAKE_VS_GLOBALS="$CMAKE_VS_GLOBALS") - mkdir -p "$CCACHE_DIR" -fi +export CCACHE_DIR="$HOME/.ccache" +export CCACHE_BASEDIR="$ROOTDIR" +export CCACHE_NOHASHDIR=1 +export CCACHE_COMPILERTYPE=msvc +VSWHERE="${VSWHERE:-C:/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe}" +VS_INSTALL="$("$VSWHERE" -latest -products "*" \ + -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 \ + -property installationPath | tr -d '\r')" +VCTOOLS_VERSION="$(tr -d '\r' < "$VS_INSTALL/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt")" +CCACHE_COMPILER="$VS_INSTALL/VC/Tools/MSVC/$VCTOOLS_VERSION/bin/Hostx64/x64/cl.exe" +CCACHE_COMPILER="${CCACHE_COMPILER//\\//}" +export CCACHE_COMPILER +PATH="$ROOTDIR/deps/ccache:$(dirname "$CCACHE_COMPILER"):$PATH" +export PATH +# Use a Windows-style path for Visual Studio globals. +WIN_PWD="$(pwd -W)" +# Visual Studio generator doesn't use compiler launchers; use ccache masquerade. +# See https://github.com/ccache/ccache/wiki/MS-Visual-Studio +CMAKE_VS_GLOBALS="CLToolPath=${WIN_PWD}/deps/ccache;UseMultiToolTask=true" +CMAKE_VS_GLOBALS_ARG=(-DCMAKE_VS_GLOBALS="$CMAKE_VS_GLOBALS") +mkdir -p "$CCACHE_DIR" mkdir -p build/ cd build/ @@ -50,9 +48,7 @@ boost_dir=("${ROOTDIR}/deps/boost/lib/cmake/Boost-"*) "${CMAKE_VS_GLOBALS_ARG[@]}" \ ${CMAKE_OPTIONS:-} \ .. -if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then - ccache -z -fi +ccache -z MSBuild.exe solidity.sln \ -property:Configuration=Release \ -maxCpuCount:10 \ @@ -62,6 +58,4 @@ MSBuild.exe solidity.sln \ -j 10 \ --target install \ --config Release -if [[ "${CCACHE_ENABLED:-}" == "1" ]]; then - ccache -s -fi +ccache -s From 81070d39868353ddada3f6c37ce052061f14ebec Mon Sep 17 00:00:00 2001 From: cui Date: Thu, 29 Jan 2026 16:41:30 +0800 Subject: [PATCH 165/243] perf: opt SimplePeepholeOptimizerMethod (#16426) perf: opt SimplePeepholeOptimizerMethod --- libevmasm/PeepholeOptimiser.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp index 267700e457..7cd7c5e962 100644 --- a/libevmasm/PeepholeOptimiser.cpp +++ b/libevmasm/PeepholeOptimiser.cpp @@ -117,9 +117,10 @@ struct OpPop: SimplePeepholeOptimizerMethod if (_pop == Instruction::POP && _op.type() == Operation) { Instruction instr = _op.instruction(); - if (instructionInfo(instr, langutil::EVMVersion()).ret == 1 && !instructionInfo(instr, langutil::EVMVersion()).sideEffects) + auto const& info = instructionInfo(instr, langutil::EVMVersion()); + if (info.ret == 1 && !info.sideEffects) { - for (int j = 0; j < instructionInfo(instr, langutil::EVMVersion()).args; j++) + for (int j = 0; j < info.args; j++) *_out = {Instruction::POP, _op.debugData()}; return true; } From 04833a570c6f01f2f7f1a9d853321c327a66283b Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Mon, 2 Feb 2026 11:35:17 +0100 Subject: [PATCH 166/243] Update ccache commands to use 'develop' branch instead of 'ci-ccache' --- .circleci/config.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2fd7fe83a5..8e4cd2fbfa 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -93,12 +93,10 @@ commands: restore_ccache_if_allowed: description: "Restore ccache unless on a tag or a skipped branch." parameters: - # TODO: remove the line below - # Before merging this PR we need to use an alternative develop branch to test the functionality, `ci-ccache` skip_branches_regex: description: "Regex of branches that should skip restoring the cache." type: string - default: "^(ci-ccache|breaking)$" + default: "^(develop|breaking)$" steps: # Tags always skip ccache. Selected branches intentionally start from scratch. - when: @@ -112,19 +110,19 @@ commands: steps: - restore_cache: keys: - # Reuse cache produced on ci-ccache (prefix match; restores the most recent). - - v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache- + # Reuse cache produced on develop (prefix match; restores the most recent). + - v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-develop- save_ccache_if_develop: - description: "Save a fresh ccache only on the ci-ccache branch." + description: "Save a fresh ccache only on the develop branch." steps: - # On `ci-ccache` we always generate a fresh cache by never restoring and by saving under a unique key. + # On `develop` we always generate a fresh cache by never restoring and by saving under a unique key. - when: condition: - equal: ["ci-ccache", << pipeline.git.branch >>] + equal: ["develop", << pipeline.git.branch >>] steps: - save_cache: - key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-ci-ccache-{{ .Revision }} + key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-develop-{{ .Revision }} paths: - ~/.ccache run_with_ccache_unless_tag: From 21e284ee234c6f5ae972ca66762ef0f7f1e578bd Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Fri, 19 Dec 2025 23:14:27 -0300 Subject: [PATCH 167/243] evmasm: Remove unused return value from `clearStorageLoop()` --- libsolidity/codegen/ArrayUtils.cpp | 16 +++++++--------- libsolidity/codegen/ArrayUtils.h | 2 +- .../array/copying/array_copy_including_array.sol | 8 ++++---- .../array/delete/delete_storage_array_packed.sol | 4 ++-- .../semanticTests/array/fixed_array_cleanup.sol | 4 ++-- .../array/pop/array_pop_array_transition.sol | 4 ++-- .../storage_boundary_delete_overflow_bug.sol | 4 ++-- ...storage_boundary_struct_array_mixed_types.sol | 4 ++-- .../storage_boundary_struct_array_multislot.sol | 4 ++-- .../structs/struct_delete_storage_with_array.sol | 8 ++++---- .../userDefinedValueType/calldata.sol | 8 ++++---- 11 files changed, 32 insertions(+), 34 deletions(-) diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 841161fa91..80ee9553c0 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -402,7 +402,6 @@ void ArrayUtils::clearArray(ArrayType const& _typeIn) const ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256()); else ArrayUtils(_context).clearStorageLoop(_type.baseType()); - _context << Instruction::POP; } solAssert(_context.stackHeight() == stackHeightStart - 2, ""); } @@ -425,9 +424,10 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const // stack: ref old_length m_context << Instruction::DUP1 << u256(31) << Instruction::LT; evmasm::AssemblyItem longByteArray = m_context.appendConditionalJump(); - m_context << Instruction::POP; + // Short byte array: no data slots to clear, just pop and exit + m_context << Instruction::POP << Instruction::POP; m_context.appendJumpTo(endTag); - m_context.adjustStackOffset(1); // needed because of jump + m_context.adjustStackOffset(2); // the longByteArray path has 2 more items on stack m_context << longByteArray; } // stack: ref old_length @@ -444,7 +444,6 @@ void ArrayUtils::clearDynamicArray(ArrayType const& _type) const clearStorageLoop(_type.baseType()); // cleanup m_context << endTag; - m_context << Instruction::POP; } void ArrayUtils::incrementDynamicArraySize(ArrayType const& _type) const @@ -585,13 +584,13 @@ void ArrayUtils::clearStorageLoop(Type const* _type) const m_context.callLowLevelFunction( "$clearStorageLoop_" + _type->identifier(), 2, - 1, + 0, [_type](CompilerContext& _context) { unsigned stackHeightStart = _context.stackHeight(); if (_type->category() == Type::Category::Mapping) { - _context << Instruction::POP; + _context << Instruction::POP << Instruction::POP; return; } // stack: start_pos slot_count @@ -618,9 +617,8 @@ void ArrayUtils::clearStorageLoop(Type const* _type) const // cleanup _context << zeroLoopEnd; // stack: start_pos slot_count i - _context << Instruction::POP << Instruction::POP; - // stack: start_pos - solAssert(_context.stackHeight() == stackHeightStart - 1, ""); + _context << Instruction::POP << Instruction::POP << Instruction::POP; + solAssert(_context.stackHeight() == stackHeightStart - 2, ""); } ); } diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h index a62df8a7b2..a5eda28f52 100644 --- a/libsolidity/codegen/ArrayUtils.h +++ b/libsolidity/codegen/ArrayUtils.h @@ -74,7 +74,7 @@ class ArrayUtils void popStorageArrayElement(ArrayType const& _type) const; /// Appends a loop that clears a sequence of storage slots of the given type. /// Stack pre: start_ref slot_count - /// Stack post: start_ref + /// Stack post: void clearStorageLoop(Type const* _type) const; /// Converts length to size (number of storage slots or calldata/memory bytes). /// if @a _pad then add padding to multiples of 32 bytes for calldata/memory. diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol b/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol index ba50c7300f..5f1dae910d 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol @@ -36,11 +36,11 @@ contract c { // ---- // test() -> 0x02000202 // gas irOptimized: 4560468 -// gas legacy: 4536566 -// gas legacyOptimized: 4456759 +// gas legacy: 4536539 +// gas legacyOptimized: 4456732 // storageEmpty -> 1 // clear() -> 0, 0 // gas irOptimized: 4488719 -// gas legacy: 4407780 -// gas legacyOptimized: 4385089 +// gas legacy: 4407759 +// gas legacyOptimized: 4385068 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol b/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol index 23b0466e75..cf416c16d1 100644 --- a/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol +++ b/test/libsolidity/semanticTests/array/delete/delete_storage_array_packed.sol @@ -15,5 +15,5 @@ contract C { // ---- // f() -> 0, 0, 0 // gas irOptimized: 88028 -// gas legacy: 88799 -// gas legacyOptimized: 87706 +// gas legacy: 88796 +// gas legacyOptimized: 87704 diff --git a/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol b/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol index af0ec6b7fa..86484ecdef 100644 --- a/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol +++ b/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol @@ -16,6 +16,6 @@ contract c { // storageEmpty -> 0 // clear() -> // gas irOptimized: 97800 -// gas legacy: 97947 -// gas legacyOptimized: 97882 +// gas legacy: 97944 +// gas legacyOptimized: 97880 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol b/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol index 3b96397166..ed4e9b8773 100644 --- a/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol +++ b/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol @@ -24,6 +24,6 @@ contract c { // ---- // test() -> 1, 2, 3 // gas irOptimized: 1828383 -// gas legacy: 1841995 -// gas legacyOptimized: 1821687 +// gas legacy: 1841877 +// gas legacyOptimized: 1821569 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_delete_overflow_bug.sol b/test/libsolidity/semanticTests/storage/storage_boundary_delete_overflow_bug.sol index 3ffb777471..09aae988e5 100644 --- a/test/libsolidity/semanticTests/storage/storage_boundary_delete_overflow_bug.sol +++ b/test/libsolidity/semanticTests/storage/storage_boundary_delete_overflow_bug.sol @@ -72,8 +72,8 @@ contract C { // gas legacyOptimized: 598016 // clearArray() // gas irOptimized: 580378 -// gas legacy: 582976 -// gas legacyOptimized: 581290 +// gas legacy: 582973 +// gas legacyOptimized: 581299 // x() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // gas irOptimized: 604108 // gas legacy: 644983 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_mixed_types.sol b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_mixed_types.sol index 213dee1dae..444f862864 100644 --- a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_mixed_types.sol +++ b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_mixed_types.sol @@ -159,8 +159,8 @@ contract C { // gas legacyOptimized: 112505 // deleteBoundaryArray() // gas irOptimized: 177968 -// gas legacy: 180997 -// gas legacyOptimized: 178175 +// gas legacy: 180995 +// gas legacyOptimized: 178182 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // gas irOptimized: 113169 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_multislot.sol b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_multislot.sol index 55e36cd08e..05ad53cf0b 100644 --- a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_multislot.sol +++ b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_multislot.sol @@ -121,8 +121,8 @@ contract C { // destArray() -> 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 // deleteBoundaryArray() // gas irOptimized: 137824 -// gas legacy: 137973 -// gas legacyOptimized: 137743 +// gas legacy: 137971 +// gas legacyOptimized: 137750 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // destArray() -> 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 diff --git a/test/libsolidity/semanticTests/structs/struct_delete_storage_with_array.sol b/test/libsolidity/semanticTests/structs/struct_delete_storage_with_array.sol index cd49ea27ad..7ae78437e4 100644 --- a/test/libsolidity/semanticTests/structs/struct_delete_storage_with_array.sol +++ b/test/libsolidity/semanticTests/structs/struct_delete_storage_with_array.sol @@ -43,9 +43,9 @@ contract C { // ---- // f() -> // gas irOptimized: 113388 -// gas legacy: 113588 -// gas legacyOptimized: 113104 +// gas legacy: 113583 +// gas legacyOptimized: 113119 // g() -> // gas irOptimized: 118768 -// gas legacy: 118766 -// gas legacyOptimized: 118188 +// gas legacy: 118761 +// gas legacyOptimized: 118203 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/calldata.sol b/test/libsolidity/semanticTests/userDefinedValueType/calldata.sol index 03c6e11bcc..63f1ff1dd1 100644 --- a/test/libsolidity/semanticTests/userDefinedValueType/calldata.sol +++ b/test/libsolidity/semanticTests/userDefinedValueType/calldata.sol @@ -50,12 +50,12 @@ contract C { // ---- // test_f() -> true // gas irOptimized: 122114 -// gas legacy: 126162 -// gas legacyOptimized: 122757 +// gas legacy: 126159 +// gas legacyOptimized: 122742 // test_g() -> true // gas irOptimized: 106248 -// gas legacy: 111824 -// gas legacyOptimized: 106606 +// gas legacy: 111821 +// gas legacyOptimized: 106591 // addresses(uint256): 0 -> 0x18 // addresses(uint256): 1 -> 0x19 // addresses(uint256): 3 -> 0x1b From cd1472967ce453feadbc8c3822559c1ece5acd17 Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Mon, 2 Feb 2026 15:55:24 +0100 Subject: [PATCH 168/243] Remove CCACHE_KEY from cache keys in CircleCI config --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8e4cd2fbfa..70060258a5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -111,7 +111,7 @@ commands: - restore_cache: keys: # Reuse cache produced on develop (prefix match; restores the most recent). - - v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-develop- + - v1-ccache-{{ .Environment.CIRCLE_JOB }}-develop- save_ccache_if_develop: description: "Save a fresh ccache only on the develop branch." @@ -122,7 +122,7 @@ commands: equal: ["develop", << pipeline.git.branch >>] steps: - save_cache: - key: v1-{{ .Environment.CCACHE_KEY }}-ccache-{{ .Environment.CIRCLE_JOB }}-develop-{{ .Revision }} + key: v1-ccache-{{ .Environment.CIRCLE_JOB }}-develop-{{ .Revision }} paths: - ~/.ccache run_with_ccache_unless_tag: From b8e0364bb678ffca2090becdeb6c48b13339abbb Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Mon, 2 Feb 2026 16:13:41 +0100 Subject: [PATCH 169/243] Remove `ensure_ccache_key_unless_tag` and its usages --- .circleci/config.yml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 70060258a5..ff42463992 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -75,21 +75,6 @@ commands: event: release condition: on_success - ensure_ccache_key_unless_tag: - description: "Fail fast when CCACHE_KEY is missing (skipped on tags)." - steps: - - when: - condition: - equal: ["", << pipeline.git.tag >>] - steps: - - run: - name: Check CCACHE_KEY - command: | - if [ -z "${CCACHE_KEY:-}" ]; then - echo "CCACHE_KEY is not set. Please set it in CircleCI environment settings." - exit 1 - fi - restore_ccache_if_allowed: description: "Restore ccache unless on a tag or a skipped branch." parameters: @@ -282,7 +267,6 @@ commands: run_build: steps: - - ensure_ccache_key_unless_tag - restore_ccache_if_allowed # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings - run_with_ccache_unless_tag: @@ -292,7 +276,6 @@ commands: run_build_ossfuzz: steps: - - ensure_ccache_key_unless_tag - restore_ccache_if_allowed # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings - run_with_ccache_unless_tag: @@ -1339,7 +1322,6 @@ jobs: MAKEFLAGS: -j 10 steps: - checkout - - ensure_ccache_key_unless_tag - restore_ccache_if_allowed # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings - run_with_ccache_unless_tag: @@ -1822,7 +1804,6 @@ jobs: key: dependencies-win-{{ arch }}-{{ checksum "scripts/install_deps.ps1" }} paths: - .\deps - - ensure_ccache_key_unless_tag - restore_ccache_if_allowed # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings - run_with_ccache_unless_tag: From eb3dc0c425d5e6b462fb6737fe0e1793df13c87c Mon Sep 17 00:00:00 2001 From: Marco Castignoli Date: Mon, 2 Feb 2026 16:24:44 +0100 Subject: [PATCH 170/243] Fix comment for resetting ccache --- .circleci/config.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ff42463992..27a331cd7c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -268,7 +268,7 @@ commands: run_build: steps: - restore_ccache_if_allowed - # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings + # WARNING! If you edit anything between here and save_cache, remember to invalidate the cache manually by bumping the version - run_with_ccache_unless_tag: step_name: Build command: scripts/ci/build.sh @@ -277,7 +277,7 @@ commands: run_build_ossfuzz: steps: - restore_ccache_if_allowed - # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings + # WARNING! If you edit anything between here and save_cache, remember to invalidate the cache manually by bumping the version - run_with_ccache_unless_tag: step_name: Build_ossfuzz command: scripts/ci/build_ossfuzz.sh @@ -1323,7 +1323,7 @@ jobs: steps: - checkout - restore_ccache_if_allowed - # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings + # WARNING! If you edit anything between here and save_cache, remember to invalidate the cache manually by bumping the version - run_with_ccache_unless_tag: step_name: Build command: scripts/ci/build_emscripten.sh @@ -1805,7 +1805,7 @@ jobs: paths: - .\deps - restore_ccache_if_allowed - # WARNING! If you edit anything between here and save_cache, remember to bump the CCACHE_KEY version in CircleCI environment settings + # WARNING! If you edit anything between here and save_cache, remember to invalidate the cache manually by bumping the version - run_with_ccache_unless_tag: step_name: "Building solidity" command: scripts/ci/build_win.sh From abeaab4409e10ac03f644d71f176e98a7d935177 Mon Sep 17 00:00:00 2001 From: Jacopo Pierantozzi Date: Thu, 29 Jan 2026 12:47:03 +0100 Subject: [PATCH 171/243] Clarify remapping rules in path-resolution documentation --- docs/path-resolution.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/path-resolution.rst b/docs/path-resolution.rst index 492959bc1e..2339a3624c 100644 --- a/docs/path-resolution.rst +++ b/docs/path-resolution.rst @@ -629,9 +629,9 @@ Here are the detailed rules governing the behavior of remappings: #. **At most one remapping is applied to a single import.** - - If multiple remappings match the same source unit name, the one with the longest matching - prefix is chosen. - - If prefixes are identical, the one specified last wins. + - If multiple remappings match the same source unit name, the one with the longest matching context is chosen. + - If contexts are identical, the one with the longest matching prefix is chosen. + - If contexts and prefixes are identical, the one specified last wins. - Remappings do not work on other remappings. For example ``a=b b=c c=d`` will not result in ``a`` being remapped to ``d``. From cf1a3b07aba8c6588bba8bb8827ed0632ed0bbb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 26 Apr 2025 00:54:14 +0200 Subject: [PATCH 172/243] EVMAssemblyTest: Warn about custom expectedExecutionsPerDeployment having no effect --- test/libevmasm/EVMAssemblyTest.cpp | 11 +++++++++++ test/libevmasm/EVMAssemblyTest.h | 1 + 2 files changed, 12 insertions(+) diff --git a/test/libevmasm/EVMAssemblyTest.cpp b/test/libevmasm/EVMAssemblyTest.cpp index 0c1dbb07ed..3bc43b1882 100644 --- a/test/libevmasm/EVMAssemblyTest.cpp +++ b/test/libevmasm/EVMAssemblyTest.cpp @@ -78,10 +78,13 @@ EVMAssemblyTest::EVMAssemblyTest(std::string const& _filename): "none" ); m_optimizerSettings = Assembly::OptimiserSettings::translateSettings(OptimiserSettings::preset(optimizationPreset)); + size_t defaultExpectedExecutionsPerDeployment = m_optimizerSettings.expectedExecutionsPerDeployment; m_optimizerSettings.expectedExecutionsPerDeployment = m_reader.sizetSetting( "optimizer.expectedExecutionsPerDeployment", m_optimizerSettings.expectedExecutionsPerDeployment ); + m_usingDefaultExpectedExecutionsPerDeployment = + m_optimizerSettings.expectedExecutionsPerDeployment == defaultExpectedExecutionsPerDeployment; auto const optimizerComponentSetting = [&](std::string const& _component, bool& _setting) { _setting = m_reader.boolSetting("optimizer." + _component, _setting); @@ -132,6 +135,14 @@ TestCase::TestResult EVMAssemblyTest::run(std::ostream& _stream, std::string con return checkResult(_stream, _linePrefix, _formatted); } + soltestAssert(evmAssemblyStack.evmAssembly()); + if (!m_usingDefaultExpectedExecutionsPerDeployment && evmAssemblyStack.evmAssembly()->numSubs() == 0) + // This is a common mistake. We can't issue a warning here, so let's report it as an error. + BOOST_THROW_EXCEPTION(std::runtime_error( + "The custom value specified for optimizer.expectedExecutionsPerDeployment has no effect " + "on the creation assembly, which is the only assembly in the test." + )); + try { evmAssemblyStack.assemble(); diff --git a/test/libevmasm/EVMAssemblyTest.h b/test/libevmasm/EVMAssemblyTest.h index 9059fd716d..9a276ddeb6 100644 --- a/test/libevmasm/EVMAssemblyTest.h +++ b/test/libevmasm/EVMAssemblyTest.h @@ -75,6 +75,7 @@ class EVMAssemblyTest: public frontend::test::EVMVersionRestrictedTestCase AssemblyFormat m_assemblyFormat{}; std::string m_selectedOutputs; evmasm::Assembly::OptimiserSettings m_optimizerSettings; + bool m_usingDefaultExpectedExecutionsPerDeployment{}; }; } From f0486ba76b6ba4c3ff9b7892026b652036ca8fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 2 Feb 2026 17:54:09 +0100 Subject: [PATCH 173/243] CI: Move constantinople and petersburg EVM runs back from nightly to the main workflow --- .circleci/config.yml | 2 +- .circleci/soltest_all.sh | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 27a331cd7c..55f12f669c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1400,7 +1400,7 @@ jobs: - attach_workspace: at: build - run_soltest_all: - evm_versions: homestead byzantium constantinople petersburg + evm_versions: homestead byzantium - store_test_results: path: test_results/ - store_artifacts_test_results diff --git a/.circleci/soltest_all.sh b/.circleci/soltest_all.sh index effbc33da0..6f8ea14f39 100755 --- a/.circleci/soltest_all.sh +++ b/.circleci/soltest_all.sh @@ -31,7 +31,18 @@ REPODIR="$(realpath "$(dirname "$0")"/..)" # shellcheck source=scripts/common.sh source "${REPODIR}/scripts/common.sh" -DEFAULT_EVM_VALUES=(istanbul berlin london paris shanghai cancun prague osaka) +DEFAULT_EVM_VALUES=( + constantinople + petersburg + istanbul + berlin + london + paris + shanghai + cancun + prague + osaka +) EVMS_WITH_EOF=(osaka) # Deserialize the EVM_VALUES array if it was provided as argument or From fc639436584ddbdbf517cf1e12bbeff3bfda38f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 2 Feb 2026 18:05:34 +0100 Subject: [PATCH 174/243] CI: Replace homestead with prague and petersburg in the nightly run - homestead is useful on PRs while prague and petersburg did not introduce any changes in EVM behavior - Rename the job and explain why we're doing it --- .circleci/config.yml | 19 +++++++++++++++---- .circleci/soltest_all.sh | 3 +-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 55f12f669c..1ca83b526f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -299,7 +299,7 @@ commands: run_soltest_all: parameters: # NOTE: If not specified, soltest_all.sh will use the default values as specified in the script. - # In other words, it will execute for all EVM versions that are not marked as deprecated. + # In other words, it will execute for all EVM versions that are not marked as low priority. evm_versions: description: "List of EVM versions (separated by space)." type: string @@ -1392,7 +1392,7 @@ jobs: - store_artifacts_test_results - matrix_notify_failure_unless_pr - t_ubu_soltest_deprecated_evm_versions: &t_ubu_soltest_deprecated_evm_versions + t_ubu_soltest_low_priority_evm_versions: &t_ubu_soltest_low_priority_evm_versions <<: *base_ubuntu2404_large parallelism: 50 steps: @@ -1400,7 +1400,18 @@ jobs: - attach_workspace: at: build - run_soltest_all: - evm_versions: homestead byzantium + # We test most EVM versions on every PR. The ones below are the exceptions. + # + # We do not test these at all. We were never doing that and no point starting now since + # they are deprecated anyway: + # - tangerineWhistle + # - spuriousDragon + # + # These have a low chance of breaking so we only run them in nightly to save credits: + # - byzantium: deprecated, if it breaks, homestead likely will as well + # - petersburg: no changes compared to constantinople + # - prague: no changes compared to cancun + evm_versions: byzantium petersburg prague - store_test_results: path: test_results/ - store_artifacts_test_results @@ -2259,7 +2270,7 @@ workflows: # Deprecated EVM versions tests - b_ubu: *requires_nothing - - t_ubu_soltest_deprecated_evm_versions: *requires_b_ubu + - t_ubu_soltest_low_priority_evm_versions: *requires_b_ubu # Build in a Docker container (on Alpine Linux) - b_alpine_docker: *requires_nothing diff --git a/.circleci/soltest_all.sh b/.circleci/soltest_all.sh index 6f8ea14f39..3890618594 100755 --- a/.circleci/soltest_all.sh +++ b/.circleci/soltest_all.sh @@ -32,15 +32,14 @@ REPODIR="$(realpath "$(dirname "$0")"/..)" source "${REPODIR}/scripts/common.sh" DEFAULT_EVM_VALUES=( + homestead constantinople - petersburg istanbul berlin london paris shanghai cancun - prague osaka ) EVMS_WITH_EOF=(osaka) From 5e6d9f15793a8bdd712e5b2a507aa08b8e9c3941 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 2 Feb 2026 10:42:18 +0100 Subject: [PATCH 175/243] Add arithmetic concept and arithmetic string parsing utilities --- libsolutil/Common.h | 8 ++++++++ libsolutil/StringUtils.cpp | 2 ++ libsolutil/StringUtils.h | 15 +++++++++++++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/libsolutil/Common.h b/libsolutil/Common.h index c908c13ae2..5ee0e11591 100644 --- a/libsolutil/Common.h +++ b/libsolutil/Common.h @@ -43,6 +43,7 @@ #include #include +#include #include #include #include @@ -52,6 +53,13 @@ namespace solidity { +namespace concepts +{ +/// Concept denoting some arithmetic type. +template +concept arithmetic = std::integral || std::floating_point; +} + // Binary data types. using bytes = std::vector; using bytesRef = util::vector_ref; diff --git a/libsolutil/StringUtils.cpp b/libsolutil/StringUtils.cpp index f5d2bbfb37..50191c083b 100644 --- a/libsolutil/StringUtils.cpp +++ b/libsolutil/StringUtils.cpp @@ -26,6 +26,8 @@ #include +#include + #include #include #include diff --git a/libsolutil/StringUtils.h b/libsolutil/StringUtils.h index 763f1ea64f..87f46252e8 100644 --- a/libsolutil/StringUtils.h +++ b/libsolutil/StringUtils.h @@ -27,11 +27,11 @@ #include #include -#include - #include +#include #include #include +#include #include #include @@ -208,4 +208,15 @@ inline std::string indent(std::string const& _input, bool _indentEmptyLines = fa return prefixLines(_input, " ", !_indentEmptyLines); } +/// Parses an arithmetic value from a `string_view` without allocating and/or throwing. Returns `nullopt` on error. +template +std::optional parseArithmetic(std::string_view const sv) +{ + T result; + auto const errorCondition = std::from_chars(sv.data(), sv.data() + sv.size(), result).ec; + if (errorCondition == std::errc()) + return result; + return std::nullopt; +} + } From 31ef2784308f4c24e722949ad93c4eaf145854db Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 2 Feb 2026 11:09:51 +0100 Subject: [PATCH 176/243] Pull Stack Depth and Offset definitions out of the Stack itself They do not depend on the callbacks template at all. --- libyul/backends/evm/ssa/Stack.h | 52 +++++++++++++++++---------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/libyul/backends/evm/ssa/Stack.h b/libyul/backends/evm/ssa/Stack.h index 20207ebc7e..31f7ac1509 100644 --- a/libyul/backends/evm/ssa/Stack.h +++ b/libyul/backends/evm/ssa/Stack.h @@ -156,6 +156,31 @@ struct NoOpStackManipulationCallbacks }; static_assert(StackManipulationCallbackConcept); +/// Array index into stack from the bottom (offset 0 = bottom). +/// Natural for array-like access and iteration; used when treating the stack as a data structure. +struct StackOffset +{ + explicit constexpr StackOffset(size_t _value) : value(_value) {} + size_t value; + auto operator<=>(StackOffset const&) const = default; +}; +// comparison operations with size_t +constexpr auto operator<=>(StackOffset const lhs, size_t const rhs) noexcept { return lhs.value <=> rhs; } +constexpr auto operator<=>(size_t const lhs, StackOffset const rhs) noexcept { return lhs <=> rhs.value; } + +/// Distance from the stack top (depth 0 = top). +/// Natural for stack operations (SWAP1 = swap with depth 1); used for operations that +/// conceptually work "from the top". +struct StackDepth +{ + explicit constexpr StackDepth(size_t _value) : value(_value) {} + size_t value; + auto operator<=>(StackDepth const&) const = default; +}; +// comparison operations with size_t +constexpr auto operator<=>(StackDepth const lhs, size_t const rhs) noexcept { return lhs.value <=> rhs; } +constexpr auto operator<=>(size_t const lhs, StackDepth const rhs) noexcept { return lhs <=> rhs.value; } + template< StackManipulationCallbackConcept CallbacksType = NoOpStackManipulationCallbacks > @@ -167,31 +192,8 @@ class Stack using Slot = StackSlot; using Data = StackData; - - /// Array index into stack from the bottom (offset 0 = bottom). - /// Natural for array-like access and iteration; used when treating the stack as a data structure. - struct Offset - { - explicit constexpr Offset(size_t _value) : value(_value) {} - size_t value; - auto operator<=>(Offset const&) const = default; - }; - // comparison operations with size_t - friend constexpr auto operator<=>(Offset lhs, size_t rhs) noexcept { return lhs.value <=> rhs; } - friend constexpr auto operator<=>(size_t lhs, Offset rhs) noexcept { return lhs <=> rhs.value; } - - /// Distance from the stack top (depth 0 = top). - /// Natural for stack operations (SWAP1 = swap with depth 1); used for operations that - /// conceptually work "from the top". - struct Depth - { - explicit constexpr Depth(size_t _value) : value(_value) {} - size_t value; - auto operator<=>(Depth const&) const = default; - }; - // comparison operations with size_t - friend constexpr auto operator<=>(Depth lhs, size_t rhs) noexcept { return lhs.value <=> rhs; } - friend constexpr auto operator<=>(size_t lhs, Depth rhs) noexcept { return lhs <=> rhs.value; } + using Depth = StackDepth; + using Offset = StackOffset; Stack( Data& _data, From e1cb8431760d8822728f67151fc9bdefc10abf01 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 2 Feb 2026 11:10:15 +0100 Subject: [PATCH 177/243] Fix dup implementation in ssa Stack abstraction --- libyul/backends/evm/ssa/Stack.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libyul/backends/evm/ssa/Stack.h b/libyul/backends/evm/ssa/Stack.h index 31f7ac1509..a98977380c 100644 --- a/libyul/backends/evm/ssa/Stack.h +++ b/libyul/backends/evm/ssa/Stack.h @@ -240,10 +240,11 @@ class Stack void dup(Depth const& _depth) { dup(depthToOffset(_depth)); } void dup(Offset const& _offset) { - yulAssert(dupReachable(_offset), "Stack too deep"); + auto const depth = offsetToDepth(_offset); + yulAssert(dupReachable(depth), "Stack too deep"); m_data->push_back((*m_data)[_offset.value]); if constexpr (!std::is_same_v) - m_callbacks.dup(offsetToDepth(_offset).value + 1); + m_callbacks.dup(depth.value + 1); } bool dupReachable(Offset const& _offset) const noexcept { return dupReachable(offsetToDepth(_offset)); } From 0d10f892b5ee1175b22bd4683c2d9abc99e1e6df Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 2 Feb 2026 11:15:28 +0100 Subject: [PATCH 178/243] Simplify stack and slot to string functions for ssa Stack --- libyul/backends/evm/ssa/SSACFG.h | 39 ++++++++++++++++++++++++ libyul/backends/evm/ssa/Stack.cpp | 50 ++----------------------------- libyul/backends/evm/ssa/Stack.h | 2 -- 3 files changed, 41 insertions(+), 50 deletions(-) diff --git a/libyul/backends/evm/ssa/SSACFG.h b/libyul/backends/evm/ssa/SSACFG.h index 40537cfcd5..49d5a4e946 100644 --- a/libyul/backends/evm/ssa/SSACFG.h +++ b/libyul/backends/evm/ssa/SSACFG.h @@ -316,3 +316,42 @@ class SSACFG }; } + +template<> +struct fmt::formatter +{ + static auto constexpr parse(format_parse_context& ctx) -> decltype(ctx.begin()) { return ctx.begin(); } + + template + auto format(solidity::yul::ssa::SSACFG::BlockId const& _blockId, FormatContext& _ctx) const -> decltype(_ctx.out()) + { + if (!_blockId.hasValue()) + return fmt::format_to(_ctx.out(), "empty"); + return fmt::format_to(_ctx.out(), "{}", _blockId.value); + } +}; + +template<> +struct fmt::formatter +{ + static auto constexpr parse(format_parse_context& ctx) -> decltype(ctx.begin()) { return ctx.begin(); } + + template + auto format(solidity::yul::ssa::SSACFG::ValueId const& _valueId, FormatContext& _ctx) const -> decltype(_ctx.out()) + { + if (!_valueId.hasValue()) + return fmt::format_to(_ctx.out(), "empty"); + switch (_valueId.kind()) + { + case solidity::yul::ssa::SSACFG::ValueId::Kind::Literal: + return fmt::format_to(_ctx.out(), "lit{}", _valueId.value()); + case solidity::yul::ssa::SSACFG::ValueId::Kind::Variable: + return fmt::format_to(_ctx.out(), "v{}", _valueId.value()); + case solidity::yul::ssa::SSACFG::ValueId::Kind::Phi: + return fmt::format_to(_ctx.out(), "phi{}", _valueId.value()); + case solidity::yul::ssa::SSACFG::ValueId::Kind::Unreachable: + return fmt::format_to(_ctx.out(), "unreachable"); + } + solidity::util::unreachable(); + } +}; diff --git a/libyul/backends/evm/ssa/Stack.cpp b/libyul/backends/evm/ssa/Stack.cpp index 17c8c3f446..59534019da 100644 --- a/libyul/backends/evm/ssa/Stack.cpp +++ b/libyul/backends/evm/ssa/Stack.cpp @@ -18,20 +18,9 @@ #include +#include #include -#include -#include - -namespace -{ -size_t junkTailSize(solidity::yul::ssa::StackData const& _stackData) -{ - auto junkTail = _stackData | ranges::views::take_while([](auto const& slot) { return slot.isJunk(); }); - return static_cast(ranges::distance(junkTail)); -} -} - namespace solidity::yul::ssa { @@ -40,10 +29,7 @@ std::string slotToString(StackSlot const& _slot) switch (_slot.kind()) { case StackSlot::Kind::ValueID: - if (_slot.isLiteralValueID()) - return fmt::format("lit{}", _slot.valueID().value()); - else - return fmt::format("v{}", _slot.valueID().value()); + return fmt::format("{}", _slot.valueID()); case StackSlot::Kind::Junk: return "JUNK"; case StackSlot::Kind::FunctionCallReturnLabel: @@ -54,44 +40,12 @@ std::string slotToString(StackSlot const& _slot) util::unreachable(); } -std::string slotToString(StackSlot const& _slot, SSACFG const& _cfg) -{ - if (_slot.kind() == StackSlot::Kind::ValueID) - return _slot.valueID().str(_cfg); - - return slotToString(_slot); -} - std::string stackToString(StackData const& _stackData) { - auto const numJunk = junkTailSize(_stackData); - if (numJunk > 0) - return fmt::format( - "[JUNK x {}, {}]", - numJunk, - fmt::join(_stackData | ranges::views::drop(numJunk) | ranges::views::transform([&](auto const& _slot) { return slotToString(_slot); }), ", ") - ); - return fmt::format( "[{}]", fmt::join(_stackData | ranges::views::transform([&](auto const& _slot) { return slotToString(_slot); }), ", ") ); } -std::string stackToString(StackData const& _stackData, SSACFG const& _cfg) -{ - auto const numJunk = junkTailSize(_stackData); - if (numJunk > 0) - return fmt::format( - "[JUNK x {}, {}]", - numJunk, - fmt::join(_stackData | ranges::views::drop(numJunk) | ranges::views::transform([&](auto const& _slot) { return slotToString(_slot, _cfg); }), ", ") - ); - - return fmt::format( - "[{}]", - fmt::join(_stackData | ranges::views::transform([&](auto const& _slot) { return slotToString(_slot, _cfg); }), ", ") - ); -} - } diff --git a/libyul/backends/evm/ssa/Stack.h b/libyul/backends/evm/ssa/Stack.h index a98977380c..95ff377e7d 100644 --- a/libyul/backends/evm/ssa/Stack.h +++ b/libyul/backends/evm/ssa/Stack.h @@ -130,8 +130,6 @@ static_assert(std::is_trivial_v, "Want to have no init/cpy overhead") using StackData = std::vector; std::string slotToString(StackSlot const& _slot); -std::string slotToString(StackSlot const& _slot, SSACFG const& _cfg); -std::string stackToString(StackData const& _stackData, SSACFG const& _cfg); std::string stackToString(StackData const& _stackData); template From 02af154f6b61e706ea00460506f36967a78acf2d Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 2 Feb 2026 11:15:51 +0100 Subject: [PATCH 179/243] Add the ssa stack shuffler --- libyul/CMakeLists.txt | 2 + libyul/backends/evm/ssa/StackShuffler.cpp | 165 +++++ libyul/backends/evm/ssa/StackShuffler.h | 853 ++++++++++++++++++++++ 3 files changed, 1020 insertions(+) create mode 100644 libyul/backends/evm/ssa/StackShuffler.cpp create mode 100644 libyul/backends/evm/ssa/StackShuffler.h diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index ff94a3905e..9d0bfe1e2e 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -92,6 +92,8 @@ add_library(yul backends/evm/ssa/SSACFGJsonExporter.h backends/evm/ssa/Stack.cpp backends/evm/ssa/Stack.h + backends/evm/ssa/StackShuffler.cpp + backends/evm/ssa/StackShuffler.h optimiser/ASTCopier.cpp optimiser/ASTCopier.h optimiser/ASTWalker.cpp diff --git a/libyul/backends/evm/ssa/StackShuffler.cpp b/libyul/backends/evm/ssa/StackShuffler.cpp new file mode 100644 index 0000000000..b42d2bb244 --- /dev/null +++ b/libyul/backends/evm/ssa/StackShuffler.cpp @@ -0,0 +1,165 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include +#include + +using namespace solidity::yul::ssa; +using namespace solidity::yul::ssa::detail; + +Target::Target(StackData const& _args, LivenessAnalysis::LivenessData const& _liveOut, std::size_t const _targetSize): + args(_args), + liveOut(_liveOut), + size(_targetSize), + tailSize(_targetSize - _args.size()) +{ + minCount.reserve(_args.size() + _liveOut.size()); + for (auto const& arg: _args) + if (!arg.isJunk()) + ++minCount[arg]; + for (auto const& _liveValueId: _liveOut | ranges::views::keys) + ++minCount[StackSlot::makeValueID(_liveValueId)]; +} + +State::State(StackData const& _stackData, Target const& _target, std::size_t const _reachableStackDepth): + m_stackData(_stackData), + m_target(_target), + m_reachableStackDepth(_reachableStackDepth) +{ + m_histogram.reserve(_stackData.size()); + m_histogramReachable.reserve(_stackData.size()); + m_histogramTail.reserve(_stackData.size()); + m_histogramArgs.reserve(_target.args.size()); + for (auto const& [i, slot]: _stackData | ranges::views::enumerate) + { + // we don't care about junk in the tail + if (i < _target.tailSize && slot.isJunk()) + continue; + // we purposefully skip over junk in the target args as they are always 'correct' + if (i >= _target.tailSize && i < _target.size && _target.args[i - _target.tailSize].isJunk()) + continue; + + ++m_histogram[slot]; + if (i < _target.tailSize) + ++m_histogramTail[slot]; + else if (i < _target.size) + ++m_histogramArgs[slot]; + if (_stackData.size() - i - 1 < _reachableStackDepth) + ++m_histogramReachable[slot]; + } +} + +std::size_t State::size() const +{ + return m_stackData.size(); +} + +std::size_t State::count(StackSlot const& _slot) const +{ + return util::valueOrDefault(m_histogram, _slot, static_cast(0)); +} + +std::size_t State::countInArgs(StackSlot const& _slot) const +{ + return util::valueOrDefault(m_histogramArgs, _slot, static_cast(0)); +} + +std::size_t State::countInTail(StackSlot const& _slot) const +{ + return util::valueOrDefault(m_histogramTail, _slot, static_cast(0)); +} + +std::size_t State::countReachable(StackSlot const& _slot) const +{ + return util::valueOrDefault(m_histogramReachable, _slot, static_cast(0)); +} + +std::size_t State::targetMinCount(StackSlot const& _slot) const +{ + return util::valueOrDefault(m_target.minCount, _slot, static_cast(0)); +} + +std::size_t State::targetArgsCount(StackSlot const& _slot) const +{ + return static_cast(ranges::count(m_target.args, _slot)); +} + +bool State::admissible() const +{ + if (m_target.size != m_stackData.size()) + return false; + + // check if the args are correct + for (size_t i = 0; i < m_target.args.size(); ++i) + if (!isArgsCompatible(StackOffset{m_stackData.size() - i - 1}, StackOffset{m_stackData.size() - i - 1})) + return false; + + // check if the distribution is correct (implying that the stack is admissible as JUNK target args are not counted) + for (auto const& [targetSlot, targetMinCount]: m_target.minCount) + if (count(targetSlot) < targetMinCount) + return false; + return true; +} + +bool State::requiredInArgs(StackSlot const& _slot) const +{ + return ranges::find(m_target.args, _slot) != ranges::end(m_target.args); +} + +bool State::requiredInTail(StackSlot const& _slot) const +{ + return _slot.isValueID() && m_target.liveOut.contains(_slot.valueID()); +} + +bool State::offsetInTargetArgsRegion(StackOffset const _offset) const +{ + return _offset.value >= m_target.size - m_target.args.size() && _offset.value < m_target.size; +} + +StackSlot const& State::targetArg(StackOffset const _targetOffset) const +{ + yulAssert(offsetInTargetArgsRegion(_targetOffset)); + return m_target.args[_targetOffset.value - m_target.tailSize]; +} + +bool State::isArgsCompatible(StackOffset const _sourceOffset, StackOffset const _targetOffset) const +{ + if (_sourceOffset >= m_stackData.size() || !offsetInTargetArgsRegion(_targetOffset)) + return false; + auto const& arg = targetArg(_targetOffset); + return arg.isJunk() || m_stackData[_sourceOffset.value] == arg; +} + +bool State::targetArbitrary(StackOffset const _targetOffset) const +{ + return targetArg(_targetOffset).isJunk(); +} + +bool State::isSourceCompatible(StackOffset const _sourceOffset1, StackOffset const _sourceOffset2) const +{ + return _sourceOffset1 < m_stackData.size() && + _sourceOffset2 < m_stackData.size() && + m_stackData[_sourceOffset1.value] == m_stackData[_sourceOffset2.value]; +} + +Target const& State::target() const +{ + return m_target; +} diff --git a/libyul/backends/evm/ssa/StackShuffler.h b/libyul/backends/evm/ssa/StackShuffler.h new file mode 100644 index 0000000000..983505bb2c --- /dev/null +++ b/libyul/backends/evm/ssa/StackShuffler.h @@ -0,0 +1,853 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include + +#include + +#include + +#include +#include + +namespace solidity::yul::ssa +{ + +namespace detail +{ +/// Contains information about the shuffling target, aggregates over args and live out to +/// provide a lower bound for the slot distribution. +struct Target +{ + Target(StackData const& _args, LivenessAnalysis::LivenessData const& _liveOut, std::size_t _targetSize); + + StackData const& args; + LivenessAnalysis::LivenessData const& liveOut; + std::size_t const size; + std::size_t const tailSize; + boost::container::flat_map minCount; +}; +/// Current state of the stack vs the shuffling target. +class State +{ +public: + State(StackData const& _stackData, Target const& _target, std::size_t _reachableStackDepth); + + std::size_t size() const; + /// How many of `_slot` do we have on stack + std::size_t count(StackSlot const& _slot) const; + /// How many of `_slot` do we have in the args section of the stack + std::size_t countInArgs(StackSlot const& _slot) const; + /// How many of `_slot` do we have in the tail section of the stack + std::size_t countInTail(StackSlot const& _slot) const; + /// How many of `_slot` are (dup) reachable on stack + std::size_t countReachable(StackSlot const& _slot) const; + + /// Obtain the amount of the provided slot that is required for distribution correctness + std::size_t targetMinCount(StackSlot const& _slot) const; + /// Obtain the amount of the provided slot that is required in target args + std::size_t targetArgsCount(StackSlot const& _slot) const; + + /// Checks if the state is compatible with the target + bool admissible() const; + + /// Checks if a particular slot is required in the target args + bool requiredInArgs(StackSlot const& _slot) const; + /// Checks if a particular slot is required in the target tail + bool requiredInTail(StackSlot const& _slot) const; + + /// Checks if an offset is in the target args (bounded from below by tail size, from above by target size) + bool offsetInTargetArgsRegion(StackOffset _offset) const; + /// Retrieves the required argument slot for a specific stack offset + StackSlot const& targetArg(StackOffset _targetOffset) const; + /// Checks the current stack offset is args-compatible with a target stack offset, meaning the target offset is + /// in the target args region and either a wildcard slot (JUNK) or a precise match for the slot at `_sourceOffset` + bool isArgsCompatible(StackOffset _sourceOffset, StackOffset _targetOffset) const; + /// Checks if the slot at `_targetOffset` admits any slot + bool targetArbitrary(StackOffset _targetOffset) const; + /// Yields whether two slots on the current stack are same, respecting stack size limits + bool isSourceCompatible(StackOffset _sourceOffset1, StackOffset _sourceOffset2) const; + /// Shuffling target information + Target const& target() const; + + /// A range of offsets `[argsBegin, argsEnd)` intersected with the current stack size + auto stackArgsRange() const + { + return ranges::views::iota(std::min(m_target.tailSize, m_stackData.size()), std::min(m_target.size, m_stackData.size())) | ranges::views::transform([](auto _i) { return StackOffset{_i}; }); + } + + /// A range of offsets `[0, argsBegin)` intersected with the current stack size + auto stackTailRange() const + { + return ranges::views::iota(0u, std::min(m_target.tailSize, m_stackData.size())) | ranges::views::transform([](auto _i) { return StackOffset{_i}; }); + } + + /// A range of offsets `[0, stackSize)` + auto stackRange() const + { + return ranges::views::iota(0u, m_stackData.size()) | ranges::views::transform([&](auto _i) { return StackOffset{_i}; }); + } + + /// A reversed range of offsets `[stackSize - reachableStackDepth - 1, stackSize)` + auto stackSwapReachableRange() const + { + return ranges::views::iota(0u, std::min(m_stackData.size(), m_reachableStackDepth + 1)) | ranges::views::transform([&](auto _i) { return StackOffset{m_stackData.size() - _i - 1}; }); + } + + /// A reversed range of offsets `[stackSize - reachableStackDepth - 1, stackSize)` + auto stackDupReachableRange() const + { + return ranges::views::iota(0u, std::min(m_stackData.size(), m_reachableStackDepth)) | ranges::views::transform([&](auto _i) { return StackOffset{m_stackData.size() - _i - 1}; }); + } + +private: + StackData const& m_stackData; + Target const& m_target; + std::size_t const m_reachableStackDepth; + boost::container::flat_map m_histogramTail; + boost::container::flat_map m_histogramArgs; + boost::container::flat_map m_histogramReachable; + boost::container::flat_map m_histogram; +}; +} + +template +class StackShuffler +{ + using Slot = StackSlot; + +public: + static void shuffle( + Stack& _stack, + StackData const& _args, + LivenessAnalysis::LivenessData const& _liveOut, + std::size_t _targetStackSize + ) + { + detail::Target const target(_args, _liveOut, _targetStackSize); + yulAssert(_liveOut.size() <= target.size, "not enough tail space"); + { + // check that all required values are on stack + detail::State const state(_stack.data(), target, ReachableStackDepth); + for (auto const& liveVariable: _liveOut | ranges::views::keys | ranges::views::transform(Slot::makeValueID)) + yulAssert(_stack.canBeFreelyGenerated(liveVariable) || ranges::find(_stack.data(), liveVariable) != ranges::end(_stack.data())); + for (auto const& arg: _args) + yulAssert(_stack.canBeFreelyGenerated(arg) || ranges::find(_stack.data(), arg) != ranges::end(_stack.data())); + } + + static std::size_t constexpr maxIterations = 1000; + std::size_t i = 0; + while (i < maxIterations) + { + detail::State const state(_stack.data(), target, ReachableStackDepth); + if (!shuffleStep(_stack, state)) + { + yulAssert(state.admissible()); + break; + } + ++i; + } + + yulAssert(i < maxIterations, fmt::format("Maximum iterations reached on {}", stackToString(_stack.data()))); + } + +private: + /// Make a local step in stack space that should bring us closer to the target. Returns true if more shuffling + /// is required, returns false if finished. + static bool shuffleStep(Stack& _stack, detail::State const& _state) + { + // if the stack is too large, we try to shrink it + if (_stack.size() > _state.target().size) + { + if (shrinkStack(_stack, _state)) + return true; + // couldn't shrink to required size, need to spill to memory or increase target size + yulAssert(false, "stack too deep"); + } + yulAssert(_stack.size() <= _state.target().size, "I1 violated: Stack size too large"); + + // after this, all current slots are either in acceptable positions or at least dup-reachable + if (auto unreachableOffset = allNecessarySlotsReachableOrFinal(_stack, _state)) + { + // !allNecessarySlotsReachableOrFinal(ops) ≡ ¬(∀s: reachable(s) ∨ final(s)) ≡ ∃s: ¬reachable(s) ∧ ¬final(s) + if (shrinkStack(_stack, _state)) + return true; + + yulAssert(false, fmt::format("stack too deep, couldn't reach offset {}", unreachableOffset->value)); + } + + // this will either grow the tail as needed, swap down something from args that needs to be in the tail, + // or return false when there's nothing to be done + if (fixTailSlot(_stack, _state)) + return true; + + // fixing tail slot fills up the tail so that now the stack must reach into the args region but also not + // exceed it as per our first invariant + yulAssert(_state.target().tailSize <= _stack.size() && _stack.size() <= _state.target().size); + + // if the stack reaches into the args region try fixing a slot in there until there's nothing left to be fixed + // within the target size constraints + if (fixArgsSlot(_stack, _state)) + return true; + + // if there are no args, we should be done now + if (_state.target().args.empty()) + return false; + yulAssert(_stack.size() == _state.target().size); + + // check whether we are done + if (_state.admissible()) + return false; + + // We couldn't improve the args tail or args situation, and we are not admissible yet, so try to reduce the + // stack size and pop something that we don't need so we make space to dup/push stuff within target size + if (shrinkStack(_stack, _state)) + return true; + + yulAssert(false, "reached final and forbidden state"); + } + + /// Select an optimal slot to dup based on liveness analysis. + /// Prioritizes slots that have the highest deficit with respect to liveOut counts. + static std::optional selectOptimalSlotToDup(Stack const& _stack, detail::State const& _state) + { + std::optional bestSlot; + int bestDeficit = 0; // Only consider positive deficits + + // Iterate through all slots on the stack that can be DUPed + for (StackOffset offset: _state.stackDupReachableRange() | ranges::views::reverse) + { + Slot const& slot = _stack[offset]; + + // Skip junk slots + if (slot.isJunk()) + continue; + + // Calculate deficit: how many more of this slot do we need? + // Uses the deficit of slots which we need to produce more of based on usage counts in liveness. + // Prioritizes slots that need more copies to be consumed down the line. + int currentCount = static_cast(_state.count(slot)); + + int liveOutCount = 0; + if (slot.isValueID() && _state.target().liveOut.contains(slot.valueID())) + liveOutCount = static_cast(_state.target().liveOut.count(slot.valueID())); + int deficit = liveOutCount - currentCount; + + // Update best if this deficit is higher + if (deficit > bestDeficit) + { + bestDeficit = deficit; + bestSlot = _stack.offsetToDepth(offset); + } + } + + return bestSlot; + } + + /// Dups the deepest reachable slot in the tail that is required in args + static bool dupDeepestRelevantTailSlot(Stack& _stack, detail::State const& _state) + { + // dup up the deepest slot that is required in args (or compress if unreachable) + for (StackOffset offset: _state.stackRange()) + { + // if we need the slot in args and there's no slot of the same kind further up + if ( + _state.requiredInArgs(_stack[offset]) && + ranges::find(ranges::begin(_stack) + static_cast(offset.value) + 1, ranges::end(_stack), _stack[offset]) == ranges::end(_stack) + ) + { + // dup if we can + if (_stack.dupReachable(offset)) + { + _stack.dup(offset); + return true; + } + + // try to compress + if (shrinkStack(_stack, _state)) + return true; + + yulAssert(false, fmt::format("Stack too deep: can't reach slot at offset {}", offset.value)); + } + } + return false; + } + + /// If dupping an ideal slot causes a slot that will still be required to become unreachable, then dup + /// the latter slot first + static bool dupDeepSlotIfRequired(Stack& _stack, detail::State const& _state) + { + // Check if the stack is large enough for anything to potentially become unreachable. + if (_stack.size() < ReachableStackDepth - 1) + return false; + // Check whether any deep slot might still be needed later (i.e. we still need to reach it with a DUP or SWAP). + for (StackOffset sourceOffset{0u}; sourceOffset < _stack.size() - (ReachableStackDepth - 1); ++sourceOffset.value) + { + // This slot needs to be moved into args and there is no tail slot of the same kind further up in the stack. + auto const& endangeredSlot = _stack[sourceOffset]; + // no need to dup deep junk + if (endangeredSlot.isJunk()) + continue; + // check if we have more of the same slot further up in the stack + bool const neededInArgs = _state.targetArgsCount(endangeredSlot) > _state.countInArgs(endangeredSlot); + bool const needMore = _state.targetMinCount(endangeredSlot) > _state.count(endangeredSlot); + if (neededInArgs || needMore) + { + // if we ever need more of a slot then this can only happen if it is something we require + // in the arguments + yulAssert(_state.requiredInArgs(endangeredSlot)); + + auto const haveMoreAbove = [&] + { + for (StackOffset offset{sourceOffset.value + 1}; offset < _stack.size(); ++offset.value) + { + if (_stack[offset] == endangeredSlot) + return true; + } + return false; + }(); + + // if we have more of the same further above, just unconditionally skip this one + if (haveMoreAbove) + continue; + + if (_stack.dupReachable(sourceOffset)) + { + + // if we can safely swap the current stack top with the endangered slot, we do that instead of DUP + if ( + !_state.isArgsCompatible(sourceOffset, sourceOffset) && // the offset isn't already in the right position wrt args + !_state.isArgsCompatible(StackOffset{_stack.size() - 1}, StackOffset{_stack.size() - 1}) && // the top isn't already in the right position wrt args + ( + !_state.requiredInArgs(_stack.top()) || // current top can go into tail, ie it's not required as arg or + _state.countReachable(_stack.top()) > 1 // there's more of it in reachable stack depth + ) && + ( + _state.target().tailSize <= sourceOffset.value || // sourceOffset not in tail + !_state.requiredInTail(endangeredSlot) || // we're in tail but sourceOffset not needed in tail + (_state.countInTail(endangeredSlot) > 1 && _state.requiredInTail(endangeredSlot)) // swapping source offset away from tail doesn't decrease tail correctness + ) + ) + { + // top can go into the tail bit, swap it down + _stack.swap(sourceOffset); + return true; + } + else + { + // we need more of the slot that is about to go out of reach, dup it + _stack.dup(sourceOffset); + return true; + } + } + else + { + std::optional depth = _stack.findSlotDepth(_stack[sourceOffset]); + yulAssert(depth); + // if there's a shallower slot with the same info that is reachable, skip this one + if (*depth < _stack.offsetToDepth(sourceOffset)) + continue; + + // the slot we need something in the args region of is unreachable, try compressing the stack, + // first looking at the top + if (shrinkStack(_stack, _state)) + return true; + + yulAssert(false, fmt::format("Stack too deep, can't reach slot at depth {}", depth->value)); + } + } + } + return false; + } + + /// Tries to fix a slot in the args section of the stack + static bool fixArgsSlot(Stack& _stack, detail::State const& _state) + { + yulAssert(_stack.size() <= _state.target().size, "this method assumes that the stack isn't too large"); + if (_stack.size() < _state.target().tailSize) + return false; + + // if we have at least one slot in the args section, try to fix something there + if (!_stack.empty() && _stack.size() >= _state.target().tailSize) + { + StackOffset const stackTop{_stack.size() - 1}; + // if the stack top isn't where it likes to be right now, try to put it somewhere more sensible + if (!_state.isArgsCompatible(stackTop, stackTop)) + { + // if the stack top should go into the tail but isn't there yet and we have enough of it in args + if ( + _state.requiredInTail(_stack[stackTop]) && + _state.countInTail(_stack[stackTop]) == 0 && + _state.countInArgs(_stack[stackTop]) > _state.targetArgsCount(_stack[stackTop]) + ) + { + // try swapping it with something in the tail that also fixes the top + for (StackOffset offset: _state.stackTailRange()) + if (_stack.swapReachable(offset) && _state.isArgsCompatible(offset, stackTop)) + { + _stack.swap(offset); + return true; + } + // otherwise try swapping it with something that needs to go into args + for (StackOffset offset: _state.stackTailRange()) + if (_stack.swapReachable(offset) && _state.countInArgs(_stack[offset]) < _state.targetArgsCount(_stack[offset])) + { + _stack.swap(offset); + return true; + } + // otherwise try swapping it with something that can be popped + for (StackOffset offset: _state.stackTailRange()) + if (_stack.swapReachable(offset) && _stack.canBeFreelyGenerated(_stack[offset]) && !_stack[offset].isLiteralValueID()) + { + _stack.swap(offset); + return true; + } + // otherwise try swapping it with a literal + for (StackOffset offset: _state.stackTailRange()) + if (_stack.swapReachable(offset) && _stack[offset].isLiteralValueID()) + { + _stack.swap(offset); + return true; + } + } + // try finding a slot that is compatible with the top and also admits the current top: + // - could be that the top slot is used elsewhere in the args (exclude junk) + // - could be that the top slot is something that is only required in the tail + for (StackOffset offset: _state.stackArgsRange()) + if ( + offset != stackTop && + _stack[offset] != _stack[stackTop] && // don't swap identical values (no-op) + _stack.swapReachable(offset) && + _state.isArgsCompatible(offset, stackTop) && + _state.isArgsCompatible(stackTop, offset) && + !_state.targetArbitrary(offset) + ) + { + _stack.swap(offset); + return true; + } + + // try finding a slot in args that wants to have the top, swap that + for (StackOffset offset: _state.stackArgsRange()) + if ( + offset != stackTop && + _stack[offset] != _stack[stackTop] && // don't swap identical values (no-op) + _stack.swapReachable(offset) && + !_state.isArgsCompatible(offset, offset) && + _state.isArgsCompatible(stackTop, offset) + ) + { + _stack.swap(offset); + return true; + } + + // try swapping top with a tail slot that has what we need at top + for (StackOffset tailOffset: _state.stackTailRange()) + if ( + _stack.swapReachable(tailOffset) && + _state.isArgsCompatible(tailOffset, stackTop) && + (!_state.requiredInTail(_stack[tailOffset]) || _state.countInTail(_stack[tailOffset]) > 1) && + // current top can safely go to tail (not needed in args, or we have excess) + ( + !_state.requiredInArgs(_stack[stackTop]) || + _state.countInArgs(_stack[stackTop]) > _state.targetArgsCount(_stack[stackTop]) + ) + ) + { + _stack.swap(tailOffset); + return true; + } + } + + // swap up any slot in args that is out of position and has a slot available in args that it can occupy + for (StackOffset offset: _state.stackArgsRange()) + { + bool const reachable = _stack.swapReachable(offset); + bool const identical = _state.isArgsCompatible(offset, stackTop) && !_state.targetArbitrary(stackTop); + if ( + reachable && + !identical && // we wouldn't just be swapping identical things + ( + !_state.isArgsCompatible(offset, offset) || // the slot at offset isn't final + (_state.targetArbitrary(offset) && !_stack.slot(offset).isJunk()) // or the target is arbitrary and the current slot isn't already junk + ) + ) + { + // for each `targetOffset` in target args, see if we can't swap the out of position `offset` to `targetOffset` + for (StackOffset targetOffset: _state.stackArgsRange()) + if ( + targetOffset != offset && // we shouldn't be looking at the very same offset + _stack.swapReachable(targetOffset) && // the target offset should be within reach + _state.isArgsCompatible(offset, targetOffset) && // we can put offset -> targetOffset + !_state.isArgsCompatible(targetOffset, targetOffset) // targetOffset doesn't like where it is + ) + { + if (offset != stackTop) + // swap up slot at offset + _stack.swap(offset); + // bring slot at offset into fixed position + _stack.swap(targetOffset); + return true; + } + } + } + } + + // dup up whatever is missing + if (_stack.size() < _state.target().size) + { + if (dupDeepSlotIfRequired(_stack, _state)) + return true; + + { + StackOffset const targetOffset{_stack.size()}; + if (_state.count(_state.targetArg(targetOffset)) < _state.targetMinCount(_state.targetArg(targetOffset))) + { + auto const sourceDepth = _stack.findSlotDepth(_state.targetArg(targetOffset)); + if (!sourceDepth) + { + _stack.push(_state.targetArg(targetOffset)); + return true; + } + + if (!_stack.dupReachable(*sourceDepth)) + yulAssert(false, fmt::format("todo: stack too deep handling, couldn't dup up arg {}", slotToString(_state.targetArg(_stack.depthToOffset(*sourceDepth))))); + _stack.dup(*sourceDepth); + return true; + } + } + + // if we can't directly produce targetOffset, take the deepest arg that we don't have enough of and dup/push that + // First, prioritize duping args that are on the stack over pushing freely-generatable ones + for (StackOffset offset{_state.target().tailSize}; offset < _state.target().size; ++offset.value) + { + Slot const& arg = _state.targetArg(offset); + if (!arg.isJunk() && (_state.count(arg) < _state.targetMinCount(arg) || _state.countInArgs(arg) < _state.targetArgsCount(arg))) + { + if (auto sourceDepth = _stack.findSlotDepth(arg)) + { + if (_stack.dupReachable(*sourceDepth)) + { + _stack.dup(*sourceDepth); + return true; + } + yulAssert(false, "stack too deep handling"); + } + yulAssert(_stack.canBeFreelyGenerated(arg)); + _stack.push(arg); + return true; + } + } + + // Try to dup the optimal slot based on liveness analysis + if (auto slotToDup = selectOptimalSlotToDup(_stack, _state)) + _stack.dup(*slotToDup); + else + // If no suitable slot found, push junk + _stack.push(Slot::makeJunk()); + return true; + } + + // if we're at size and have to push or dup something to satisfy args + if (_stack.size() == _state.target().size) + { + for (auto const& arg: _state.target().args) + if (_state.count(arg) < _state.targetMinCount(arg)) + { + // we have asserted that all relevant slots are reachable or final, so the arg must either be + // within dup-reach or we can just push it + if (auto depth = _stack.findSlotDepth(arg)) + { + yulAssert(depth->value == 0 || _stack.swapReachable(*depth)); + // if we can't outright dup the slot, let's shrink the stack first + if (!_stack.dupReachable(*depth)) + { + yulAssert(shrinkStack(_stack, _state), "stack too deep, need to spill arg to memory"); + return true; + } + _stack.dup(*depth); + return true; + } + else + { + yulAssert(_stack.canBeFreelyGenerated(arg)); + if (!dupDeepSlotIfRequired(_stack, _state)) + _stack.push(arg); + return true; + } + } + } + return false; + } + + /// Grows the tail if too small, otherwise tries swapping something down from args if its required in tail but not + /// there yet. + static bool fixTailSlot(Stack& _stack, detail::State const& _state) + { + yulAssert(_stack.size() <= _state.target().size, "this method assumes that the stack isn't exceeding target size"); + for (StackOffset offset: _state.stackArgsRange() | ranges::views::reverse) + { + Slot const& slotAtOffset = _stack[offset]; + if ( + _state.requiredInTail(slotAtOffset) && // if we need the slot in tail + _state.countInTail(slotAtOffset) == 0 // if we don't have the slot in tail right now + ) + { + // If we don't have enough copies of this slot, dup first instead of swapping. + if (_state.count(slotAtOffset) < _state.targetMinCount(slotAtOffset)) + { + if (_stack.dupReachable(offset)) + { + _stack.dup(offset); + return true; + } + } + + // find the lowest swappable slot in tail that needs to go to args, swap + for (StackOffset tailOffset: _state.stackTailRange()) + { + auto const& slotAtTailOffset = _stack[tailOffset]; + if ( + _stack.swapReachable(tailOffset) && // we can swap that deep + (!_state.requiredInTail(slotAtTailOffset) || _state.countInTail(slotAtTailOffset) > 1) && // dont need it in tail or it's available more than once + _state.requiredInArgs(slotAtTailOffset) && // we need the tail offset slot in args + _state.targetArgsCount(slotAtTailOffset) > _state.countInArgs(slotAtTailOffset) // we don't already have enough of it in args + ) + { + // bring up offset slot if necessary + if (offset != StackOffset{_stack.size() - 1}) + _stack.swap(offset); + // swap offset slot down into tail + _stack.swap(tailOffset); + return true; + } + } + // find the lowest swappable slot in tail that can be popped but is no literal, swap + for (StackOffset tailOffset: _state.stackTailRange()) + if ( + _stack.swapReachable(tailOffset) && + _stack.canBeFreelyGenerated(_stack[tailOffset]) && + !_stack[tailOffset].isLiteralValueID() + ) + { + // bring up offset slot if necessary + if (offset != StackOffset{_stack.size() - 1}) + _stack.swap(offset); + // swap offset slot down into tail + _stack.swap(tailOffset); + return true; + } + // find the lowest swappable slot in tail that is a literal, swap + for (StackOffset tailOffset: _state.stackTailRange()) + if ( + _stack.swapReachable(tailOffset) && + _stack[tailOffset].isLiteralValueID() + ) + { + // bring up offset slot if necessary + if (offset != StackOffset{_stack.size() - 1}) + _stack.swap(offset); + // swap offset slot down into tail + _stack.swap(tailOffset); + return true; + } + // we needed to bring the slot into tail but couldn't, not enough stack target space -> spill to memory + yulAssert(false, "stack too deep: couldn't swap args slot into tail without moving something else out that is required there"); + } + } + + if (_stack.size() < _state.target().tailSize) + { + // if something is on the verge of going out of scope by duping something, dup that first + if (dupDeepSlotIfRequired(_stack, _state)) + return true; + + // dup up the deepest slot that needs to go into args so we avoid having to fish it back up later + if (dupDeepestRelevantTailSlot(_stack, _state)) + return true; + + // Try to dup the optimal slot based on liveness analysis + if (auto slotToDup = selectOptimalSlotToDup(_stack, _state)) + _stack.dup(*slotToDup); + else + // If no suitable slot found, push junk + _stack.push(Slot::makeJunk()); + return true; + } + return false; + } + + /// Tries to compress the stack + static bool shrinkStack(Stack& _stack, detail::State const& _state) + { + yulAssert(!_stack.empty(), "Stack is empty, can't shrink"); + + StackOffset const stackTop{_stack.size() - 1}; + // pop top if it is junk (ie actual junk, not in args, not in live out) + if ( + _stack[stackTop].isJunk() || + (!_state.requiredInArgs(_stack[stackTop]) && !_state.requiredInTail(_stack[stackTop])) + ) + { + _stack.pop(); + return true; + } + + // swap top to suitable position, prioritizing args region + { + if (_state.requiredInArgs(_stack[stackTop])) + { + for (StackOffset argsOffset: _state.stackArgsRange()) + if ( + _stack[argsOffset] != _stack[stackTop] && // don't swap identical values (no-op) + _stack.swapReachable(argsOffset) && + _state.isArgsCompatible(stackTop, argsOffset) && + !_state.isArgsCompatible(argsOffset, argsOffset) + ) + { + _stack.swap(argsOffset); + return true; + } + } + // we don't need it in args but in tail + if (!_state.requiredInArgs(_stack[stackTop]) && _state.requiredInTail(_stack[stackTop])) + { + // pop when at least one of the two conditions is fulfilled + // - the top slot is contained in tail, and we're in args or excess region + // - there's more than one in tail + if ( + ( + _state.countInTail(_stack[stackTop]) >= 1 && + (_state.offsetInTargetArgsRegion(stackTop) || _stack.size() > _state.target().size) + ) || _state.countInTail(_stack[stackTop]) > 1 + ) + { + _stack.pop(); + return true; + } + + // if we need it down there, try to swap down + for (StackOffset tailOffset: _state.stackTailRange() | ranges::views::reverse) + if ( + _stack[tailOffset] != _stack[stackTop] && // don't swap identical values (no-op) + _stack.swapReachable(tailOffset) && // we can reach the offset + !(_state.requiredInTail(_stack[tailOffset]) && _state.countInTail(_stack[tailOffset]) <= 1) // it's okay to swap the tail offset out + ) + { + _stack.swap(tailOffset); + return true; + } + } + } + // pop junk (but not if JUNK is exactly what's needed at that position) + for (StackOffset offset: _state.stackSwapReachableRange()) + if (_stack[offset].isJunk() && !_state.isArgsCompatible(offset, offset)) + { + if (offset != stackTop && _stack[offset] != _stack[stackTop]) + _stack.swap(offset); + _stack.pop(); + return true; + } + + // pop something that can be freely generated except for literals + // (but not if it's already in a compatible position) + for (StackOffset offset: _state.stackSwapReachableRange()) + if ( + _stack.canBeFreelyGenerated(_stack[offset]) && + !_stack[offset].isLiteralValueID() && + !_state.isArgsCompatible(offset, offset) + ) + { + if (offset != stackTop && _stack[offset] != _stack[stackTop]) + _stack.swap(offset); + _stack.pop(); + return true; + } + + // pop anything that isn't in position and we have more than one of + for (StackOffset offset: _state.stackSwapReachableRange()) + if (_state.count(_stack[offset]) > _state.targetMinCount(_stack[offset])) + { + if (offset != stackTop && _stack[offset] != _stack[stackTop]) + _stack.swap(offset); + _stack.pop(); + return true; + } + // pop anything that can be freely generated + for (StackOffset offset: _state.stackSwapReachableRange()) + if (_stack.canBeFreelyGenerated(_stack[offset])) + { + if (offset != stackTop && _stack[offset] != _stack[stackTop]) + _stack.swap(offset); + _stack.pop(); + return true; + } + return false; + } + + /// Checks if all current slots are either in a position that is compatible with the target or, if not, are dup-reachable. + static std::optional allNecessarySlotsReachableOrFinal(Stack const& _stack, detail::State const& _state) + { + // check that args are either in position or reachable + for (StackOffset offset{_state.target().tailSize}; offset < _state.target().size; ++offset.value) + if ( + offset < _state.size() && + !_state.isArgsCompatible(offset, offset) + ) + { + // find first occurrence of the slot + std::optional depth = _stack.findSlotDepth(_state.targetArg(offset)); + + if (!depth) + { + // if there is no occurrence of the slot anywhere, we must be able to freely generate it + yulAssert(_stack.canBeFreelyGenerated(_state.targetArg(offset))); + } + else + { + if (!_stack.swapReachable(*depth)) + return _stack.depthToOffset(*depth); + } + } + // distribution check: all we have to dup can be duped + for (StackOffset const offset: _state.stackRange()) + { + auto const& slotAtOffset = _stack[offset]; + // we don't have enough of the slot + if ( + _state.count(slotAtOffset) < _state.targetMinCount(slotAtOffset) && + !_stack.dupReachable(offset) + ) + { + // find first occurrence of the slot + std::optional depth = _stack.findSlotDepth(slotAtOffset); + // it must exist + yulAssert(depth); + if (!_stack.dupReachable(*depth)) + return _stack.depthToOffset(*depth); + } + } + + return std::nullopt; + } +}; + +} From b2cb2af6f21841f3cfe52abaf3484bcce7afff05 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 2 Feb 2026 11:51:16 +0100 Subject: [PATCH 180/243] Add stack shuffler test --- test/CMakeLists.txt | 2 + test/InteractiveTests.h | 3 + test/libyul/ssa/StackShufflerTest.cpp | 453 ++++++++++++++++++++++++++ test/libyul/ssa/StackShufflerTest.h | 36 ++ test/tools/CMakeLists.txt | 2 + 5 files changed, 496 insertions(+) create mode 100644 test/libyul/ssa/StackShufflerTest.cpp create mode 100644 test/libyul/ssa/StackShufflerTest.h diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b0cd991f73..f36ae41476 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -173,6 +173,8 @@ set(libyul_sources libyul/YulOptimizerTest.h libyul/YulOptimizerTestCommon.cpp libyul/YulOptimizerTestCommon.h + libyul/ssa/StackShufflerTest.cpp + libyul/ssa/StackShufflerTest.h ) detect_stray_source_files("${libyul_sources}" "libyul/") diff --git a/test/InteractiveTests.h b/test/InteractiveTests.h index 107a6aae0b..ff2725471c 100644 --- a/test/InteractiveTests.h +++ b/test/InteractiveTests.h @@ -30,6 +30,8 @@ #include #include #include + +#include #include #include #include @@ -72,6 +74,7 @@ Testsuite const g_interactiveTestsuites[] = { {"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create}, {"Yul Control Flow Graph", "libyul", "yulControlFlowGraph", false, false, &yul::test::ControlFlowGraphTest::create}, {"Yul SSA Control Flow Graph", "libyul", "yulSSAControlFlowGraph", false, false, &yul::test::SSAControlFlowGraphTest::create}, + {"Yul SSA StackShuffling", "libyul", "ssa/stackShuffler", false, false, &yul::ssa::test::ShufflingTest::create}, {"Yul Stack Layout", "libyul", "yulStackLayout", false, false, &yul::test::StackLayoutGeneratorTest::create}, {"Yul Stack Shuffling", "libyul", "yulStackShuffling", false, false, &yul::test::StackShufflingTest::create}, {"Control Flow Side Effects", "libyul", "controlFlowSideEffects", false, false, &yul::test::ControlFlowSideEffectsTest::create}, diff --git a/test/libyul/ssa/StackShufflerTest.cpp b/test/libyul/ssa/StackShufflerTest.cpp new file mode 100644 index 0000000000..3c8c964e7e --- /dev/null +++ b/test/libyul/ssa/StackShufflerTest.cpp @@ -0,0 +1,453 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace solidity; +using namespace solidity::yul::ssa; +using namespace solidity::yul::ssa::test; + +namespace +{ +std::string_view constexpr parserKeyInitialStack {"initial"}; +std::string_view constexpr parserKeyStackTop {"targetStackTop"}; +std::string_view constexpr parserKeyTailSet {"targetStackTailSet"}; +std::string_view constexpr parserKeyStackSize {"targetStackSize"}; + +using Liveness = LivenessAnalysis::LivenessData; +using Slot = StackSlot; +using ValueId = SSACFG::ValueId; +struct StackManipulationCallbacks +{ + void swap(size_t _depth) const + { + if (hook) + (*hook)(fmt::format("SWAP{}", _depth)); + } + void dup(size_t const _depth) const + { + if (hook) + (*hook)(fmt::format("DUP{}", _depth)); + } + void push(Slot const& _slot) const + { + if (hook) + (*hook)(fmt::format("PUSH {}", slotToString(_slot))); + } + void pop() const + { + if (hook) + (*hook)("POP"); + } + + std::optional> hook = std::nullopt; +}; +using TestStack = Stack; + +/// removes leading and trailing whitespace from a string view +std::string_view trim(std::string_view s) +{ + s.remove_prefix(std::min(s.find_first_not_of(" \t\r\v\n"), s.size())); + s.remove_suffix(std::min(s.size() - s.find_last_not_of(" \t\r\v\n") - 1, s.size())); + return s; +} + +/// Parse a value ID token like "v172", "phi109", "lit7", or "JUNK". +Slot parseSlot(std::string_view token) +{ + if (token == "JUNK") + return Slot::makeJunk(); + + if (token.starts_with("v")) + { + if (auto const num = util::parseArithmetic(token.substr(1))) + return Slot::makeValueID(ValueId::makeVariable(*num)); + throw std::runtime_error(fmt::format("Couldn't parse variable token: {}", token)); + } + + if (token.starts_with("phi")) + { + if (auto const num = util::parseArithmetic(token.substr(3))) + return Slot::makeValueID(ValueId::makePhi(*num)); + throw std::runtime_error(fmt::format("Couldn't parse phi token: {}", token)); + } + + if (token.starts_with("lit")) + { + if (auto const num = util::parseArithmetic(token.substr(3))) + return Slot::makeValueID(ValueId::makeLiteral(*num)); + throw std::runtime_error(fmt::format("Couldn't parse literal token: {}", token)); + } + throw std::runtime_error(fmt::format("Unknown token: {}", token)); +} + +/// Parse a string like "[v172, phi109, lit7, JUNK]" into Stack::Data +TestStack::Data parseSlots(std::string_view _input, char const brackBegin = '[', char const brackEnd = ']') +{ + TestStack::Data result; + + // trim and remove brackets + { + _input = trim(_input); + yulAssert(_input.starts_with(brackBegin)); + _input.remove_prefix(1); + yulAssert(_input.ends_with(brackEnd)); + _input.remove_suffix(1); + } + + for (auto&& slotToken: ranges::views::split(_input, ',')) + { + auto const slotTokenBegin = ranges::begin(slotToken); + auto const slotTokenEnd = ranges::end(slotToken); + + std::string_view token; + if(slotTokenBegin != slotTokenEnd) + token = {&*slotTokenBegin, static_cast(ranges::distance(slotTokenBegin, slotTokenEnd))}; + token = trim(token); + yulAssert(!token.empty(), "Empty token."); + result.push_back(parseSlot(token)); + } + return result; +} + +/// Parse liveness like "{phi109, phi150, v172}" +/// Returns Liveness with reference count 1 for each value +Liveness parseLiveness(std::string_view _input) +{ + auto const slots = parseSlots(_input, '{', '}'); + std::vector> liveCounts; + liveCounts.reserve(slots.size()); + for (auto const& slot: slots) + { + yulAssert(slot.isValueID(), "Only value IDs are permitted in liveness definition."); + liveCounts.emplace_back(slot.valueID(), 1); + } + return {liveCounts.begin(), liveCounts.end()}; +} + +struct ShuffleTestInput +{ + std::optional initial; + std::optional targetStackTop; + std::optional targetStackTailSet; + std::optional targetStackSize; + + bool valid() const + { + return initial.has_value() && + targetStackTop.has_value() && + targetStackTailSet.has_value() && + targetStackSize.has_value(); + } + + static ShuffleTestInput parse(std::string_view _source) + { + ShuffleTestInput result; + + auto const stripComment = [](std::string_view sv) -> std::string_view + { + auto const pos = sv.find("//"); + if (pos != std::string_view::npos) + return sv.substr(0, pos); + return sv; + }; + + for (auto&& lineRange: ranges::views::split(_source, '\n')) + { + auto lineBegin = ranges::begin(lineRange); + auto lineEnd = ranges::end(lineRange); + if (lineBegin == lineEnd) + continue; + + std::string_view line{&*lineBegin, static_cast(ranges::distance(lineBegin, lineEnd))}; + line = trim(stripComment(line)); + if (line.empty()) + continue; + + auto const colonPos = line.find(':'); + if (colonPos == std::string_view::npos) + continue; + + auto const key = trim(line.substr(0, colonPos)); + auto const value = trim(line.substr(colonPos + 1)); + + if (key == parserKeyInitialStack) + result.initial = parseSlots(value, '[', ']'); + else if (key == parserKeyStackTop) + result.targetStackTop = parseSlots(value, '[', ']'); + else if (key == parserKeyTailSet) + result.targetStackTailSet = parseLiveness(value); + else if (key == parserKeyStackSize) + { + if (auto num = util::parseArithmetic(value)) + result.targetStackSize = *num; + else + throw std::runtime_error(fmt::format("Couldn't parse targetStackSize: {}", value)); + } + + } + return result; + } +}; + +/// Records a shuffling trace and produces formatted output into some ostream when going out of scope +class TraceRecorder +{ + static size_t constexpr operationColumnWidth = 12; + static size_t constexpr slotColumnWidth = 7; + static char constexpr junkSymbol = '*'; + +public: + TraceRecorder(std::ostream& _out, TestStack::Data const& _targetArgs, Liveness const& _targetTail, size_t _targetStackSize): + m_out(_out), + m_targetArgs(_targetArgs), + m_targetTail(_targetTail), + m_targetStackSize(_targetStackSize), + m_targetTailSize( + [&] { + yulAssert(_targetStackSize >= m_targetArgs.size()); + return _targetStackSize - m_targetArgs.size(); + }() + ) + {} + + void record(std::string const& _operation, TestStack::Data const& _stack) + { + m_entries.push_back(TraceEntry{_operation, _stack}); + } + + ~TraceRecorder() + { + if (m_entries.empty()) + return; + + size_t maxStackDepth = 0; + for (const auto& [operation, stackAfter]: m_entries) + maxStackDepth = std::max(maxStackDepth, stackAfter.size()); + + if (maxStackDepth == 0) + return; + + bool const hasExcess = maxStackDepth > m_targetStackSize; + + emitHeader(maxStackDepth, hasExcess); + emitSeparatorLine(maxStackDepth, hasExcess); + for (auto const& entry: m_entries) + emitDataRow(entry, hasExcess); + emitSeparatorLine(maxStackDepth, hasExcess); + emitTargetRow(hasExcess); + } + +private: + struct TraceEntry { + std::string operation; + TestStack::Data stackAfter; + }; + + std::ostream& m_out; + std::vector m_entries; + TestStack::Data const& m_targetArgs; + Liveness const& m_targetTail; + size_t const m_targetStackSize; + size_t const m_targetTailSize; + + void emitSeparator(size_t const _index, bool const _hasExcess, char const _junction) const + { + bool const endOfTargetTail = _index == m_targetTailSize && !m_targetArgs.empty() && m_targetTailSize > 0; + bool const endOfTargetStackWithExcess = _hasExcess && _index == m_targetTailSize + m_targetArgs.size(); + if (endOfTargetTail || endOfTargetStackWithExcess) + m_out << ' ' << _junction; + } + + void emitHeader(size_t const _maxStackDepth, bool const _hasExcess) const + { + m_out << fmt::format("{:>{}}", "", operationColumnWidth) << "|"; + for (size_t i = 0; i < _maxStackDepth; ++i) + { + emitSeparator(i, _hasExcess, '|'); + m_out << fmt::format("{:>{}}", i, slotColumnWidth); + } + m_out << "\n"; + } + + void emitSeparatorLine(size_t const _maxStackDepth, bool const _hasExcess) const + { + m_out << fmt::format("{:>{}}", "", operationColumnWidth) << '+'; + for (size_t i = 0; i < _maxStackDepth; ++i) + { + emitSeparator(i, _hasExcess, '+'); + m_out << std::string(slotColumnWidth, '-'); + } + m_out << '\n'; + } + + void emitDataRow(TraceEntry const& _entry, bool const _hasExcess) const + { + m_out << fmt::format("{:>{}}", _entry.operation, operationColumnWidth) << "|"; + for (size_t i = 0; i < _entry.stackAfter.size(); ++i) + { + emitSeparator(i, _hasExcess, '|'); + auto const& slot = _entry.stackAfter[i]; + std::string slotStr = slot.isJunk() ? std::string(1, junkSymbol) : slotToString(slot); + m_out << fmt::format("{:>{}}", slotStr, slotColumnWidth); + } + m_out << '\n'; + } + + void emitTargetRow(bool const _hasExcess) const + { + m_out << fmt::format("{:>{}}", "(target)", operationColumnWidth) << "|"; + + // Print tail region with set notation + if (m_targetTailSize > 0 && !(m_targetTail.empty() && m_targetArgs.empty())) + { + std::string const tailSetStr = fmt::format( + "{{{}}}", + fmt::join( + m_targetTail | ranges::views::keys | ranges::views::transform( + [](auto const& id) { return slotToString(Slot::makeValueID(id)); } + ), + ", " + ) + ); + m_out << fmt::format("{:>{}}", tailSetStr, m_targetTailSize * slotColumnWidth); + } + + // Args separator + if (!m_targetArgs.empty() && m_targetTailSize > 0) + m_out << " |"; + + // Print args region + for (auto const& slot: m_targetArgs) + { + std::string slotStr = slot.isJunk() ? std::string(1, junkSymbol) : slotToString(slot); + m_out << fmt::format("{:>{}}", slotStr, slotColumnWidth); + } + + // Excess separator + if (_hasExcess) + m_out << " |"; + + m_out << '\n'; + } +}; +} + +std::unique_ptr ShufflingTest::create(Config const& _config) +{ + return std::make_unique(_config.filename); +} + +ShufflingTest::ShufflingTest(std::string const& _filename): TestCase(_filename) +{ + m_source = m_reader.source(); + auto dialectName = m_reader.stringSetting("dialect", "evm"); + soltestAssert(dialectName == "evm"); + m_expectation = m_reader.simpleExpectations(); +} + +ShufflingTest::TestResult ShufflingTest::run(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted) +{ + auto const testConfig = ShuffleTestInput::parse(m_source); + if (!testConfig.valid()) + { + static constexpr std::string_view formatHelp = R"(initial: [, ...] +targetStackTop: [, ...] +targetStackTailSet: {, ...} +targetStackSize: + +Where is one of: + v - variable + phi - phi node + lit - literal + JUNK - junk slot + +Lines starting with // are comments. Comments at the end of lines are supported, too.)"; + util::AnsiColorized out(_stream, _formatted, {util::formatting::BOLD, util::formatting::RED}); + out << _linePrefix << fmt::format("Error parsing source. Expected format:") << '\n'; + + for (auto const line: ranges::views::split(formatHelp, '\n')) + { + auto const lineSVBegin = ranges::begin(line); + auto const lineSVEnd = ranges::end(line); + std::string_view lineSV; + if (lineSVBegin != lineSVEnd) + lineSV = {&*lineSVBegin, static_cast(ranges::distance(lineSVBegin, lineSVEnd))}; + out << _linePrefix << " " << lineSV << '\n'; + } + return TestResult::FatalError; + } + + auto stackData = *testConfig.initial; + std::ostringstream oss; + { + TraceRecorder trace(oss, *testConfig.targetStackTop, *testConfig.targetStackTailSet, *testConfig.targetStackSize); + trace.record("(initial)", *testConfig.initial); + TestStack stack(stackData, {.hook = [&](std::string const& op){ trace.record(op, stackData); }}); + StackShuffler::shuffle( + stack, + *testConfig.targetStackTop, + *testConfig.targetStackTailSet, + *testConfig.targetStackSize + ); + } + // check stack data + { + yulAssert(*testConfig.targetStackSize >= testConfig.targetStackTop->size()); + auto const tailSize = *testConfig.targetStackSize - testConfig.targetStackTop->size(); + yulAssert(stackData.size() == *testConfig.targetStackSize); + for (const auto& valueID: *testConfig.targetStackTailSet | ranges::views::keys) + { + auto const findIt = ranges::find( + stackData.begin(), + stackData.begin() + static_cast(tailSize), + StackSlot::makeValueID(valueID) + ); + yulAssert(findIt != ranges::end(stackData)); + } + for (std::size_t offset = tailSize; offset < *testConfig.targetStackSize; ++offset) + { + auto const& targetSlot = testConfig.targetStackTop->at(offset - tailSize); + yulAssert(targetSlot.isJunk() || stackData[offset] == targetSlot); + } + } + m_obtainedResult = oss.str(); + + + return checkResult(_stream, _linePrefix, _formatted); +} diff --git a/test/libyul/ssa/StackShufflerTest.h b/test/libyul/ssa/StackShufflerTest.h new file mode 100644 index 0000000000..14b0d98f68 --- /dev/null +++ b/test/libyul/ssa/StackShufflerTest.h @@ -0,0 +1,36 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include + +#include + +namespace solidity::yul::ssa::test +{ + +class ShufflingTest: public frontend::test::TestCase +{ +public: + static std::unique_ptr create(Config const& _config); + explicit ShufflingTest(std::string const& _filename); + TestResult run(std::ostream& _stream, std::string const& _linePrefix, bool _formatted) override; +}; + +} diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 31bdf34ee4..7b7331c77f 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -53,6 +53,8 @@ add_executable(isoltest ../libyul/YulOptimizerTest.cpp ../libyul/YulOptimizerTestCommon.cpp ../libyul/YulInterpreterTest.cpp + ../libyul/ssa/StackShufflerTest.cpp + ../libyul/ssa/StackShufflerTest.h ) target_compile_definitions(isoltest PRIVATE ISOLTEST) target_link_libraries(isoltest PRIVATE evmc libsolc solidity yulInterpreter evmasm Boost::boost Boost::program_options Boost::unit_test_framework Threads::Threads) From 70d774f70ac92a22cacc3b8b038f6f8034c3a960 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 2 Feb 2026 12:14:43 +0100 Subject: [PATCH 181/243] Add stack shuffler tests --- .../deep_argument_dup_order.stack | 14 ++ .../stackShuffler/deep_argument_shrink.stack | 20 +++ .../ssa/stackShuffler/dup_up_args.stack | 18 +++ test/libyul/ssa/stackShuffler/empty.stack | 14 ++ test/libyul/ssa/stackShuffler/grow.stack | 12 ++ .../ssa/stackShuffler/introduce_junk.stack | 17 +++ .../ssa/stackShuffler/reduce_size.stack | 13 ++ .../satisfy_tail_requirements.stack | 30 +++++ .../stackShuffler/shrink_and_grow_a_lot.stack | 122 ++++++++++++++++++ .../stackShuffler/superfluous_top_slots.stack | 18 +++ .../stackShuffler/swap_junk_over_pop.stack | 14 ++ 11 files changed, 292 insertions(+) create mode 100644 test/libyul/ssa/stackShuffler/deep_argument_dup_order.stack create mode 100644 test/libyul/ssa/stackShuffler/deep_argument_shrink.stack create mode 100644 test/libyul/ssa/stackShuffler/dup_up_args.stack create mode 100644 test/libyul/ssa/stackShuffler/empty.stack create mode 100644 test/libyul/ssa/stackShuffler/grow.stack create mode 100644 test/libyul/ssa/stackShuffler/introduce_junk.stack create mode 100644 test/libyul/ssa/stackShuffler/reduce_size.stack create mode 100644 test/libyul/ssa/stackShuffler/satisfy_tail_requirements.stack create mode 100644 test/libyul/ssa/stackShuffler/shrink_and_grow_a_lot.stack create mode 100644 test/libyul/ssa/stackShuffler/superfluous_top_slots.stack create mode 100644 test/libyul/ssa/stackShuffler/swap_junk_over_pop.stack diff --git a/test/libyul/ssa/stackShuffler/deep_argument_dup_order.stack b/test/libyul/ssa/stackShuffler/deep_argument_dup_order.stack new file mode 100644 index 0000000000..3dbbcf438d --- /dev/null +++ b/test/libyul/ssa/stackShuffler/deep_argument_dup_order.stack @@ -0,0 +1,14 @@ +// if lit0 is pushed first, v129 can no longer be duped up +initial: [v129, v43, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, v101, JUNK, v130, v131] +targetStackTop: [v43, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, lit0, v129] +targetStackTailSet: {v129} +targetStackSize: 18 +// ---- +// | 0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +// +------- +----------------------------------------------------------------------------------------------------------------------- +// (initial)| v129 | v43 * * * * * * * * * * v101 * v130 v131 +// DUP16| v129 | v43 * * * * * * * * * * v101 * v130 v131 v129 +// PUSH lit0| v129 | v43 * * * * * * * * * * v101 * v130 v131 v129 lit0 +// SWAP1| v129 | v43 * * * * * * * * * * v101 * v130 v131 lit0 v129 +// +------- +----------------------------------------------------------------------------------------------------------------------- +// (target)| {v129} | v43 * * * * * * * * * * * * * * lit0 v129 diff --git a/test/libyul/ssa/stackShuffler/deep_argument_shrink.stack b/test/libyul/ssa/stackShuffler/deep_argument_shrink.stack new file mode 100644 index 0000000000..b7fdbcbafe --- /dev/null +++ b/test/libyul/ssa/stackShuffler/deep_argument_shrink.stack @@ -0,0 +1,20 @@ +// the shuffler has to first shrink the stack to reach v12 for args +initial: [v12, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, v68, JUNK, phi111, v84, v86] +targetStackTop: [v12, v86] +targetStackTailSet: {v68, v84, phi111} +targetStackSize: 19 +// ---- +// | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 17 18 +// +----------------------------------------------------------------------------------------------------------------------- +-------------- +// (initial)| v12 * * * * * * * * * * * * v68 * phi111 v84 | v86 +// SWAP3| v12 * * * * * * * * * * * * v68 v86 phi111 v84 | * +// POP| v12 * * * * * * * * * * * * v68 v86 phi111 v84 +// SWAP2| v12 * * * * * * * * * * * * v68 v84 phi111 v86 +// SWAP4| v12 * * * * * * * * * * * v86 v68 v84 phi111 * +// POP| v12 * * * * * * * * * * * v86 v68 v84 phi111 +// SWAP15| phi111 * * * * * * * * * * * v86 v68 v84 v12 +// DUP4| phi111 * * * * * * * * * * * v86 v68 v84 v12 v86 +// DUP2| phi111 * * * * * * * * * * * v86 v68 v84 v12 v86 | v12 +// DUP2| phi111 * * * * * * * * * * * v86 v68 v84 v12 v86 | v12 v86 +// +----------------------------------------------------------------------------------------------------------------------- +-------------- +// (target)| {v68, v84, phi111} | v12 v86 diff --git a/test/libyul/ssa/stackShuffler/dup_up_args.stack b/test/libyul/ssa/stackShuffler/dup_up_args.stack new file mode 100644 index 0000000000..f3be04a895 --- /dev/null +++ b/test/libyul/ssa/stackShuffler/dup_up_args.stack @@ -0,0 +1,18 @@ +// populate args by a sequence of dups +initial: [JUNK, v12, phi9, phi13, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, v65, v67] +targetStackTop: [lit27, phi13, phi9, v12, v67] +targetStackTailSet: {phi9, v12, phi13, v65} +targetStackSize: 21 +// ---- +// | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 16 17 18 19 20 +// +---------------------------------------------------------------------------------------------------------------- +----------------------------------- +// (initial)| * v12 phi9 phi13 * * * * * * * * v65 v67 +// DUP13| * v12 phi9 phi13 * * * * * * * * v65 v67 v12 +// DUP13| * v12 phi9 phi13 * * * * * * * * v65 v67 v12 phi9 +// PUSH lit27| * v12 phi9 phi13 * * * * * * * * v65 v67 v12 phi9 | lit27 +// DUP14| * v12 phi9 phi13 * * * * * * * * v65 v67 v12 phi9 | lit27 phi13 +// DUP3| * v12 phi9 phi13 * * * * * * * * v65 v67 v12 phi9 | lit27 phi13 phi9 +// DUP5| * v12 phi9 phi13 * * * * * * * * v65 v67 v12 phi9 | lit27 phi13 phi9 v12 +// DUP7| * v12 phi9 phi13 * * * * * * * * v65 v67 v12 phi9 | lit27 phi13 phi9 v12 v67 +// +---------------------------------------------------------------------------------------------------------------- +----------------------------------- +// (target)| {phi9, v12, phi13, v65} | lit27 phi13 phi9 v12 v67 diff --git a/test/libyul/ssa/stackShuffler/empty.stack b/test/libyul/ssa/stackShuffler/empty.stack new file mode 100644 index 0000000000..077660d6f7 --- /dev/null +++ b/test/libyul/ssa/stackShuffler/empty.stack @@ -0,0 +1,14 @@ +// populate an empty stack with junk +initial: [] +targetStackTop: [] +targetStackTailSet: {} +targetStackSize: 3 +// ---- +// | 0 1 2 +// +--------------------- +// (initial)| +// PUSH JUNK| * +// PUSH JUNK| * * +// PUSH JUNK| * * * +// +--------------------- +// (target)| diff --git a/test/libyul/ssa/stackShuffler/grow.stack b/test/libyul/ssa/stackShuffler/grow.stack new file mode 100644 index 0000000000..4c07f8e36f --- /dev/null +++ b/test/libyul/ssa/stackShuffler/grow.stack @@ -0,0 +1,12 @@ +// need to grow the stack to reach target stack size but still fulfill args requirements +initial: [JUNK, JUNK, JUNK, v15] +targetStackTop: [JUNK, JUNK, JUNK, JUNK, v15] +targetStackTailSet: {} +targetStackSize: 5 +// ---- +// | 0 1 2 3 4 +// +----------------------------------- +// (initial)| * * * v15 +// DUP1| * * * v15 v15 +// +----------------------------------- +// (target)| * * * * v15 diff --git a/test/libyul/ssa/stackShuffler/introduce_junk.stack b/test/libyul/ssa/stackShuffler/introduce_junk.stack new file mode 100644 index 0000000000..54b29a9a31 --- /dev/null +++ b/test/libyul/ssa/stackShuffler/introduce_junk.stack @@ -0,0 +1,17 @@ +// some of the slots in the initial stack no longer appear in the target / are junk +initial: [v189, phi112, JUNK, v204, JUNK, JUNK, JUNK, v185, JUNK, v188, v190, v191, JUNK, v199, phi112, phi112, v205, v206] +targetStackTop: [v199, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, v185, JUNK, v188, v190, v191, JUNK, v189, JUNK, v206, v205, v204] +targetStackTailSet: {} +targetStackSize: 18 +// ---- +// | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +// +------------------------------------------------------------------------------------------------------------------------------ +// (initial)| v189 phi112 * v204 * * * v185 * v188 v190 v191 * v199 phi112 phi112 v205 v206 +// SWAP2| v189 phi112 * v204 * * * v185 * v188 v190 v191 * v199 phi112 v206 v205 phi112 +// POP| v189 phi112 * v204 * * * v185 * v188 v190 v191 * v199 phi112 v206 v205 +// SWAP16| v205 phi112 * v204 * * * v185 * v188 v190 v191 * v199 phi112 v206 v189 +// SWAP3| v205 phi112 * v204 * * * v185 * v188 v190 v191 * v189 phi112 v206 v199 +// SWAP16| v199 phi112 * v204 * * * v185 * v188 v190 v191 * v189 phi112 v206 v205 +// DUP14| v199 phi112 * v204 * * * v185 * v188 v190 v191 * v189 phi112 v206 v205 v204 +// +------------------------------------------------------------------------------------------------------------------------------ +// (target)| v199 * * * * * * v185 * v188 v190 v191 * v189 * v206 v205 v204 diff --git a/test/libyul/ssa/stackShuffler/reduce_size.stack b/test/libyul/ssa/stackShuffler/reduce_size.stack new file mode 100644 index 0000000000..d6a6706193 --- /dev/null +++ b/test/libyul/ssa/stackShuffler/reduce_size.stack @@ -0,0 +1,13 @@ +// v199 can be popped as it's already contained in the tail +initial: [JUNK, v189, phi112, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, v185, phi115, v188, v190, v191, phi113, v199, phi112, v199, v202] +targetStackTop: [v202] +targetStackTailSet: {phi112, phi113, phi115, v185, v188, v189, v190, v191, v199} +targetStackSize: 19 +// ---- +// | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 18 | 19 +// +------------------------------------------------------------------------------------------------------------------------------ +------- +------- +// (initial)| * v189 phi112 * * * * * * * v185 phi115 v188 v190 v191 phi113 v199 phi112 | v199 | v202 +// SWAP1| * v189 phi112 * * * * * * * v185 phi115 v188 v190 v191 phi113 v199 phi112 | v202 | v199 +// POP| * v189 phi112 * * * * * * * v185 phi115 v188 v190 v191 phi113 v199 phi112 | v202 +// +------------------------------------------------------------------------------------------------------------------------------ +------- +------- +// (target)| {phi112, phi113, phi115, v185, v188, v189, v190, v191, v199} | v202 | diff --git a/test/libyul/ssa/stackShuffler/satisfy_tail_requirements.stack b/test/libyul/ssa/stackShuffler/satisfy_tail_requirements.stack new file mode 100644 index 0000000000..e685f06607 --- /dev/null +++ b/test/libyul/ssa/stackShuffler/satisfy_tail_requirements.stack @@ -0,0 +1,30 @@ +// here, we have to create one more instance of v10 to be able to satisfy the tail requirements +initial: [v2, v3, v4, v5, v6, v7, v8, v9, v10] +targetStackTop: [lit3, v9, v8, v7, v6, v5, v4, v3, v2, v10] +targetStackTailSet: {v10} +targetStackSize: 17 +// ---- +// | 0 1 2 3 4 5 6 | 7 8 9 10 11 12 13 14 15 16 +// +------------------------------------------------- +---------------------------------------------------------------------- +// (initial)| v2 v3 v4 v5 v6 v7 v8 | v9 v10 +// DUP1| v2 v3 v4 v5 v6 v7 v8 | v9 v10 v10 +// SWAP9| v10 v3 v4 v5 v6 v7 v8 | v9 v10 v2 +// SWAP2| v10 v3 v4 v5 v6 v7 v8 | v2 v10 v9 +// SWAP1| v10 v3 v4 v5 v6 v7 v8 | v2 v9 v10 +// PUSH lit3| v10 v3 v4 v5 v6 v7 v8 | v2 v9 v10 lit3 +// SWAP3| v10 v3 v4 v5 v6 v7 v8 | lit3 v9 v10 v2 +// DUP5| v10 v3 v4 v5 v6 v7 v8 | lit3 v9 v10 v2 v8 +// SWAP2| v10 v3 v4 v5 v6 v7 v8 | lit3 v9 v8 v2 v10 +// DUP7| v10 v3 v4 v5 v6 v7 v8 | lit3 v9 v8 v2 v10 v7 +// SWAP2| v10 v3 v4 v5 v6 v7 v8 | lit3 v9 v8 v7 v10 v2 +// DUP9| v10 v3 v4 v5 v6 v7 v8 | lit3 v9 v8 v7 v10 v2 v6 +// SWAP2| v10 v3 v4 v5 v6 v7 v8 | lit3 v9 v8 v7 v6 v2 v10 +// DUP11| v10 v3 v4 v5 v6 v7 v8 | lit3 v9 v8 v7 v6 v2 v10 v5 +// SWAP2| v10 v3 v4 v5 v6 v7 v8 | lit3 v9 v8 v7 v6 v5 v10 v2 +// DUP13| v10 v3 v4 v5 v6 v7 v8 | lit3 v9 v8 v7 v6 v5 v10 v2 v4 +// SWAP2| v10 v3 v4 v5 v6 v7 v8 | lit3 v9 v8 v7 v6 v5 v4 v2 v10 +// DUP15| v10 v3 v4 v5 v6 v7 v8 | lit3 v9 v8 v7 v6 v5 v4 v2 v10 v3 +// SWAP2| v10 v3 v4 v5 v6 v7 v8 | lit3 v9 v8 v7 v6 v5 v4 v3 v10 v2 +// SWAP1| v10 v3 v4 v5 v6 v7 v8 | lit3 v9 v8 v7 v6 v5 v4 v3 v2 v10 +// +------------------------------------------------- +---------------------------------------------------------------------- +// (target)| {v10} | lit3 v9 v8 v7 v6 v5 v4 v3 v2 v10 diff --git a/test/libyul/ssa/stackShuffler/shrink_and_grow_a_lot.stack b/test/libyul/ssa/stackShuffler/shrink_and_grow_a_lot.stack new file mode 100644 index 0000000000..7d4f68a8a9 --- /dev/null +++ b/test/libyul/ssa/stackShuffler/shrink_and_grow_a_lot.stack @@ -0,0 +1,122 @@ +// the stack top needs a very deep slot +initial: [v55, v71, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, JUNK, v94] +targetStackTop: [lit11, v55] +targetStackTailSet: {v55, v71, v94} +targetStackSize: 54 +// todo: instead of alternating swap1 + pop, we should be (eg) doing swap16 and then a sequence of 16 pops +// ---- +// | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | 52 53 +// +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-------------- +// (initial)| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * * v94 +// SWAP1| v55 v71 * * * * * * * * * * * * * v94 * +// POP| v55 v71 * * * * * * * * * * * * * v94 +// DUP16| v55 v71 * * * * * * * * * * * * * v94 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// DUP1| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 +// PUSH lit11| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 | lit11 +// DUP2| v55 v71 * * * * * * * * * * * * * v94 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 v55 | lit11 v55 +// +---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-------------- +// (target)| {v55, v71, v94} | lit11 v55 diff --git a/test/libyul/ssa/stackShuffler/superfluous_top_slots.stack b/test/libyul/ssa/stackShuffler/superfluous_top_slots.stack new file mode 100644 index 0000000000..87d9ea9a81 --- /dev/null +++ b/test/libyul/ssa/stackShuffler/superfluous_top_slots.stack @@ -0,0 +1,18 @@ +// todo: The upper two junk slots should be popped immediately, they are completely superfluous. +initial: [JUNK, JUNK, v56, v57, JUNK, JUNK] +targetStackTop: [JUNK, JUNK, v56, v57, lit0, v56, lit11] +targetStackTailSet: {} +targetStackSize: 7 +// ---- +// | 0 1 2 3 4 5 6 | 7 +// +------------------------------------------------- +------- +// (initial)| * * v56 v57 * * +// PUSH lit11| * * v56 v57 * * lit11 +// DUP5| * * v56 v57 * * lit11 | v56 +// SWAP2| * * v56 v57 * v56 lit11 | * +// POP| * * v56 v57 * v56 lit11 +// PUSH lit0| * * v56 v57 * v56 lit11 | lit0 +// SWAP3| * * v56 v57 lit0 v56 lit11 | * +// POP| * * v56 v57 lit0 v56 lit11 +// +------------------------------------------------- +------- +// (target)| * * v56 v57 lit0 v56 lit11 | diff --git a/test/libyul/ssa/stackShuffler/swap_junk_over_pop.stack b/test/libyul/ssa/stackShuffler/swap_junk_over_pop.stack new file mode 100644 index 0000000000..56f623b754 --- /dev/null +++ b/test/libyul/ssa/stackShuffler/swap_junk_over_pop.stack @@ -0,0 +1,14 @@ +// here, the algorithm should prefer a swap of junk into tail over pop and dup +initial: [v0, JUNK, v2] +targetStackTop: [lit2, v2, v0] +targetStackTailSet: {} +targetStackSize: 4 +// ---- +// | 0 | 1 2 3 +// +------- +--------------------- +// (initial)| v0 | * v2 +// PUSH lit2| v0 | * v2 lit2 +// SWAP2| v0 | lit2 v2 * +// SWAP3| * | lit2 v2 v0 +// +------- +--------------------- +// (target)| {} | lit2 v2 v0 From 8f643082c250abbebba5a4f81d88129041ca4e4a Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Sat, 29 Mar 2025 13:14:35 +0530 Subject: [PATCH 182/243] Update resources.rst --- docs/resources.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/resources.rst b/docs/resources.rst index 52df87fdd5..3dac252ee1 100644 --- a/docs/resources.rst +++ b/docs/resources.rst @@ -107,7 +107,7 @@ Solidity Tools Tool to generate Solidity interface source from a given ABI JSON. * `Aderyn `_ - Rust-based solidity smart contract static analyzer designed to help find vulnerabilities in Solidity code bases. + Rust-based solidity smart contract static analyzer designed to help find vulnerabilities in Solidity code bases. Comes with `VS Code Extension ` for easy use. * `Doxity `_ Documentation Generator for Solidity. From 1a499f10f0fd4cfc0563bd1770627573c0086c5b Mon Sep 17 00:00:00 2001 From: Tilak Madichetti Date: Mon, 23 Jun 2025 08:22:18 +0000 Subject: [PATCH 183/243] fix --- docs/resources.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/resources.rst b/docs/resources.rst index 3dac252ee1..7fdb55367c 100644 --- a/docs/resources.rst +++ b/docs/resources.rst @@ -78,6 +78,9 @@ Editor Integrations * Visual Studio Code (VS Code) + * `Aderyn Visual Studio Code extension `_ + Solidity Smart contract analyzer designed to help find vulnerabilities. It supports projects built with Hardhat, Foundry, or any custom framework. + * `Ethereum Remix Visual Studio Code extension `_ Ethereum Remix extension pack for VS Code 💡 Note: As per the official repository, this extension has been removed from the VSCODE marketplace and will be replaced by a dedicated stand-alone desktop application. @@ -107,7 +110,7 @@ Solidity Tools Tool to generate Solidity interface source from a given ABI JSON. * `Aderyn `_ - Rust-based solidity smart contract static analyzer designed to help find vulnerabilities in Solidity code bases. Comes with `VS Code Extension ` for easy use. + Command Line Tool that helps find vulnerabilities in Solidity smart contracts. It supports projects built with Hardhat, Foundry, or any custom framework. * `Doxity `_ Documentation Generator for Solidity. From 7e2fb2d4739e135a9622df778e50af52a05cc134 Mon Sep 17 00:00:00 2001 From: rodiazet Date: Wed, 4 Feb 2026 10:30:36 +0100 Subject: [PATCH 184/243] Fix `check_style.sh`. Replace `\s` to `[[:space:]]` --- scripts/check_style.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/check_style.sh b/scripts/check_style.sh index 7188f84f2b..5a744de78e 100755 --- a/scripts/check_style.sh +++ b/scripts/check_style.sh @@ -47,19 +47,19 @@ FORMATERROR=$( ( preparedGrep "#include \"" | grep -E -v -e "license.h" -e "BuildInfo.h" # Use include with <> characters preparedGrep "\<(if|for|while|switch)\(" # no space after "if", "for", "while" or "switch" - preparedGrep "\\s*\([^=]*\>\s:\s.*\)" # no space before range based for-loop - preparedGrep "\\s*\(.*\)\s*\{\s*$" # "{\n" on same line as "if" + preparedGrep "\[[:space:]]*\([^=]*\>[[:space:]]:[[:space:]].*\)" # no space before range based for-loop + preparedGrep "\[[:space:]]*\(.*\)[[:space:]]*\{[[:space:]]*$" # "{\n" on same line as "if" preparedGrep "namespace .*\{" - preparedGrep "[,\(<]\s*const " # const on left side of type - preparedGrep "^\s*(static)?\s*const " # const on left side of type (beginning of line) + preparedGrep "[,\(<][[:space:]]*const " # const on left side of type + preparedGrep "^[[:space:]]*(static)?[[:space:]]*const " # const on left side of type (beginning of line) preparedGrep "^ [^*]|[^*] | [^*]" # uses spaces for indentation or mixes spaces and tabs - preparedGrep "[a-zA-Z0-9_]\s*[&][a-zA-Z_]" | grep -E -v "return [&]" # right-aligned reference ampersand (needs to exclude return) + preparedGrep "[a-zA-Z0-9_][[:space:]]*[&][a-zA-Z_]" | grep -E -v "return [&]" # right-aligned reference ampersand (needs to exclude return) # right-aligned reference pointer star (needs to exclude return and comments) - preparedGrep "[a-zA-Z0-9_]\s*[*][a-zA-Z_]" | grep -E -v -e "return [*]" -e ":[0-9]+:\s*\*\s" -e "//" + preparedGrep "[a-zA-Z0-9_][[:space:]]*[*][a-zA-Z_]" | grep -E -v -e "return [*]" -e ":[0-9]+:[[:space:]]*\*[[:space:]]" -e "//" # unqualified move()/forward() checks, i.e. make sure that std::move() and std::forward() are used instead of move() and forward() preparedGrep "move\(.+\)" | grep -v "std::move" | grep -E "[^a-z]move" preparedGrep "forward\(.+\)" | grep -v "std::forward" | grep -E "[^a-z]forward" -) | grep -E -v -e "^[a-zA-Z./]*:[0-9]*:\s*/[/*]" -e "^test/" || true +) | grep -E -v -e "^[a-zA-Z./]*:[0-9]*:[[:space:]]*/[/*]" -e "^test/" || true ) # Special error handling for `using namespace std;` exclusion, since said statement can be present in the test directory From 64f26d919034c0e9060a90386bcd76d434f13129 Mon Sep 17 00:00:00 2001 From: davidekete Date: Sat, 7 Feb 2026 15:27:12 +0100 Subject: [PATCH 185/243] update information about using Remix offline --- docs/installing-solidity.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index f061829ecf..7e372ea4e3 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -31,8 +31,7 @@ Remix *We recommend Remix for small contracts and for quickly learning Solidity.* `Access Remix online `_, you do not need to install anything. -If you want to use it without connection to the Internet, go to -https://github.com/ethereum/remix-live/tree/gh-pages#readme and follow the instructions on that page. +If you want to use it without connection to the Internet, download Remix Desktop from `the releases page `_. Remix is also a convenient option for testing nightly builds without installing multiple Solidity versions. From 39bf0830b80fd5de8dbac5312d78c0e4eec8f0ad Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 9 Feb 2026 11:18:53 +0100 Subject: [PATCH 186/243] move SSACFGTopologicalSort -> traversal/ForwardTopologicalSort --- libyul/CMakeLists.txt | 4 ++-- .../backends/evm/ssa/JunkAdmittingBlocksFinder.cpp | 2 +- libyul/backends/evm/ssa/JunkAdmittingBlocksFinder.h | 4 ++-- libyul/backends/evm/ssa/LivenessAnalysis.h | 6 +++--- libyul/backends/evm/ssa/SSACFGLoopNestingForest.cpp | 2 +- libyul/backends/evm/ssa/SSACFGLoopNestingForest.h | 6 +++--- .../ForwardTopologicalSort.cpp} | 12 ++++++------ .../ForwardTopologicalSort.h} | 6 +++--- 8 files changed, 21 insertions(+), 21 deletions(-) rename libyul/backends/evm/ssa/{SSACFGTopologicalSort.cpp => traversal/ForwardTopologicalSort.cpp} (83%) rename libyul/backends/evm/ssa/{SSACFGTopologicalSort.h => traversal/ForwardTopologicalSort.h} (94%) diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 9d0bfe1e2e..2e62f6d946 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -82,8 +82,6 @@ add_library(yul backends/evm/ssa/LivenessAnalysis.h backends/evm/ssa/SSACFGLoopNestingForest.cpp backends/evm/ssa/SSACFGLoopNestingForest.h - backends/evm/ssa/SSACFGTopologicalSort.cpp - backends/evm/ssa/SSACFGTopologicalSort.h backends/evm/ssa/SSACFG.cpp backends/evm/ssa/SSACFG.h backends/evm/ssa/SSACFGBuilder.cpp @@ -94,6 +92,8 @@ add_library(yul backends/evm/ssa/Stack.h backends/evm/ssa/StackShuffler.cpp backends/evm/ssa/StackShuffler.h + backends/evm/ssa/traversal/ForwardTopologicalSort.cpp + backends/evm/ssa/traversal/ForwardTopologicalSort.h optimiser/ASTCopier.cpp optimiser/ASTCopier.h optimiser/ASTWalker.cpp diff --git a/libyul/backends/evm/ssa/JunkAdmittingBlocksFinder.cpp b/libyul/backends/evm/ssa/JunkAdmittingBlocksFinder.cpp index d7c9202ef4..e0519db614 100644 --- a/libyul/backends/evm/ssa/JunkAdmittingBlocksFinder.cpp +++ b/libyul/backends/evm/ssa/JunkAdmittingBlocksFinder.cpp @@ -23,7 +23,7 @@ namespace solidity::yul::ssa { -JunkAdmittingBlocksFinder::JunkAdmittingBlocksFinder(SSACFG const& _cfg, ForwardSSACFGTopologicalSort const& _topologicalSort): +JunkAdmittingBlocksFinder::JunkAdmittingBlocksFinder(SSACFG const& _cfg, traversal::ForwardTopologicalSort const& _topologicalSort): m_blockAllowsJunk(_cfg.numBlocks(), false) { // special case: only one block here, we mark it as junkable in case it's not a function return diff --git a/libyul/backends/evm/ssa/JunkAdmittingBlocksFinder.h b/libyul/backends/evm/ssa/JunkAdmittingBlocksFinder.h index e567603dd3..6d2104b03f 100644 --- a/libyul/backends/evm/ssa/JunkAdmittingBlocksFinder.h +++ b/libyul/backends/evm/ssa/JunkAdmittingBlocksFinder.h @@ -18,7 +18,7 @@ #pragma once -#include +#include #include #include @@ -37,7 +37,7 @@ namespace solidity::yul::ssa class JunkAdmittingBlocksFinder { public: - explicit JunkAdmittingBlocksFinder(SSACFG const& _cfg, ForwardSSACFGTopologicalSort const& _topologicalSort); + explicit JunkAdmittingBlocksFinder(SSACFG const& _cfg, traversal::ForwardTopologicalSort const& _topologicalSort); bool allowsAdditionOfJunk(SSACFG::BlockId const& _blockId) const; private: std::vector m_blockAllowsJunk; diff --git a/libyul/backends/evm/ssa/LivenessAnalysis.h b/libyul/backends/evm/ssa/LivenessAnalysis.h index b34a790bf1..5a19cb0bd3 100644 --- a/libyul/backends/evm/ssa/LivenessAnalysis.h +++ b/libyul/backends/evm/ssa/LivenessAnalysis.h @@ -18,7 +18,7 @@ #pragma once -#include +#include #include #include @@ -107,7 +107,7 @@ class LivenessAnalysis LivenessData const& liveOut(SSACFG::BlockId const _blockId) const { return m_liveOuts[_blockId.value]; } LivenessData used(SSACFG::BlockId _blockId) const; std::vector const& operationsLiveOut(SSACFG::BlockId _blockId) const { return m_operationLiveOuts[_blockId.value]; } - ForwardSSACFGTopologicalSort const& topologicalSort() const { return m_topologicalSort; } + traversal::ForwardTopologicalSort const& topologicalSort() const { return m_topologicalSort; } SSACFG const& cfg() const { return m_cfg; } private: @@ -117,7 +117,7 @@ class LivenessAnalysis LivenessData blockExitValues(SSACFG::BlockId const& _blockId) const; SSACFG const& m_cfg; - ForwardSSACFGTopologicalSort m_topologicalSort; + traversal::ForwardTopologicalSort m_topologicalSort; SSACFGLoopNestingForest m_loopNestingForest; std::vector m_liveIns; std::vector m_liveOuts; diff --git a/libyul/backends/evm/ssa/SSACFGLoopNestingForest.cpp b/libyul/backends/evm/ssa/SSACFGLoopNestingForest.cpp index c5c422a467..6adff4023b 100644 --- a/libyul/backends/evm/ssa/SSACFGLoopNestingForest.cpp +++ b/libyul/backends/evm/ssa/SSACFGLoopNestingForest.cpp @@ -22,7 +22,7 @@ using namespace solidity::yul::ssa; -SSACFGLoopNestingForest::SSACFGLoopNestingForest(ForwardSSACFGTopologicalSort const& _sort): +SSACFGLoopNestingForest::SSACFGLoopNestingForest(traversal::ForwardTopologicalSort const& _sort): m_sort(_sort), m_cfg(_sort.cfg()), m_vertexPartition(m_cfg.numBlocks()), diff --git a/libyul/backends/evm/ssa/SSACFGLoopNestingForest.h b/libyul/backends/evm/ssa/SSACFGLoopNestingForest.h index 4654cbf135..2ee8333356 100644 --- a/libyul/backends/evm/ssa/SSACFGLoopNestingForest.h +++ b/libyul/backends/evm/ssa/SSACFGLoopNestingForest.h @@ -18,7 +18,7 @@ #pragma once -#include +#include #include #include @@ -39,7 +39,7 @@ class SSACFGLoopNestingForest using BlockIdValue = SSACFG::BlockId::ValueType; public: - explicit SSACFGLoopNestingForest(ForwardSSACFGTopologicalSort const& _sort); + explicit SSACFGLoopNestingForest(traversal::ForwardTopologicalSort const& _sort); /// blocks which are not contained in a loop get assigned the loop parent numeric_limit::max() std::vector const& loopParents() const { return m_loopParents; } @@ -51,7 +51,7 @@ class SSACFGLoopNestingForest void findLoop(BlockIdValue _potentialHeader); void collapse(std::set const& _loopBody, BlockIdValue _loopHeader); - ForwardSSACFGTopologicalSort const& m_sort; + traversal::ForwardTopologicalSort const& m_sort; SSACFG const& m_cfg; util::ContiguousDisjointSet m_vertexPartition; diff --git a/libyul/backends/evm/ssa/SSACFGTopologicalSort.cpp b/libyul/backends/evm/ssa/traversal/ForwardTopologicalSort.cpp similarity index 83% rename from libyul/backends/evm/ssa/SSACFGTopologicalSort.cpp rename to libyul/backends/evm/ssa/traversal/ForwardTopologicalSort.cpp index 3a1ca43c57..f880954ab1 100644 --- a/libyul/backends/evm/ssa/SSACFGTopologicalSort.cpp +++ b/libyul/backends/evm/ssa/traversal/ForwardTopologicalSort.cpp @@ -16,11 +16,11 @@ */ // SPDX-License-Identifier: GPL-3.0 -#include +#include -using namespace solidity::yul::ssa; +using namespace solidity::yul::ssa::traversal; -ForwardSSACFGTopologicalSort::ForwardSSACFGTopologicalSort(SSACFG const& _cfg): +ForwardTopologicalSort::ForwardTopologicalSort(SSACFG const& _cfg): m_cfg(_cfg), m_explored(m_cfg.numBlocks(), false), m_blockWisePreOrder(m_cfg.numBlocks(), 0), m_blockWiseMaxSubtreePreOrder(m_cfg.numBlocks(), 0) @@ -35,7 +35,7 @@ ForwardSSACFGTopologicalSort::ForwardSSACFGTopologicalSort(SSACFG const& _cfg): m_backEdgeTargets.insert(v2); } -void ForwardSSACFGTopologicalSort::dfs(SSACFG::BlockId::ValueType const _vertex) { +void ForwardTopologicalSort::dfs(SSACFG::BlockId::ValueType const _vertex) { yulAssert(!m_explored[_vertex]); m_explored[_vertex] = true; m_blockWisePreOrder[_vertex] = static_cast(m_preOrder.size()); @@ -55,7 +55,7 @@ void ForwardSSACFGTopologicalSort::dfs(SSACFG::BlockId::ValueType const _vertex) m_postOrder.push_back(_vertex); } -bool ForwardSSACFGTopologicalSort::ancestor(SSACFG::BlockId::ValueType const _block1, SSACFG::BlockId::ValueType const _block2) const { +bool ForwardTopologicalSort::ancestor(SSACFG::BlockId::ValueType const _block1, SSACFG::BlockId::ValueType const _block2) const { yulAssert(_block1 < m_blockWisePreOrder.size()); yulAssert(_block2 < m_blockWisePreOrder.size()); @@ -67,7 +67,7 @@ bool ForwardSSACFGTopologicalSort::ancestor(SSACFG::BlockId::ValueType const _bl return node1VisitedBeforeNode2 && node2InSubtreeOfNode1; } -bool ForwardSSACFGTopologicalSort::backEdge(SSACFG::BlockId const& _block1, SSACFG::BlockId const& _block2) const +bool ForwardTopologicalSort::backEdge(SSACFG::BlockId const& _block1, SSACFG::BlockId const& _block2) const { if (ancestor(_block2.value, _block1.value)) { diff --git a/libyul/backends/evm/ssa/SSACFGTopologicalSort.h b/libyul/backends/evm/ssa/traversal/ForwardTopologicalSort.h similarity index 94% rename from libyul/backends/evm/ssa/SSACFGTopologicalSort.h rename to libyul/backends/evm/ssa/traversal/ForwardTopologicalSort.h index 39c2eebc35..b116635975 100644 --- a/libyul/backends/evm/ssa/SSACFGTopologicalSort.h +++ b/libyul/backends/evm/ssa/traversal/ForwardTopologicalSort.h @@ -24,14 +24,14 @@ #include #include -namespace solidity::yul::ssa +namespace solidity::yul::ssa::traversal { /// Performs a topological sort on the forward CFG (no back/cross edges) -class ForwardSSACFGTopologicalSort +class ForwardTopologicalSort { public: - explicit ForwardSSACFGTopologicalSort(SSACFG const& _cfg); + explicit ForwardTopologicalSort(SSACFG const& _cfg); std::vector const& preOrder() const { return m_preOrder; } std::vector const& postOrder() const { return m_postOrder; } From d783ff9813bf5243355b882fb1901b1f94c899a7 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 9 Feb 2026 11:22:31 +0100 Subject: [PATCH 187/243] move SSA CFG json exporter into io subdirectory and namespace --- libyul/CMakeLists.txt | 4 ++-- libyul/YulStack.cpp | 4 ++-- .../{SSACFGJsonExporter.cpp => io/JSONExporter.cpp} | 10 ++++------ .../ssa/{SSACFGJsonExporter.h => io/JSONExporter.h} | 2 +- 4 files changed, 9 insertions(+), 11 deletions(-) rename libyul/backends/evm/ssa/{SSACFGJsonExporter.cpp => io/JSONExporter.cpp} (95%) rename libyul/backends/evm/ssa/{SSACFGJsonExporter.h => io/JSONExporter.h} (95%) diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 2e62f6d946..d425283b69 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -86,12 +86,12 @@ add_library(yul backends/evm/ssa/SSACFG.h backends/evm/ssa/SSACFGBuilder.cpp backends/evm/ssa/SSACFGBuilder.h - backends/evm/ssa/SSACFGJsonExporter.cpp - backends/evm/ssa/SSACFGJsonExporter.h backends/evm/ssa/Stack.cpp backends/evm/ssa/Stack.h backends/evm/ssa/StackShuffler.cpp backends/evm/ssa/StackShuffler.h + backends/evm/ssa/io/JSONExporter.cpp + backends/evm/ssa/io/JSONExporter.h backends/evm/ssa/traversal/ForwardTopologicalSort.cpp backends/evm/ssa/traversal/ForwardTopologicalSort.h optimiser/ASTCopier.cpp diff --git a/libyul/YulStack.cpp b/libyul/YulStack.cpp index 34de47b929..ae32c0f25b 100644 --- a/libyul/YulStack.cpp +++ b/libyul/YulStack.cpp @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include @@ -405,7 +405,7 @@ Json YulStack::cfgJson() const keepLiteralAssignments ); std::unique_ptr liveness = std::make_unique(*controlFlow); - return ssa::json::exportControlFlow(*controlFlow, liveness.get()); + return ssa::io::json::exportControlFlow(*controlFlow, liveness.get()); }; std::function>)> exportCFGFromSubObjects; diff --git a/libyul/backends/evm/ssa/SSACFGJsonExporter.cpp b/libyul/backends/evm/ssa/io/JSONExporter.cpp similarity index 95% rename from libyul/backends/evm/ssa/SSACFGJsonExporter.cpp rename to libyul/backends/evm/ssa/io/JSONExporter.cpp index 9b03c13c95..df4e4d8148 100644 --- a/libyul/backends/evm/ssa/SSACFGJsonExporter.cpp +++ b/libyul/backends/evm/ssa/io/JSONExporter.cpp @@ -16,7 +16,7 @@ */ // SPDX-License-Identifier: GPL-3.0 -#include +#include #include @@ -31,7 +31,7 @@ using namespace solidity; using namespace solidity::yul; using namespace solidity::yul::ssa; -using namespace solidity::yul::ssa::json; +using namespace solidity::yul::ssa::io::json; namespace { @@ -134,8 +134,6 @@ Json exportBlock(SSACFG const& _cfg, SSACFG::BlockId _entryId, LivenessAnalysis Json blocksJson = Json::array(); util::BreadthFirstSearch bfs{{{_entryId}}}; bfs.run([&](SSACFG::BlockId _blockId, auto _addChild) { - auto const& block = _cfg.block(_blockId); - // Convert current block to JSON Json blockJson = toJson(_cfg, _blockId, _liveness); Json exitBlockJson = Json::object(); @@ -166,7 +164,7 @@ Json exportBlock(SSACFG const& _cfg, SSACFG::BlockId _entryId, LivenessAnalysis [&](SSACFG::BasicBlock::JumpTable const&) { yulAssert(false); } - }, block.exit); + }, _cfg.block(_blockId).exit); blockJson["exit"] = exitBlockJson; blocksJson.emplace_back(blockJson); }); @@ -188,7 +186,7 @@ Json exportFunction(SSACFG const& _cfg, LivenessAnalysis const* _liveness) } -Json json::exportControlFlow(ControlFlow const& _controlFlow, ControlFlowLiveness const* _liveness) +Json io::json::exportControlFlow(ControlFlow const& _controlFlow, ControlFlowLiveness const* _liveness) { if (_liveness) yulAssert(&_liveness->controlFlow.get() == &_controlFlow); diff --git a/libyul/backends/evm/ssa/SSACFGJsonExporter.h b/libyul/backends/evm/ssa/io/JSONExporter.h similarity index 95% rename from libyul/backends/evm/ssa/SSACFGJsonExporter.h rename to libyul/backends/evm/ssa/io/JSONExporter.h index 08f846fc5e..5cca634aa3 100644 --- a/libyul/backends/evm/ssa/SSACFGJsonExporter.h +++ b/libyul/backends/evm/ssa/io/JSONExporter.h @@ -21,7 +21,7 @@ #include #include -namespace solidity::yul::ssa::json +namespace solidity::yul::ssa::io::json { Json exportControlFlow(ControlFlow const& _controlFlow, ControlFlowLiveness const* _liveness); From 05457c1f171abe383500d9646d2ee0a860d525d5 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 9 Feb 2026 13:36:34 +0100 Subject: [PATCH 188/243] Add SSA CFG Dot exporter base --- libyul/CMakeLists.txt | 2 + .../backends/evm/ssa/io/DotExporterBase.cpp | 220 ++++++++++++++++++ libyul/backends/evm/ssa/io/DotExporterBase.h | 93 ++++++++ 3 files changed, 315 insertions(+) create mode 100644 libyul/backends/evm/ssa/io/DotExporterBase.cpp create mode 100644 libyul/backends/evm/ssa/io/DotExporterBase.h diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index d425283b69..d07b25a507 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -90,6 +90,8 @@ add_library(yul backends/evm/ssa/Stack.h backends/evm/ssa/StackShuffler.cpp backends/evm/ssa/StackShuffler.h + backends/evm/ssa/io/DotExporterBase.cpp + backends/evm/ssa/io/DotExporterBase.h backends/evm/ssa/io/JSONExporter.cpp backends/evm/ssa/io/JSONExporter.h backends/evm/ssa/traversal/ForwardTopologicalSort.cpp diff --git a/libyul/backends/evm/ssa/io/DotExporterBase.cpp b/libyul/backends/evm/ssa/io/DotExporterBase.cpp new file mode 100644 index 0000000000..23541422a5 --- /dev/null +++ b/libyul/backends/evm/ssa/io/DotExporterBase.cpp @@ -0,0 +1,220 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wtautological-compare" +#include +#pragma GCC diagnostic pop + +#include +#include + +using namespace solidity; +using namespace solidity::util; +using namespace solidity::yul; +using namespace solidity::yul::ssa; +using namespace solidity::yul::ssa::io; + +DotExporterBase::DotExporterBase(SSACFG const& _cfg, size_t const _functionIndex, RankDir const _rankDir): + m_cfg(_cfg), + m_functionIndex(_functionIndex), + m_rankDir(_rankDir) +{ +} + +std::string DotExporterBase::escapeId(std::string_view const _str) +{ + std::string result; + result.reserve(_str.size()); + for (auto const c: _str) + { + if (std::isalnum(static_cast(c)) || c == '_') + result += c; + else + result += '_'; + } + return result; +} + +std::string DotExporterBase::escapeLabel(std::string_view const _str) +{ + std::string result; + result.reserve(_str.size()); + for (auto const c: _str) + { + if (c == '"' || c == '\\') + result += '\\'; + result += c; + } + return result; +} + +std::string DotExporterBase::formatBlockHandle(SSACFG::BlockId _id) const +{ + return fmt::format("Block{}_{}", m_functionIndex, _id.value); +} + +std::string DotExporterBase::formatEdge(SSACFG::BlockId _source, SSACFG::BlockId _target, std::optional const& _exitPort) +{ + std::string const style = edgeStyle(_source, _target) == EdgeStyle::Dashed ? "dashed" : "solid"; + if (_exitPort) + return fmt::format("{}Exit:{} -> {} [style=\"{}\"];\n", formatBlockHandle(_source), *_exitPort, formatBlockHandle(_target), style); + else + return fmt::format("{}Exit -> {} [style=\"{}\"];\n", formatBlockHandle(_source), formatBlockHandle(_target), style); +} + +void DotExporterBase::writeBlock(std::ostream& _out, SSACFG::BlockId const _id) +{ + auto const& block = m_cfg.block(_id); + + auto const attributes = blockNodeAttributes(_id); + std::string attrStr; + for (auto const& [name, value]: attributes) + attrStr += fmt::format("{}={}, ", name, value); + _out << fmt::format("{} [{}label=\"", formatBlockHandle(_id), attrStr); + writeBlockLabel(_out, _id); + _out << "\"];\n"; + + std::visit(GenericVisitor{ + [&](SSACFG::BasicBlock::MainExit const&) + { + _out << fmt::format("{}Exit [label=\"MainExit\"];\n", formatBlockHandle(_id)); + _out << fmt::format("{} -> {}Exit;\n", formatBlockHandle(_id), formatBlockHandle(_id)); + }, + [&](SSACFG::BasicBlock::Jump const& _jump) + { + _out << fmt::format("{} -> {}Exit [arrowhead=none];\n", formatBlockHandle(_id), formatBlockHandle(_id)); + _out << fmt::format("{}Exit [label=\"Jump\" shape=oval];\n", formatBlockHandle(_id)); + _out << formatEdge(_id, _jump.target); + }, + [&](SSACFG::BasicBlock::ConditionalJump const& _conditionalJump) + { + _out << fmt::format("{} -> {}Exit;\n", formatBlockHandle(_id), formatBlockHandle(_id)); + _out << fmt::format( + "{}Exit [label=\"{{ If {} | {{ <0> Zero | <1> NonZero }}}}\" shape=Mrecord];\n", + formatBlockHandle(_id), _conditionalJump.condition.str(m_cfg) + ); + _out << formatEdge(_id, _conditionalJump.zero, "0"); + _out << formatEdge(_id, _conditionalJump.nonZero, "1"); + }, + [&](SSACFG::BasicBlock::JumpTable const& jt) + { + _out << fmt::format("{} -> {}Exit;\n", formatBlockHandle(_id), formatBlockHandle(_id)); + std::string options; + for (auto const& jumpCase: jt.cases) + { + if (!options.empty()) + options += " | "; + options += fmt::format("<{0}> {0}", formatNumber(jumpCase.first)); + } + if (!options.empty()) + options += " | "; + options += " default"; + _out << fmt::format("{}Exit [label=\"{{ JT | {{ {} }} }}\" shape=Mrecord];\n", formatBlockHandle(_id), options); + for (auto const& jumpCase: jt.cases) + _out << formatEdge(_id, jumpCase.second, formatNumber(jumpCase.first)); + _out << formatEdge(_id, jt.defaultCase, "default"); + }, + [&](SSACFG::BasicBlock::FunctionReturn const& fr) + { + auto const valueToString = [&](SSACFG::ValueId const& valueId) { return valueId.str(m_cfg); }; + _out << fmt::format("{}Exit [label=\"FunctionReturn[{}]\"];\n", + formatBlockHandle(_id), + fmt::join(fr.returnValues | ranges::views::transform(valueToString), ", ") + ); + _out << fmt::format("{} -> {}Exit;\n", formatBlockHandle(_id), formatBlockHandle(_id)); + }, + [&](SSACFG::BasicBlock::Terminated const&) + { + _out << fmt::format("{}Exit [label=\"Terminated\"];\n", formatBlockHandle(_id)); + _out << fmt::format("{} -> {}Exit;\n", formatBlockHandle(_id), formatBlockHandle(_id)); + } + }, block.exit); +} + +void DotExporterBase::traverse(std::ostream& _out, SSACFG::BlockId _entry) +{ + std::vector explored(m_cfg.numBlocks(), false); + explored[_entry.value] = true; + + std::deque toVisit{}; + toVisit.emplace_back(_entry); + + while (!toVisit.empty()) + { + auto const id = toVisit.front(); + toVisit.pop_front(); + writeBlock(_out, id); + m_cfg.block(id).forEachExit( + [&](SSACFG::BlockId const& _exitBlock) + { + if (!explored[_exitBlock.value]) + { + explored[_exitBlock.value] = true; + toVisit.emplace_back(_exitBlock); + } + } + ); + } +} + +std::string DotExporterBase::exportBlocks(SSACFG::BlockId _entry, bool _wrapInDigraph) +{ + std::ostringstream out; + if (_wrapInDigraph) + out << fmt::format("digraph SSACFG {{\nnodesep=0.7;\ngraph[fontname=\"DejaVu Sans\", rankdir={}]\nnode[shape=box,fontname=\"DejaVu Sans\"];\n\n", m_rankDir); + out << fmt::format("Entry [label=\"Entry\"];\n"); + out << fmt::format("Entry -> {};\n", formatBlockHandle(_entry)); + traverse(out, _entry); + if (_wrapInDigraph) + out << "}\n"; + return out.str(); +} + +std::string DotExporterBase::exportFunction(Scope::Function const& _function, bool _wrapInDigraph) +{ + std::ostringstream out; + if (_wrapInDigraph) + out << fmt::format("digraph SSACFG {{\nnodesep=0.7;\ngraph[fontname=\"DejaVu Sans\", rankdir={}]\nnode[shape=box,fontname=\"DejaVu Sans\"];\n\n", m_rankDir); + + static auto constexpr returnsTransform = [](auto const& functionReturnValue) { return escapeLabel(functionReturnValue.get().name.str()); }; + static auto constexpr argsTransform = [](auto const& arg) { return fmt::format("v{}", std::get<1>(arg).value()); }; + auto const entryHandle = fmt::format("FunctionEntry_{}_{}", escapeId(_function.name.str()), m_cfg.entry.value); + if (!m_cfg.returns.empty()) + out << fmt::format("{} [label=\"function {}:\n {} := {}({})\"];\n", + entryHandle, escapeLabel(_function.name.str()), + fmt::join(m_cfg.returns | ranges::views::transform(returnsTransform), ", "), + escapeLabel(_function.name.str()), + fmt::join(m_cfg.arguments | ranges::views::transform(argsTransform), ", ")); + else + out << fmt::format("{} [label=\"function {}:\n {}({})\"];\n", + entryHandle, escapeLabel(_function.name.str()), + escapeLabel(_function.name.str()), + fmt::join(m_cfg.arguments | ranges::views::transform(argsTransform), ", ")); + out << fmt::format("{} -> {};\n", entryHandle, formatBlockHandle(m_cfg.entry)); + traverse(out, m_cfg.entry); + + if (_wrapInDigraph) + out << "}\n"; + return out.str(); +} diff --git a/libyul/backends/evm/ssa/io/DotExporterBase.h b/libyul/backends/evm/ssa/io/DotExporterBase.h new file mode 100644 index 0000000000..f7fcdcda21 --- /dev/null +++ b/libyul/backends/evm/ssa/io/DotExporterBase.h @@ -0,0 +1,93 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include + +#include + +#include +#include +#include +#include + +namespace solidity::yul::ssa::io +{ + +/// Base class for exporting an SSACFG to Graphviz dot format. +/// Handles graph structure (BFS traversal, edges, exit nodes, entry nodes) and +/// delegates block content rendering to derived classes via writeBlockLabel(). +class DotExporterBase +{ +public: + enum class EdgeStyle { Solid, Dashed }; + enum class RankDir { TB, BT, LR, RL }; + + DotExporterBase(SSACFG const& _cfg, size_t _functionIndex, RankDir _rankDir = RankDir::LR); + virtual ~DotExporterBase() = default; + + std::string exportBlocks(SSACFG::BlockId _entry, bool _wrapInDigraph = true); + std::string exportFunction(Scope::Function const& _function, bool _wrapInDigraph = true); + +protected: + /// Override to write the content inside a block's dot label. + /// Everything between the opening `label="` and the closing `"` is the responsibility of this method. + virtual void writeBlockLabel(std::ostream& _out, SSACFG::BlockId _blockId) = 0; + + /// Override to provide extra node attributes (e.g., fillcolor). + virtual std::vector> blockNodeAttributes(SSACFG::BlockId) { return {}; } + + /// Override to customize edge style (e.g., dashed for back edges). + virtual EdgeStyle edgeStyle(SSACFG::BlockId, SSACFG::BlockId) { return EdgeStyle::Solid; } + + std::string formatBlockHandle(SSACFG::BlockId _id) const; + /// Escapes a string for use in dot node IDs (replaces non-alphanumeric characters with underscores). + static std::string escapeId(std::string_view _str); + /// Escapes a string for use inside dot label text (escapes quotes and backslashes). + static std::string escapeLabel(std::string_view _str); + + SSACFG const& m_cfg; + size_t m_functionIndex; + RankDir m_rankDir; + +private: + void writeBlock(std::ostream& _out, SSACFG::BlockId _id); + std::string formatEdge(SSACFG::BlockId _source, SSACFG::BlockId _target, std::optional const& _exitPort = std::nullopt); + void traverse(std::ostream& _out, SSACFG::BlockId _entry); +}; + +} + +template<> +struct fmt::formatter: fmt::formatter +{ + auto format(solidity::yul::ssa::io::DotExporterBase::RankDir _rankDir, fmt::format_context& _ctx) const + { + using RankDir = solidity::yul::ssa::io::DotExporterBase::RankDir; + std::string_view name; + switch (_rankDir) + { + case RankDir::TB: name = "TB"; break; + case RankDir::BT: name = "BT"; break; + case RankDir::LR: name = "LR"; break; + case RankDir::RL: name = "RL"; break; + } + return fmt::formatter::format(name, _ctx); + } +}; From e3c73c208717c11612b62f6ca5c137465bd23b9b Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 9 Feb 2026 13:37:02 +0100 Subject: [PATCH 189/243] Use Dot Exporter Base in `toDot` of SSACFG --- libyul/backends/evm/ssa/SSACFG.cpp | 319 +++++------------- .../libyul/yulSSAControlFlowGraph/complex.yul | 4 +- .../yulSSAControlFlowGraph/complex2.yul | 4 +- .../default_initialized_variable.yul | 4 +- .../yulSSAControlFlowGraph/function.yul | 4 +- test/libyul/yulSSAControlFlowGraph/if.yul | 4 +- .../yulSSAControlFlowGraph/if_const.yul | 4 +- .../yulSSAControlFlowGraph/nested_for.yul | 4 +- .../nested_function.yul | 4 +- test/libyul/yulSSAControlFlowGraph/switch.yul | 4 +- .../yulSSAControlFlowGraph/switch_const.yul | 4 +- 11 files changed, 109 insertions(+), 250 deletions(-) diff --git a/libyul/backends/evm/ssa/SSACFG.cpp b/libyul/backends/evm/ssa/SSACFG.cpp index a785c51aa2..24cc77091e 100644 --- a/libyul/backends/evm/ssa/SSACFG.cpp +++ b/libyul/backends/evm/ssa/SSACFG.cpp @@ -19,6 +19,8 @@ #include #include +#include +#include #include #include @@ -28,8 +30,6 @@ #include #pragma GCC diagnostic pop -#include - #include using namespace solidity; @@ -39,256 +39,120 @@ using namespace solidity::yul::ssa; namespace { -class SSACFGPrinter + +std::string formatPhi(SSACFG const& _cfg, SSACFG::PhiValue const& _phiValue) +{ + auto const transform = [&](SSACFG::ValueId const& valueId) { return valueId.str(_cfg); }; + std::vector formattedArgs; + formattedArgs.reserve(_phiValue.arguments.size()); + for (auto const& [arg, entry]: ranges::zip_view(_phiValue.arguments | ranges::views::transform(transform), _cfg.block(_phiValue.block).entries)) + formattedArgs.push_back(fmt::format("Block {} => {}", entry.value, arg)); + if (!formattedArgs.empty()) + return fmt::format("φ(\\l\\\n\t{}\\l\\\n)", fmt::join(formattedArgs, ",\\l\\\n\t")); + else + return "φ()"; +} + +class SSACFGDotExporter: public io::DotExporterBase { public: - SSACFGPrinter(SSACFG const& _cfg, SSACFG::BlockId _blockId, LivenessAnalysis const* _liveness): - m_cfg(_cfg), m_functionIndex(0), m_liveness(_liveness) + SSACFGDotExporter(SSACFG const& _cfg, size_t _functionIndex, LivenessAnalysis const* _liveness): + DotExporterBase(_cfg, _functionIndex), + m_liveness(_liveness) { if (_liveness) m_junkAdmittingBlocks = std::make_unique(_cfg, _liveness->topologicalSort()); - printBlock(_blockId); - } - SSACFGPrinter(SSACFG const& _cfg, size_t _functionIndex, Scope::Function const& _function, LivenessAnalysis const* _liveness): - m_cfg(_cfg), m_functionIndex(_functionIndex), m_liveness(_liveness) - { - if (_liveness) - m_junkAdmittingBlocks = std::make_unique(_cfg, _liveness->topologicalSort()); - printFunction(_function); - } - friend std::ostream& operator<<(std::ostream& stream, SSACFGPrinter const& printer) { - stream << printer.m_result.str(); - return stream; } -private: - static std::string escape(std::string_view const str) +protected: + void writeBlockLabel(std::ostream& _out, SSACFG::BlockId _blockId) override { - using namespace std::literals; - static constexpr auto replacements = std::array{std::make_tuple('$', "_d_")}; - std::stringstream ss; - for (auto const c: str) + auto const& block = m_cfg.block(_blockId); + auto const valueToString = [&](SSACFG::ValueId const& valueId) { return valueId.str(m_cfg); }; + + if (m_liveness) { - auto const it = std::find_if(replacements.begin(), replacements.end(), [c](auto const& replacement) - { - return std::get<0>(replacement) == c; - }); - if (it != replacements.end()) - ss << std::get<1>(*it); - else - ss << c; + _out << fmt::format( + "\\\nBlock {}; ({}, max {})\\n", + _blockId.value, + m_liveness->topologicalSort().preOrderIndexOf(_blockId.value), + m_liveness->topologicalSort().maxSubtreePreOrderIndexOf(_blockId.value) + ); + _out << fmt::format( + "LiveIn: {}\\l\\\n", + fmt::join(m_liveness->liveIn(_blockId) | ranges::views::transform([&](auto const& liveIn) { return valueToString(SSACFG::ValueId{liveIn.first}) + fmt::format("[{}]", liveIn.second); }), ", ") + ); + _out << fmt::format( + "LiveOut: {}\\l\\n", + fmt::join(m_liveness->liveOut(_blockId) | ranges::views::transform([&](auto const& liveOut) { return valueToString(SSACFG::ValueId{liveOut.first}) + fmt::format("[{}]", liveOut.second); }), ", ") + ); + auto const usedVariables = m_liveness->used(_blockId); + _out << fmt::format( + "Used: {}\\l\\n", + fmt::join(usedVariables | ranges::views::transform([&](auto const& used) { return valueToString(SSACFG::ValueId{used.first}) + fmt::format("[{}]", used.second); }), ", ") + ); } - return ss.str(); - } - - std::string formatBlockHandle(SSACFG::BlockId const& _id) const - { - return fmt::format("Block{}_{}", m_functionIndex, _id.value); - } - - std::string formatEdge(SSACFG::BlockId const& _v, SSACFG::BlockId const& _w, std::optional const& _vPort = std::nullopt) - { - std::string const style = m_liveness && m_liveness->topologicalSort().backEdge(_v, _w) ? "dashed" : "solid"; - if (_vPort) - return fmt::format("{}Exit:{} -> {} [style=\"{}\"];\n", formatBlockHandle(_v), *_vPort, formatBlockHandle(_w), style); - else - return fmt::format("{}Exit -> {} [style=\"{}\"];\n", formatBlockHandle(_v), formatBlockHandle(_w), style); - } - - static std::string formatPhi(SSACFG const& _cfg, SSACFG::PhiValue const& _phiValue) - { - auto const transform = [&](SSACFG::ValueId const& valueId) { return valueId.str(_cfg); }; - std::vector formattedArgs; - formattedArgs.reserve(_phiValue.arguments.size()); - for (auto const& [arg, entry]: ranges::zip_view(_phiValue.arguments | ranges::views::transform(transform), _cfg.block(_phiValue.block).entries)) - formattedArgs.push_back(fmt::format("Block {} => {}", entry.value, arg)); - if (!formattedArgs.empty()) - return fmt::format("φ(\\l\\\n\t{}\\l\\\n)", fmt::join(formattedArgs, ",\\l\\\n\t")); else - return "φ()"; - } + _out << fmt::format("\\\nBlock {}\\n", _blockId.value); - void writeBlock(SSACFG::BlockId const& _id, SSACFG::BasicBlock const& _block) - { - auto const valueToString = [&](SSACFG::ValueId const& valueId) { return valueId.str(m_cfg); }; - bool entryBlock = _id.value == 0 && m_functionIndex == 0; - if (entryBlock) + for (auto const& phi: block.phis) { - m_result << fmt::format("Entry{} [label=\"Entry\"];\n", m_functionIndex); - m_result << fmt::format("Entry{} -> {};\n", m_functionIndex, formatBlockHandle(_id)); + auto const& phiInfo = m_cfg.phiInfo(phi); + _out << fmt::format("phi{} := {}\\l\\\n", phi.value(), formatPhi(m_cfg, phiInfo)); } + for (auto const& operation: block.operations) { - std::string revertPathInfo; - if (m_junkAdmittingBlocks) - revertPathInfo = m_junkAdmittingBlocks->allowsAdditionOfJunk(_id) ? "fillcolor=\"#FF746C\", style=filled, " : ""; - if (m_liveness) - { - m_result << fmt::format( - "{} [{}label=\"\\\nBlock {}; ({}, max {})\\n", - formatBlockHandle(_id), - revertPathInfo, - _id.value, - m_liveness->topologicalSort().preOrderIndexOf(_id.value), - m_liveness->topologicalSort().maxSubtreePreOrderIndexOf(_id.value) - ); - m_result << fmt::format( - "LiveIn: {}\\l\\\n", - fmt::join(m_liveness->liveIn(_id) | ranges::views::transform([&](auto const& liveIn) { return valueToString(SSACFG::ValueId{liveIn.first}) + fmt::format("[{}]", liveIn.second); }), ", ") - ); - m_result << fmt::format( - "LiveOut: {}\\l\\n", - fmt::join(m_liveness->liveOut(_id) | ranges::views::transform([&](auto const& liveOut) { return valueToString(SSACFG::ValueId{liveOut.first}) + fmt::format("[{}]", liveOut.second); }), ", ") - ); - auto const usedVariables = m_liveness->used(_id); - m_result << fmt::format( - "Used: {}\\l\\n", - fmt::join(usedVariables | ranges::views::transform([&](auto const& used) { return valueToString(SSACFG::ValueId{used.first}) + fmt::format("[{}]", used.second); }), ", ") - ); - } - else - m_result << fmt::format("{} [{}label=\"\\\nBlock {}\\n", formatBlockHandle(_id), revertPathInfo, _id.value); - - for (auto const& phi: _block.phis) - { - auto const& phiInfo = m_cfg.phiInfo(phi); - m_result << fmt::format("phi{} := {}\\l\\\n", phi.value(), formatPhi(m_cfg, phiInfo)); - } - for (auto const& operation: _block.operations) - { - std::string const label = std::visit(GenericVisitor{ - [&](SSACFG::Call const& _call) { - return _call.function.get().name.str(); - }, - [&](SSACFG::BuiltinCall const& _call) { - return _call.builtin.get().name; - }, - [&](SSACFG::LiteralAssignment const&) - { - yulAssert(operation.inputs.size() == 1); - return operation.inputs.back().str(m_cfg); - } - }, operation.kind); - if (!operation.outputs.empty()) - m_result << fmt::format( - "{} := ", - fmt::join(operation.outputs | ranges::views::transform(valueToString), ", ") - ); - if (std::holds_alternative(operation.kind)) - m_result << fmt::format( - "{}\\l\\\n", - escape(label) - ); - else - m_result << fmt::format( - "{}({})\\l\\\n", - escape(label), - fmt::join(operation.inputs | ranges::views::transform(valueToString), ", ") - ); - } - m_result << "\"];\n"; - std::visit(GenericVisitor{ - [&](SSACFG::BasicBlock::MainExit const&) - { - m_result << fmt::format("{}Exit [label=\"MainExit\"];\n", formatBlockHandle(_id)); - m_result << fmt::format("{} -> {}Exit;\n", formatBlockHandle(_id), formatBlockHandle(_id)); - }, - [&](SSACFG::BasicBlock::Jump const& _jump) - { - m_result << fmt::format("{} -> {}Exit [arrowhead=none];\n", formatBlockHandle(_id), formatBlockHandle(_id)); - m_result << fmt::format("{}Exit [label=\"Jump\" shape=oval];\n", formatBlockHandle(_id)); - m_result << formatEdge(_id, _jump.target); - }, - [&](SSACFG::BasicBlock::ConditionalJump const& _conditionalJump) - { - m_result << fmt::format("{} -> {}Exit;\n", formatBlockHandle(_id), formatBlockHandle(_id)); - m_result << fmt::format( - "{}Exit [label=\"{{ If {} | {{ <0> Zero | <1> NonZero }}}}\" shape=Mrecord];\n", - formatBlockHandle(_id), _conditionalJump.condition.str(m_cfg) - ); - m_result << formatEdge(_id, _conditionalJump.zero, "0"); - m_result << formatEdge(_id, _conditionalJump.nonZero, "1"); - }, - [&](SSACFG::BasicBlock::JumpTable const& jt) - { - m_result << fmt::format("{} -> {}Exit;\n", formatBlockHandle(_id), formatBlockHandle(_id)); - std::string options; - for (auto const& jumpCase: jt.cases) - { - if (!options.empty()) - options += " | "; - options += fmt::format("<{0}> {0}", formatNumber(jumpCase.first)); - } - if (!options.empty()) - options += " | "; - options += " default"; - m_result << fmt::format("{}Exit [label=\"{{ JT | {{ {} }} }}\" shape=Mrecord];\n", formatBlockHandle(_id), options); - for (auto const& jumpCase: jt.cases) - m_result << formatEdge(_id, jumpCase.second, formatNumber(jumpCase.first)); - m_result << formatEdge(_id, jt.defaultCase, "default"); + std::string const label = std::visit(GenericVisitor{ + [&](SSACFG::Call const& _call) { + return _call.function.get().name.str(); }, - [&](SSACFG::BasicBlock::FunctionReturn const& fr) - { - m_result << formatBlockHandle(_id) << "Exit [label=\"FunctionReturn[" - << fmt::format("{}", fmt::join(fr.returnValues | ranges::views::transform(valueToString), ", ")) - << "]\"];\n"; - m_result << formatBlockHandle(_id) << " -> " << formatBlockHandle(_id) << "Exit;\n"; + [&](SSACFG::BuiltinCall const& _call) { + return _call.builtin.get().name; }, - [&](SSACFG::BasicBlock::Terminated const&) + [&](SSACFG::LiteralAssignment const&) { - m_result << formatBlockHandle(_id) << "Exit [label=\"Terminated\"];\n"; - m_result << formatBlockHandle(_id) << " -> " << formatBlockHandle(_id) << "Exit;\n"; + yulAssert(operation.inputs.size() == 1); + return operation.inputs.back().str(m_cfg); } - }, _block.exit); + }, operation.kind); + if (!operation.outputs.empty()) + _out << fmt::format( + "{} := ", + fmt::join(operation.outputs | ranges::views::transform(valueToString), ", ") + ); + if (std::holds_alternative(operation.kind)) + _out << fmt::format( + "{}\\l\\\n", + escapeLabel(label) + ); + else + _out << fmt::format( + "{}({})\\l\\\n", + escapeLabel(label), + fmt::join(operation.inputs | ranges::views::transform(valueToString), ", ") + ); } } - void printBlock(SSACFG::BlockId const& _rootId) + std::vector> blockNodeAttributes(SSACFG::BlockId _blockId) override { - std::set explored{}; - explored.insert(_rootId); - - std::deque toVisit{}; - toVisit.emplace_back(_rootId); - - while (!toVisit.empty()) - { - auto const id = toVisit.front(); - toVisit.pop_front(); - auto const& block = m_cfg.block(id); - writeBlock(id, block); - block.forEachExit( - [&](SSACFG::BlockId const& _exitBlock) - { - if (explored.count(_exitBlock) == 0) - { - explored.insert(_exitBlock); - toVisit.emplace_back(_exitBlock); - } - } - ); - } + if (m_junkAdmittingBlocks && m_junkAdmittingBlocks->allowsAdditionOfJunk(_blockId)) + return {{"fillcolor", "\"#FF746C\""}, {"style", "filled"}}; + return {}; } - void printFunction(Scope::Function const& _fun) + EdgeStyle edgeStyle(SSACFG::BlockId _source, SSACFG::BlockId _target) override { - static auto constexpr returnsTransform = [](auto const& functionReturnValue) { return escape(functionReturnValue.get().name.str()); }; - static auto constexpr argsTransform = [](auto const& arg) { return fmt::format("v{}", std::get<1>(arg).value()); }; - m_result << "FunctionEntry_" << escape(_fun.name.str()) << "_" << m_cfg.entry.value << " [label=\""; - if (!m_cfg.returns.empty()) - m_result << fmt::format("function {0}:\n {1} := {0}({2})", escape(_fun.name.str()), fmt::join(m_cfg.returns | ranges::views::transform(returnsTransform), ", "), fmt::join(m_cfg.arguments | ranges::views::transform(argsTransform), ", ")); - else - m_result << fmt::format("function {0}:\n {0}({1})", escape(_fun.name.str()), fmt::join(m_cfg.arguments | ranges::views::transform(argsTransform), ", ")); - m_result << "\"];\n"; - m_result << "FunctionEntry_" << escape(_fun.name.str()) << "_" << m_cfg.entry.value << " -> Block" << m_functionIndex << "_" << m_cfg.entry.value << ";\n"; - printBlock(m_cfg.entry); + if (m_liveness && m_liveness->topologicalSort().backEdge(_source, _target)) + return EdgeStyle::Dashed; + return EdgeStyle::Solid; } - SSACFG const& m_cfg; - std::unique_ptr m_junkAdmittingBlocks; - size_t m_functionIndex; +private: LivenessAnalysis const* m_liveness; - std::stringstream m_result{}; + std::unique_ptr m_junkAdmittingBlocks; }; + } std::string SSACFG::ValueId::str(SSACFG const& _cfg) const @@ -312,14 +176,9 @@ std::string SSACFG::toDot( LivenessAnalysis const* _liveness ) const { - std::ostringstream output; - if (_includeDiGraphDefinition) - output << "digraph SSACFG {\nnodesep=0.7;\ngraph[fontname=\"DejaVu Sans\", rankdir=LR]\nnode[shape=box,fontname=\"DejaVu Sans\"];\n\n"; + SSACFGDotExporter exporter(*this, _functionIndex.value_or(function ? 1 : 0), _liveness); if (function) - output << SSACFGPrinter(*this, _functionIndex ? *_functionIndex : static_cast(1), *function, _liveness); + return exporter.exportFunction(*function, _includeDiGraphDefinition); else - output << SSACFGPrinter(*this, entry, _liveness); - if (_includeDiGraphDefinition) - output << "}\n"; - return output.str(); + return exporter.exportBlocks(entry, _includeDiGraphDefinition); } diff --git a/test/libyul/yulSSAControlFlowGraph/complex.yul b/test/libyul/yulSSAControlFlowGraph/complex.yul index 24ab7af125..4f16122e31 100644 --- a/test/libyul/yulSSAControlFlowGraph/complex.yul +++ b/test/libyul/yulSSAControlFlowGraph/complex.yul @@ -49,8 +49,8 @@ // graph[fontname="DejaVu Sans"] // node[shape=box,fontname="DejaVu Sans"]; // -// Entry0 [label="Entry"]; -// Entry0 -> Block0_0; +// Entry [label="Entry"]; +// Entry -> Block0_0; // Block0_0 [fillcolor="#FF746C", style=filled, label="\ // Block 0; (0, max 0)\nLiveIn: \l\ // LiveOut: \l\nUsed: \l\nv0 := f(0x02, 0x01)\l\ diff --git a/test/libyul/yulSSAControlFlowGraph/complex2.yul b/test/libyul/yulSSAControlFlowGraph/complex2.yul index 9910d8356b..d82c400e8d 100644 --- a/test/libyul/yulSSAControlFlowGraph/complex2.yul +++ b/test/libyul/yulSSAControlFlowGraph/complex2.yul @@ -56,8 +56,8 @@ // graph[fontname="DejaVu Sans"] // node[shape=box,fontname="DejaVu Sans"]; // -// Entry0 [label="Entry"]; -// Entry0 -> Block0_0; +// Entry [label="Entry"]; +// Entry -> Block0_0; // Block0_0 [fillcolor="#FF746C", style=filled, label="\ // Block 0; (0, max 0)\nLiveIn: \l\ // LiveOut: \l\nUsed: \l\nsstore(0x01, 0x01)\l\ diff --git a/test/libyul/yulSSAControlFlowGraph/default_initialized_variable.yul b/test/libyul/yulSSAControlFlowGraph/default_initialized_variable.yul index 14a80dadc7..45893e8ac2 100644 --- a/test/libyul/yulSSAControlFlowGraph/default_initialized_variable.yul +++ b/test/libyul/yulSSAControlFlowGraph/default_initialized_variable.yul @@ -11,8 +11,8 @@ // graph[fontname="DejaVu Sans"] // node[shape=box,fontname="DejaVu Sans"]; // -// Entry0 [label="Entry"]; -// Entry0 -> Block0_0; +// Entry [label="Entry"]; +// Entry -> Block0_0; // Block0_0 [label="\ // Block 0; (0, max 2)\nLiveIn: \l\ // LiveOut: v0[1]\l\nUsed: \l\nv0 := 0x00\l\ diff --git a/test/libyul/yulSSAControlFlowGraph/function.yul b/test/libyul/yulSSAControlFlowGraph/function.yul index f5090b8265..e2140d3169 100644 --- a/test/libyul/yulSSAControlFlowGraph/function.yul +++ b/test/libyul/yulSSAControlFlowGraph/function.yul @@ -24,8 +24,8 @@ // graph[fontname="DejaVu Sans"] // node[shape=box,fontname="DejaVu Sans"]; // -// Entry0 [label="Entry"]; -// Entry0 -> Block0_0; +// Entry [label="Entry"]; +// Entry -> Block0_0; // Block0_0 [fillcolor="#FF746C", style=filled, label="\ // Block 0; (0, max 0)\nLiveIn: \l\ // LiveOut: \l\nUsed: \l\nv0, v1 := i()\l\ diff --git a/test/libyul/yulSSAControlFlowGraph/if.yul b/test/libyul/yulSSAControlFlowGraph/if.yul index 540947e180..f884d33486 100644 --- a/test/libyul/yulSSAControlFlowGraph/if.yul +++ b/test/libyul/yulSSAControlFlowGraph/if.yul @@ -12,8 +12,8 @@ // graph[fontname="DejaVu Sans"] // node[shape=box,fontname="DejaVu Sans"]; // -// Entry0 [label="Entry"]; -// Entry0 -> Block0_0; +// Entry [label="Entry"]; +// Entry -> Block0_0; // Block0_0 [label="\ // Block 0; (0, max 2)\nLiveIn: \l\ // LiveOut: v0[1]\l\nUsed: \l\nv0 := calldataload(0x03)\l\ diff --git a/test/libyul/yulSSAControlFlowGraph/if_const.yul b/test/libyul/yulSSAControlFlowGraph/if_const.yul index d8be1eaf2f..f8bab3f481 100644 --- a/test/libyul/yulSSAControlFlowGraph/if_const.yul +++ b/test/libyul/yulSSAControlFlowGraph/if_const.yul @@ -17,8 +17,8 @@ // graph[fontname="DejaVu Sans"] // node[shape=box,fontname="DejaVu Sans"]; // -// Entry0 [label="Entry"]; -// Entry0 -> Block0_0; +// Entry [label="Entry"]; +// Entry -> Block0_0; // Block0_0 [fillcolor="#FF746C", style=filled, label="\ // Block 0; (0, max 0)\nLiveIn: \l\ // LiveOut: \l\nUsed: \l\nv0 := calldataload(0x03)\l\ diff --git a/test/libyul/yulSSAControlFlowGraph/nested_for.yul b/test/libyul/yulSSAControlFlowGraph/nested_for.yul index e9e5751400..55edb2ea67 100644 --- a/test/libyul/yulSSAControlFlowGraph/nested_for.yul +++ b/test/libyul/yulSSAControlFlowGraph/nested_for.yul @@ -23,8 +23,8 @@ // graph[fontname="DejaVu Sans"] // node[shape=box,fontname="DejaVu Sans"]; // -// Entry0 [label="Entry"]; -// Entry0 -> Block0_0; +// Entry [label="Entry"]; +// Entry -> Block0_0; // Block0_0 [fillcolor="#FF746C", style=filled, label="\ // Block 0; (0, max 24)\nLiveIn: \l\ // LiveOut: v0[1]\l\nUsed: \l\nv0 := 0x00\l\ diff --git a/test/libyul/yulSSAControlFlowGraph/nested_function.yul b/test/libyul/yulSSAControlFlowGraph/nested_function.yul index 2ae7f5522b..876768d665 100644 --- a/test/libyul/yulSSAControlFlowGraph/nested_function.yul +++ b/test/libyul/yulSSAControlFlowGraph/nested_function.yul @@ -35,8 +35,8 @@ // graph[fontname="DejaVu Sans"] // node[shape=box,fontname="DejaVu Sans"]; // -// Entry0 [label="Entry"]; -// Entry0 -> Block0_0; +// Entry [label="Entry"]; +// Entry -> Block0_0; // Block0_0 [fillcolor="#FF746C", style=filled, label="\ // Block 0; (0, max 0)\nLiveIn: \l\ // LiveOut: \l\nUsed: \l\n"]; diff --git a/test/libyul/yulSSAControlFlowGraph/switch.yul b/test/libyul/yulSSAControlFlowGraph/switch.yul index 56e50d5629..3114bbca36 100644 --- a/test/libyul/yulSSAControlFlowGraph/switch.yul +++ b/test/libyul/yulSSAControlFlowGraph/switch.yul @@ -19,8 +19,8 @@ // graph[fontname="DejaVu Sans"] // node[shape=box,fontname="DejaVu Sans"]; // -// Entry0 [label="Entry"]; -// Entry0 -> Block0_0; +// Entry [label="Entry"]; +// Entry -> Block0_0; // Block0_0 [label="\ // Block 0; (0, max 5)\nLiveIn: \l\ // LiveOut: v1[1]\l\nUsed: \l\nv0 := calldataload(0x03)\l\ diff --git a/test/libyul/yulSSAControlFlowGraph/switch_const.yul b/test/libyul/yulSSAControlFlowGraph/switch_const.yul index 559061195a..56116eb3b1 100644 --- a/test/libyul/yulSSAControlFlowGraph/switch_const.yul +++ b/test/libyul/yulSSAControlFlowGraph/switch_const.yul @@ -41,8 +41,8 @@ // graph[fontname="DejaVu Sans"] // node[shape=box,fontname="DejaVu Sans"]; // -// Entry0 [label="Entry"]; -// Entry0 -> Block0_0; +// Entry [label="Entry"]; +// Entry -> Block0_0; // Block0_0 [fillcolor="#FF746C", style=filled, label="\ // Block 0; (0, max 0)\nLiveIn: \l\ // LiveOut: \l\nUsed: \l\nv0 := calldataload(0x03)\l\ From 025600fa4517bef01ed9e972f38137ff9dd5e38b Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:56:08 +0100 Subject: [PATCH 190/243] Add phi inverse function for ssa-based cfgs --- libyul/CMakeLists.txt | 2 ++ libyul/backends/evm/ssa/PhiInverse.cpp | 46 ++++++++++++++++++++++++++ libyul/backends/evm/ssa/PhiInverse.h | 46 ++++++++++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 libyul/backends/evm/ssa/PhiInverse.cpp create mode 100644 libyul/backends/evm/ssa/PhiInverse.h diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index d07b25a507..2c0f6d4465 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -80,6 +80,8 @@ add_library(yul backends/evm/ssa/JunkAdmittingBlocksFinder.h backends/evm/ssa/LivenessAnalysis.cpp backends/evm/ssa/LivenessAnalysis.h + backends/evm/ssa/PhiInverse.cpp + backends/evm/ssa/PhiInverse.h backends/evm/ssa/SSACFGLoopNestingForest.cpp backends/evm/ssa/SSACFGLoopNestingForest.h backends/evm/ssa/SSACFG.cpp diff --git a/libyul/backends/evm/ssa/PhiInverse.cpp b/libyul/backends/evm/ssa/PhiInverse.cpp new file mode 100644 index 0000000000..c88b132ade --- /dev/null +++ b/libyul/backends/evm/ssa/PhiInverse.cpp @@ -0,0 +1,46 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +using namespace solidity::yul::ssa; + +PhiInverse::PhiInverse(SSACFG const& _cfg, SSACFG::BlockId const& _from, SSACFG::BlockId const& _to) +{ + auto const argIndex = _cfg.phiArgumentIndex(_from, _to); + for (auto const& phiId: _cfg.block(_to).phis) + { + auto const& phiInfo = _cfg.phiInfo(phiId); + m_phiToPreImage[phiId] = phiInfo.arguments[argIndex]; + } +} + +bool PhiInverse::noOp() const +{ + return m_phiToPreImage.empty(); +} + +SSACFG::ValueId PhiInverse::operator()(SSACFG::ValueId _valueId) const +{ + return util::valueOrDefault(m_phiToPreImage, _valueId, _valueId); +} + +std::map const& PhiInverse::data() const +{ + return m_phiToPreImage; +} diff --git a/libyul/backends/evm/ssa/PhiInverse.h b/libyul/backends/evm/ssa/PhiInverse.h new file mode 100644 index 0000000000..3f29aae468 --- /dev/null +++ b/libyul/backends/evm/ssa/PhiInverse.h @@ -0,0 +1,46 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include + +#include + +namespace solidity::yul::ssa +{ + +/// If Block `_from` -> Block `_to` and `_to` has phi functions `v_k := phi(..., _from => v_i, ...)`, this transform +/// pulls values `v_k` back to `v_i`. +class PhiInverse +{ +public: + PhiInverse() = default; + PhiInverse(SSACFG const& _cfg, SSACFG::BlockId const& _from, SSACFG::BlockId const& _to); + + /// whether the transform is guaranteed to be a no-op, ie, there is no phi function in `_to` + bool noOp() const; + SSACFG::ValueId operator()(SSACFG::ValueId _valueId) const; + + std::map const& data() const; + +private: + std::map m_phiToPreImage = {}; +}; + +} From b3a7948f07fec360c9841ee2237fe36823a23caa Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:56:29 +0100 Subject: [PATCH 191/243] ssa-cfg stack abstraction can also declare junk based on offsets --- libyul/backends/evm/ssa/Stack.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libyul/backends/evm/ssa/Stack.h b/libyul/backends/evm/ssa/Stack.h index 95ff377e7d..834e21078d 100644 --- a/libyul/backends/evm/ssa/Stack.h +++ b/libyul/backends/evm/ssa/Stack.h @@ -250,7 +250,8 @@ class Stack bool swapReachable(Offset const& _offset) const noexcept { return swapReachable(offsetToDepth(_offset)); } bool swapReachable(Depth const& _depth) const noexcept { return _depth < size() && 1 <= _depth.value && _depth.value <= reachableStackDepth; } - void declareJunk(Depth const& _depth) { (*m_data)[depthToOffset(_depth).value] = Slot::makeJunk(); } + void declareJunk(Offset const& _offset) { (*m_data)[_offset.value] = Slot::makeJunk(); } + void declareJunk(Depth const& _depth) { declareJunk(depthToOffset(_depth)); } Slot const& slot(Depth const& _depth) const { return (*m_data)[depthToOffset(_depth).value]; } Slot const& slot(Offset const& _offset) const { return slot(offsetToDepth(_offset)); } From 746871abd44bbd8cf79882749f56b0d150386ee9 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:57:07 +0100 Subject: [PATCH 192/243] Define what constitutes a stack layout for SSA-CFGs --- libyul/CMakeLists.txt | 1 + libyul/backends/evm/ssa/StackLayout.h | 60 +++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 libyul/backends/evm/ssa/StackLayout.h diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 2c0f6d4465..893b32e94d 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -90,6 +90,7 @@ add_library(yul backends/evm/ssa/SSACFGBuilder.h backends/evm/ssa/Stack.cpp backends/evm/ssa/Stack.h + backends/evm/ssa/StackLayout.h backends/evm/ssa/StackShuffler.cpp backends/evm/ssa/StackShuffler.h backends/evm/ssa/io/DotExporterBase.cpp diff --git a/libyul/backends/evm/ssa/StackLayout.h b/libyul/backends/evm/ssa/StackLayout.h new file mode 100644 index 0000000000..d0cd819726 --- /dev/null +++ b/libyul/backends/evm/ssa/StackLayout.h @@ -0,0 +1,60 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include + +#include + +namespace solidity::yul::ssa +{ + +struct BlockLayout +{ + // stack layout required to enter the block + StackData stackIn; + // stack layout required to execute the i-th operation in the block + std::vector operationIn; + // stack after the block was executed + StackData stackOut; +}; + +/// For each (reachable) block in the SSACFG one block layout +class SSACFGStackLayout +{ +public: + SSACFGStackLayout(std::size_t const _numBlocks): m_blockLayouts(_numBlocks) {} + + std::optional& operator[](SSACFG::BlockId const& _blockId) + { + yulAssert(_blockId.hasValue() && _blockId.value < m_blockLayouts.size()); + return m_blockLayouts[_blockId.value]; + } + + std::optional const& operator[](SSACFG::BlockId const& _blockId) const + { + yulAssert(_blockId.hasValue() && _blockId.value < m_blockLayouts.size()); + return m_blockLayouts[_blockId.value]; + } + +private: + std::vector> m_blockLayouts; +}; + +} From d2de781babf0c32a6a24f46d4296b8ba558907f5 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:57:27 +0100 Subject: [PATCH 193/243] Add SSA-CFG stack layout generator --- libyul/CMakeLists.txt | 2 + .../backends/evm/ssa/StackLayoutGenerator.cpp | 311 ++++++++++++++++++ .../backends/evm/ssa/StackLayoutGenerator.h | 51 +++ 3 files changed, 364 insertions(+) create mode 100644 libyul/backends/evm/ssa/StackLayoutGenerator.cpp create mode 100644 libyul/backends/evm/ssa/StackLayoutGenerator.h diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 893b32e94d..dcc0f923a0 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -91,6 +91,8 @@ add_library(yul backends/evm/ssa/Stack.cpp backends/evm/ssa/Stack.h backends/evm/ssa/StackLayout.h + backends/evm/ssa/StackLayoutGenerator.cpp + backends/evm/ssa/StackLayoutGenerator.h backends/evm/ssa/StackShuffler.cpp backends/evm/ssa/StackShuffler.h backends/evm/ssa/io/DotExporterBase.cpp diff --git a/libyul/backends/evm/ssa/StackLayoutGenerator.cpp b/libyul/backends/evm/ssa/StackLayoutGenerator.cpp new file mode 100644 index 0000000000..04abd8b644 --- /dev/null +++ b/libyul/backends/evm/ssa/StackLayoutGenerator.cpp @@ -0,0 +1,311 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +using namespace solidity::yul::ssa; + +namespace +{ +void handlePhiFunctions(StackData& _stackData, PhiInverse const& _phiInverse, LivenessAnalysis::LivenessData const& _liveness) +{ + // add any phi function values here that are not already contained in the stack + for (auto const& [phi, preImage]: _phiInverse.data()) + { + auto reversedStackData = _stackData | ranges::views::reverse; + auto it = ranges::find(reversedStackData, StackSlot::makeValueID(preImage)); + if (_liveness.contains(preImage)) + { + // Both the phi function and the preimage are part of the live-in set. + // If the preimage occurs more than once on the stack, one occurrence is + // symbolically replaced by the phi function; otherwise, we push the phi value. + if (ranges::count(_stackData, StackSlot::makeValueID(preImage)) > 1) + *it = StackSlot::makeValueID(phi); + else + _stackData.emplace_back(StackSlot::makeValueID(phi)); + } + else + { + // replace all occurrences of the preimage with the phi value + ranges::replace(_stackData, StackSlot::makeValueID(preImage), StackSlot::makeValueID(phi)); + // if it's not contained, push it (could be derived from a literal) + if (it == ranges::end(reversedStackData)) + _stackData.emplace_back(StackSlot::makeValueID(phi)); + } + } +} + +using StackType = Stack; + +void declareJunk(StackType& _stack, LivenessAnalysis::LivenessData const& _live) +{ + for (StackOffset offset{0}; offset < _stack.size(); ++offset.value) + { + auto const& slot = _stack[offset]; + if (slot.isValueID() && !_live.contains(slot.valueID())) + _stack.declareJunk(offset); + } +} + +} + +SSACFGStackLayout StackLayoutGenerator::generate(LivenessAnalysis const& _liveness, CallSites const& _callSites) +{ + return StackLayoutGenerator(_liveness, _callSites).m_resultLayout; +} + +StackLayoutGenerator::StackLayoutGenerator(LivenessAnalysis const& _liveness, CallSites const& _callSites): + m_cfg(_liveness.cfg()), + m_liveness(_liveness), + m_callSites(_callSites), + m_junkAdmittingBlocksFinder(std::make_unique(_liveness.cfg(), _liveness.topologicalSort())), + m_resultLayout(m_cfg.numBlocks()) +{ + // traverse the cfg layer-wise using Kahn's algorithm: + // if a block is visited, the predecessors of that block have already their exit layouts defined + // with minor exceptions when dealing with back-edges + { + // Future optimization: it might be beneficial to revisit the loop heads (back edge targets) after the first iteration + std::vector inDegreesIgnoringBackedges(m_cfg.numBlocks(), 0); + + for (SSACFG::BlockId id{0}; id.value < m_cfg.numBlocks(); ++id.value) + for (auto const& entry: m_cfg.block(id).entries) + if (!m_liveness.topologicalSort().backEdge(entry, id)) + inDegreesIgnoringBackedges[id.value] += 1; + + std::queue traversalQueue; + traversalQueue.push(m_cfg.entry); + + std::size_t numVisited = 0; + while (!traversalQueue.empty()) + { + auto currentBlockId = traversalQueue.front(); + traversalQueue.pop(); + + visitBlock(currentBlockId); + + m_cfg.block(currentBlockId).forEachExit([&](SSACFG::BlockId const& _exit){ + if (--inDegreesIgnoringBackedges[_exit.value] == 0) + traversalQueue.push(_exit); + }); + ++numVisited; + } + yulAssert(numVisited == m_liveness.topologicalSort().preOrder().size()); + } +} + +void StackLayoutGenerator::defineStackIn(SSACFG::BlockId const& _blockId) +{ + // we already have an input layout defined, return + if (m_resultLayout[_blockId]) + return; + BlockLayout blockLayout{}; + + if (_blockId == m_cfg.entry) + { + if (m_cfg.function) + blockLayout.stackIn = + m_cfg.arguments | + ranges::views::reverse | + ranges::views::transform([](auto&& _variableAndValueId) -> Slot { return Slot::makeValueID(std::get<1>(_variableAndValueId)); }) | + ranges::to; + m_resultLayout[_blockId] = blockLayout; + return; + } + + auto const& block = m_cfg.block(_blockId); + + std::vector> parentExits; + for (auto const& entry: block.entries) + if (m_resultLayout[entry]) + parentExits.emplace_back(entry, &m_resultLayout[entry]->stackOut); + + yulAssert(!parentExits.empty(), fmt::format("None of the parents of block {} were generated", _blockId)); + + if (block.entries.size() == 1) + { + // pass through + yulAssert(parentExits.size() == 1); + blockLayout.stackIn = *parentExits[0].second; + + if (!block.phis.empty()) + handlePhiFunctions(blockLayout.stackIn, PhiInverse(m_cfg, parentExits[0].first, _blockId), m_liveness.liveIn(_blockId)); + } + else + { + // we have more than one entry and need to unify or at the very least apply phi fct. + auto const& liveIn = m_liveness.liveIn(_blockId); + std::vector cumulativeCosts(parentExits.size(), std::numeric_limits::max()); + for (std::size_t i = 0; i < parentExits.size(); ++i) + { + if (!parentExits[i].second) + continue; + std::size_t cumulativeCost = 0; + auto referenceStackIn = *parentExits[i].second; + { + StackType stack(referenceStackIn, {}); + declareJunk(stack, liveIn); + } + handlePhiFunctions(referenceStackIn, PhiInverse(m_cfg, parentExits[i].first, _blockId), liveIn); + for (std::size_t j = 0; j < parentExits.size(); ++j) + { + if (j != i) + { + if (parentExits[j].second != nullptr) + { + auto otherStackIn = *parentExits[j].second; + StackType stack(otherStackIn, {}); + StackShuffler::shuffle( + stack, + stackPreImage(referenceStackIn, PhiInverse(m_cfg, parentExits[j].first, _blockId)), + {}, + referenceStackIn.size() + ); + cumulativeCost += stack.callbacks().numOps; + } + } + } + cumulativeCosts[i] = cumulativeCost; + } + auto const argMin = static_cast( + std::distance(cumulativeCosts.begin(), ranges::min_element(cumulativeCosts)) + ); + yulAssert(parentExits[argMin].second); + blockLayout.stackIn = *parentExits[argMin].second; + StackType stack(blockLayout.stackIn, {}); + declareJunk(stack, liveIn); + handlePhiFunctions(blockLayout.stackIn, PhiInverse(m_cfg, parentExits[argMin].first, _blockId), liveIn); + } + m_resultLayout[_blockId] = blockLayout; +} + +void StackLayoutGenerator::visitBlock(SSACFG::BlockId const& _blockId) +{ + defineStackIn(_blockId); + yulAssert(m_resultLayout[_blockId]); + BlockLayout& blockLayout = *m_resultLayout[_blockId]; + + SSACFG::BasicBlock const& block = m_cfg.block(_blockId); + + StackData currentStackData = blockLayout.stackIn; + StackType stack(currentStackData, {}); + bool const junkCanBeAdded = m_junkAdmittingBlocksFinder->allowsAdditionOfJunk(_blockId); + + auto const& operationsLiveOut = m_liveness.operationsLiveOut(_blockId); + blockLayout.operationIn.reserve(block.operations.size()); + for (std::size_t operationIndex = 0; operationIndex < block.operations.size(); ++operationIndex) + { + SSACFG::Operation const& operation = block.operations[operationIndex]; + LivenessAnalysis::LivenessData opLiveOut = operationsLiveOut[operationIndex]; + auto opLiveOutWithoutOutputs = opLiveOut; + for (auto const& output: operation.outputs) + opLiveOutWithoutOutputs.erase(output); + + std::vector requiredStackTop; + if (auto const* call = std::get_if(&operation.kind)) + if (call->canContinue) + { + auto const callSiteID = m_callSites.callSiteID(&call->call.get()); + yulAssert(callSiteID.has_value()); + requiredStackTop.emplace_back(Slot::makeFunctionCallReturnLabel(*callSiteID)); + } + requiredStackTop += operation.inputs | ranges::views::transform(Slot::makeValueID); + + for (StackType::Depth depth{0}; depth < stack.size(); ++depth.value) + if ( + stack.slot(depth).isValueID() && + !opLiveOutWithoutOutputs.contains(stack.slot(depth).valueID()) && + ranges::find(requiredStackTop, stack.slot(depth)) == ranges::end(requiredStackTop) + ) + stack.declareJunk(depth); + + std::size_t const targetSize = findOptimalTargetSize(stack.data(), requiredStackTop, opLiveOutWithoutOutputs, junkCanBeAdded); + StackShuffler::shuffle( + stack, + requiredStackTop, + opLiveOutWithoutOutputs, + targetSize + ); + + blockLayout.operationIn.push_back(currentStackData); + for (std::size_t i = 0; i < requiredStackTop.size(); ++i) + stack.pop(); + for (auto const& val: operation.outputs) + stack.push(Slot::makeValueID(val)); + } + + if (auto const* cjump = std::get_if(&block.exit)) + { + auto const& zeroLiveIn = m_liveness.liveIn(cjump->zero); + auto const& blockLiveOut = m_liveness.liveOut(_blockId); + + // check if we have to do anything (dup the condition, bring it to the top etc) + bool const conditionSlotAlreadyFinal = + !blockLiveOut.contains(cjump->condition) && // if our live out does not contain the condition (ie we dont have to dup it) + !stack.empty() && // our stack is not empty + stack.top().isValueID() && stack.top().valueID() == cjump->condition; // and the condition is already on top + if (!conditionSlotAlreadyFinal) + { + auto const condition = Slot::makeValueID(cjump->condition); + auto const targetSize = findOptimalTargetSize(stack.data(), {condition}, blockLiveOut, false); + StackShuffler::shuffle( + stack, {condition}, blockLiveOut, targetSize + ); + } + + yulAssert(!stack.empty() && stack.top().isValueID() && stack.top().valueID() == cjump->condition); + yulAssert(m_cfg.block(cjump->nonZero).phis.empty()); + + // define zero and non-zero stack in layouts + { + std::optional& nonZeroLayout = m_resultLayout[cjump->nonZero]; + yulAssert(!nonZeroLayout, "There should be no way to reach this block other than by going through the parent."); + nonZeroLayout = BlockLayout{}; + nonZeroLayout->stackIn = currentStackData; + // Remove condition; consumed by JUMPI + nonZeroLayout->stackIn.pop_back(); + + if ( + std::optional& zeroLayout = m_resultLayout[cjump->zero]; + !zeroLayout + ) + { + // same as nonzero stack in initially + zeroLayout = nonZeroLayout; + handlePhiFunctions(zeroLayout->stackIn, PhiInverse(m_cfg, _blockId, cjump->zero), zeroLiveIn); + StackType zeroStack(zeroLayout->stackIn, {}); + declareJunk(zeroStack, zeroLiveIn); + } + } + } + + blockLayout.stackOut = currentStackData; +} diff --git a/libyul/backends/evm/ssa/StackLayoutGenerator.h b/libyul/backends/evm/ssa/StackLayoutGenerator.h new file mode 100644 index 0000000000..c9d45c72a2 --- /dev/null +++ b/libyul/backends/evm/ssa/StackLayoutGenerator.h @@ -0,0 +1,51 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include + +#include + +namespace solidity::yul::ssa +{ + +class JunkAdmittingBlocksFinder; + +class StackLayoutGenerator +{ +public: + using Slot = StackSlot; + static SSACFGStackLayout generate(LivenessAnalysis const& _liveness, CallSites const& _callSites); + +private: + explicit StackLayoutGenerator(LivenessAnalysis const& _liveness, CallSites const& _callSites); + + void defineStackIn(SSACFG::BlockId const& _blockId); + void visitBlock(SSACFG::BlockId const& _blockId); + + SSACFG const& m_cfg; + LivenessAnalysis const& m_liveness; + CallSites const& m_callSites; + + std::unique_ptr m_junkAdmittingBlocksFinder; + SSACFGStackLayout m_resultLayout; +}; + +} From 22b1b120be84e072c63e380d0b442c18000b0efd Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Wed, 4 Feb 2026 22:47:46 +0100 Subject: [PATCH 194/243] Add SSA-CFG StackUtils --- libyul/CMakeLists.txt | 2 + libyul/backends/evm/ssa/StackUtils.cpp | 143 +++++++++++++++++++++++++ libyul/backends/evm/ssa/StackUtils.h | 43 ++++++++ 3 files changed, 188 insertions(+) create mode 100644 libyul/backends/evm/ssa/StackUtils.cpp create mode 100644 libyul/backends/evm/ssa/StackUtils.h diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index dcc0f923a0..d52dbba0db 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -95,6 +95,8 @@ add_library(yul backends/evm/ssa/StackLayoutGenerator.h backends/evm/ssa/StackShuffler.cpp backends/evm/ssa/StackShuffler.h + backends/evm/ssa/StackUtils.cpp + backends/evm/ssa/StackUtils.h backends/evm/ssa/io/DotExporterBase.cpp backends/evm/ssa/io/DotExporterBase.h backends/evm/ssa/io/JSONExporter.cpp diff --git a/libyul/backends/evm/ssa/StackUtils.cpp b/libyul/backends/evm/ssa/StackUtils.cpp new file mode 100644 index 0000000000..3f2d284238 --- /dev/null +++ b/libyul/backends/evm/ssa/StackUtils.cpp @@ -0,0 +1,143 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include + +#include + +#include + +using namespace solidity::yul::ssa; + +StackData solidity::yul::ssa::stackPreImage(StackData _stack, PhiInverse const& _phiInverse) +{ + if (!_phiInverse.noOp()) + for (auto& slot: _stack) + if (slot.isValueID()) + slot = StackSlot::makeValueID(_phiInverse(slot.valueID())); + return _stack; +} + +std::size_t solidity::yul::ssa::findOptimalTargetSize +( + StackData const& _stackData, + StackData const& _targetArgs, + LivenessAnalysis::LivenessData const& _targetLiveOut, + bool const _canIntroduceJunk +) +{ + std::size_t const minSize = _targetLiveOut.size() + _targetArgs.size(); + boost::container::flat_map deficit; + for (auto const& v: _targetLiveOut | ranges::views::keys) + deficit[StackSlot::makeValueID(v)]++; + for (auto const& arg: _targetArgs) + deficit[arg]++; + for (auto const& slot: _stackData) + if (auto it = deficit.find(slot); it != deficit.end()) + it->second = it->second > 0 ? it->second - 1 : 0; + + std::size_t const pivot = _canIntroduceJunk ? _stackData.size() + _targetArgs.size() : _stackData.size() + ranges::accumulate(deficit | ranges::views::values, 0u); + std::size_t const startSize = std::max(pivot, minSize); + static std::size_t constexpr maxUpwardExpansion = 32; + + StackData data; + data.reserve(startSize + maxUpwardExpansion); + auto const evaluateCost = [&](std::size_t const _targetSize) -> std::size_t + { + data = _stackData; + Stack countOpsStack(data, {}); + StackShuffler::shuffle(countOpsStack, _targetArgs, _targetLiveOut, _targetSize); + yulAssert(countOpsStack.size() == _targetSize); + return countOpsStack.callbacks().numOps; + }; + + std::size_t bestCost = evaluateCost(startSize); + auto result = startSize; + + // On non-reverting paths, only search downward from pivot to avoid growing the stack. + // On reverting paths, search in both directions since stack cleanup doesn't matter. + static std::size_t constexpr stopAfter = 3; + std::size_t consecutiveIncreases = 0; + // search downward + { + std::size_t size = startSize; + while (size > minSize) + { + --size; + auto const cost = evaluateCost(size); + if (cost < bestCost) + { + bestCost = cost; + result = size; + consecutiveIncreases = 0; + } + else if (++consecutiveIncreases >= stopAfter) + break; + } + } + // search upward (only on reverting paths) + if (_canIntroduceJunk) + { + consecutiveIncreases = 0; + for (std::size_t size = startSize + 1; size <= startSize + maxUpwardExpansion; ++size) + { + auto const cost = evaluateCost(size); + if (cost < bestCost) + { + bestCost = cost; + result = size; + consecutiveIncreases = 0; + } + else if (++consecutiveIncreases >= stopAfter) + break; + } + } + return result; +} + +CallSites solidity::yul::ssa::gatherCallSites(SSACFG const& _cfg) +{ + CallSites result; + std::vector visited(_cfg.numBlocks(), false); + visited[_cfg.entry.value] = true; + std::vector toVisit; + toVisit.reserve(_cfg.numBlocks()); + toVisit.push_back(_cfg.entry); + + while (!toVisit.empty()) + { + auto const blockId = toVisit.back(); + toVisit.pop_back(); + auto const& block = _cfg.block(blockId); + block.forEachExit([&toVisit, &visited](SSACFG::BlockId const& _exitBlockId){ + if (!visited[_exitBlockId.value]) + { + visited[_exitBlockId.value] = true; + toVisit.push_back(_exitBlockId); + } + }); + + for (auto const& operation: block.operations) + if (auto const* call = std::get_if(&operation.kind)) + if (call->canContinue) + result.addCallSite(&call->call.get()); + } + return result; +} diff --git a/libyul/backends/evm/ssa/StackUtils.h b/libyul/backends/evm/ssa/StackUtils.h new file mode 100644 index 0000000000..6f5e991a09 --- /dev/null +++ b/libyul/backends/evm/ssa/StackUtils.h @@ -0,0 +1,43 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include + +namespace solidity::yul::ssa +{ + +struct CountingInstructionsCallbacks +{ + std::size_t numOps = 0; + void swap(std::size_t) { ++numOps; } + void dup(std::size_t) { ++numOps; } + void push(StackSlot const&) { ++numOps; } + void pop() { ++numOps; } +}; + +/// Transform stack data by replacing all its phi variables with their respective preimages. +StackData stackPreImage(StackData _stack, PhiInverse const& _phiInverse); + +std::size_t findOptimalTargetSize(StackData const& _stackData, StackData const& _targetArgs, LivenessAnalysis::LivenessData const& _targetLiveOut, bool _canIntroduceJunk); + +CallSites gatherCallSites(SSACFG const& _cfg); + +} From 889679b01e23d439eddc5085101af92822753b7a Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Thu, 5 Feb 2026 14:46:54 +0100 Subject: [PATCH 195/243] Add SSA-CFG StackLayoutGenerator test --- test/CMakeLists.txt | 2 + test/InteractiveTests.h | 4 +- test/libyul/ssa/StackLayoutGeneratorTest.cpp | 216 +++++++++++++++++++ test/libyul/ssa/StackLayoutGeneratorTest.h | 37 ++++ test/libyul/ssa/StackShufflerTest.cpp | 4 +- test/libyul/ssa/StackShufflerTest.h | 2 +- test/tools/CMakeLists.txt | 2 + 7 files changed, 264 insertions(+), 3 deletions(-) create mode 100644 test/libyul/ssa/StackLayoutGeneratorTest.cpp create mode 100644 test/libyul/ssa/StackLayoutGeneratorTest.h diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f36ae41476..02a45088f9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -173,6 +173,8 @@ set(libyul_sources libyul/YulOptimizerTest.h libyul/YulOptimizerTestCommon.cpp libyul/YulOptimizerTestCommon.h + libyul/ssa/StackLayoutGeneratorTest.cpp + libyul/ssa/StackLayoutGeneratorTest.h libyul/ssa/StackShufflerTest.cpp libyul/ssa/StackShufflerTest.h ) diff --git a/test/InteractiveTests.h b/test/InteractiveTests.h index ff2725471c..7930dc32d0 100644 --- a/test/InteractiveTests.h +++ b/test/InteractiveTests.h @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -74,7 +75,8 @@ Testsuite const g_interactiveTestsuites[] = { {"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create}, {"Yul Control Flow Graph", "libyul", "yulControlFlowGraph", false, false, &yul::test::ControlFlowGraphTest::create}, {"Yul SSA Control Flow Graph", "libyul", "yulSSAControlFlowGraph", false, false, &yul::test::SSAControlFlowGraphTest::create}, - {"Yul SSA StackShuffling", "libyul", "ssa/stackShuffler", false, false, &yul::ssa::test::ShufflingTest::create}, + {"Yul SSA StackShuffling", "libyul", "ssa/stackShuffler", false, false, &yul::test::ssa::ShufflingTest::create}, + {"Yul SSA StackLayoutGenerator","libyul", "ssa/stackLayoutGenerator", false, false, &yul::test::ssa::StackLayoutGeneratorTest::create}, {"Yul Stack Layout", "libyul", "yulStackLayout", false, false, &yul::test::StackLayoutGeneratorTest::create}, {"Yul Stack Shuffling", "libyul", "yulStackShuffling", false, false, &yul::test::StackShufflingTest::create}, {"Control Flow Side Effects", "libyul", "controlFlowSideEffects", false, false, &yul::test::ControlFlowSideEffectsTest::create}, diff --git a/test/libyul/ssa/StackLayoutGeneratorTest.cpp b/test/libyul/ssa/StackLayoutGeneratorTest.cpp new file mode 100644 index 0000000000..a7ddad529c --- /dev/null +++ b/test/libyul/ssa/StackLayoutGeneratorTest.cpp @@ -0,0 +1,216 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#ifdef ISOLTEST +#include +#if (BOOST_VERSION < 108800) +#include +#else +#define BOOST_PROCESS_VERSION 1 +#include +#include +#include +#endif +#endif + +using namespace solidity; +using namespace solidity::util; +using namespace solidity::yul::ssa; +using namespace solidity::yul::test::ssa; + +namespace +{ + +class StackLayoutDotExporter: public io::DotExporterBase +{ +public: + StackLayoutDotExporter(SSACFG const& _cfg, std::size_t _functionIndex, SSACFGStackLayout const& _layout): + DotExporterBase(_cfg, _functionIndex), + m_layout(_layout) + { + } + +protected: + void writeBlockLabel(std::ostream& _out, SSACFG::BlockId _blockId) override + { + auto const& block = m_cfg.block(_blockId); + auto const& blockLayout = m_layout[_blockId]; + yulAssert(blockLayout.has_value()); + + _out << "\\\n"; + _out << "IN: " << stackToString(blockLayout->stackIn) << "\\l\\\n"; + + for (std::size_t i = 0; i < block.operations.size(); ++i) + { + auto const& operation = block.operations[i]; + yulAssert(i < blockLayout->operationIn.size()); + auto operationStack = blockLayout->operationIn[i]; + + _out << "\\l\\\n"; + _out << stackToString(operationStack) << "\\l\\\n"; + + std::visit(GenericVisitor{ + [&](SSACFG::Call const& _call) { + _out << escapeLabel(_call.function.get().name.str()); + }, + [&](SSACFG::BuiltinCall const& _call) { + _out << escapeLabel(_call.builtin.get().name); + }, + [&](SSACFG::LiteralAssignment const&) { + yulAssert(operation.inputs.size() == 1); + _out << escapeLabel(operation.inputs.back().str(m_cfg)); + } + }, operation.kind); + _out << "\\l\\\n"; + + yulAssert(operation.inputs.size() <= operationStack.size()); + for (std::size_t j = 0; j < operation.inputs.size(); ++j) + operationStack.pop_back(); + for (auto const& output: operation.outputs) + operationStack.push_back(StackSlot::makeValueID(output)); + _out << stackToString(operationStack) << "\\l\\\n"; + } + + _out << "\\l\\\n"; + _out << "OUT: " << stackToString(blockLayout->stackOut) << "\\l\\\n"; + } + +private: + SSACFGStackLayout const& m_layout; +}; + +} + +std::unique_ptr StackLayoutGeneratorTest::create(Config const& _config) +{ + return std::make_unique(_config.filename); +} + +StackLayoutGeneratorTest::StackLayoutGeneratorTest(std::string const& _filename): TestCase(_filename) +{ + m_source = m_reader.source(); + auto dialectName = m_reader.stringSetting("dialect", "evm"); + soltestAssert(dialectName == "evm"); + m_expectation = m_reader.simpleExpectations(); +} + +frontend::test::TestCase::TestResult StackLayoutGeneratorTest::run(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted) +{ + static std::string_view constexpr SUBOBJECT_GRAPH_SEPARATOR = "\n>>>>> GRAPH SEPARATOR\n"; + YulStack const yulStack = yul::test::parseYul(m_source); + if (yulStack.hasErrors()) + { + yul::test::printYulErrors(yulStack, _stream, _linePrefix, _formatted); + return TestResult::FatalError; + } + + std::set visited; + visited.insert(yulStack.parserResult().get()); + + std::vector toVisit{yulStack.parserResult().get()}; + while (!toVisit.empty()) + { + auto const& object = *toVisit.back(); + toVisit.pop_back(); + + std::unique_ptr const controlFlow = SSACFGBuilder::build( + *object.analysisInfo, + *object.dialect(), + object.code()->root(), + false + ); + // insert separator + if (!m_obtainedResult.empty()) + m_obtainedResult += SUBOBJECT_GRAPH_SEPARATOR; + + m_obtainedResult += "digraph SSACFG {\nnodesep=0.7;\ngraph[fontname=\"DejaVu Sans\", rankdir=LR]\nnode[shape=box,fontname=\"DejaVu Sans\"];\n\n"; + + for (std::size_t index = 0; index < controlFlow->functionGraphs.size(); ++index) + { + auto const& cfg = *controlFlow->functionGraphs[index]; + SSACFGStackLayout const layout = StackLayoutGenerator::generate( + LivenessAnalysis(cfg), + gatherCallSites(cfg) + ); + StackLayoutDotExporter exporter(cfg, index, layout); + if (cfg.function) + m_obtainedResult += exporter.exportFunction(*cfg.function, false); + else + m_obtainedResult += exporter.exportBlocks(cfg.entry, false); + } + + m_obtainedResult += "}\n"; + + for (auto const& subNode: object.subObjects) + if (auto subObject = std::dynamic_pointer_cast(subNode)) + if (!visited.contains(subObject.get())) + { + visited.insert(subObject.get()); + toVisit.push_back(subObject.get()); + } + } + + auto const result = checkResult(_stream, _linePrefix, _formatted); + +#ifdef ISOLTEST + char* graphDisplayer = nullptr; + if (result == TestResult::Failure) + graphDisplayer = getenv("ISOLTEST_DISPLAY_GRAPHS_FAILURE"); + else if (result == TestResult::Success) + graphDisplayer = getenv("ISOLTEST_DISPLAY_GRAPHS_SUCCESS"); + + if (graphDisplayer) + { + if (result == TestResult::Success) + std::cout << std::endl << m_source << std::endl; + for (auto const dotRange: ranges::views::split(m_obtainedResult, SUBOBJECT_GRAPH_SEPARATOR)) + { + std::string_view dot(&*dotRange.begin(), static_cast(ranges::distance(dotRange))); + boost::process::opstream pipe; + boost::process::child child(graphDisplayer, boost::process::std_in < pipe); + + pipe << dot; + pipe.flush(); + pipe.pipe().close(); + if (result == TestResult::Success) + child.wait(); + else + child.detach(); + } + } +#endif + + return result; +} diff --git a/test/libyul/ssa/StackLayoutGeneratorTest.h b/test/libyul/ssa/StackLayoutGeneratorTest.h new file mode 100644 index 0000000000..7b66363f3e --- /dev/null +++ b/test/libyul/ssa/StackLayoutGeneratorTest.h @@ -0,0 +1,37 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + + +#include + +#include + +namespace solidity::yul::test::ssa +{ + +class StackLayoutGeneratorTest: public frontend::test::TestCase +{ +public: + static std::unique_ptr create(Config const& _config); + explicit StackLayoutGeneratorTest(std::string const& _filename); + TestResult run(std::ostream& _stream, std::string const& _linePrefix, bool _formatted) override; +}; + +} diff --git a/test/libyul/ssa/StackShufflerTest.cpp b/test/libyul/ssa/StackShufflerTest.cpp index 3c8c964e7e..cd24b3b5ec 100644 --- a/test/libyul/ssa/StackShufflerTest.cpp +++ b/test/libyul/ssa/StackShufflerTest.cpp @@ -39,8 +39,10 @@ #include using namespace solidity; +using namespace solidity::yul; using namespace solidity::yul::ssa; -using namespace solidity::yul::ssa::test; +using namespace solidity::yul::test; +using namespace solidity::yul::test::ssa; namespace { diff --git a/test/libyul/ssa/StackShufflerTest.h b/test/libyul/ssa/StackShufflerTest.h index 14b0d98f68..37f9b61e58 100644 --- a/test/libyul/ssa/StackShufflerTest.h +++ b/test/libyul/ssa/StackShufflerTest.h @@ -22,7 +22,7 @@ #include -namespace solidity::yul::ssa::test +namespace solidity::yul::test::ssa { class ShufflingTest: public frontend::test::TestCase diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 7b7331c77f..0a1c338735 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -53,6 +53,8 @@ add_executable(isoltest ../libyul/YulOptimizerTest.cpp ../libyul/YulOptimizerTestCommon.cpp ../libyul/YulInterpreterTest.cpp + ../libyul/ssa/StackLayoutGeneratorTest.cpp + ../libyul/ssa/StackLayoutGeneratorTest.h ../libyul/ssa/StackShufflerTest.cpp ../libyul/ssa/StackShufflerTest.h ) From b0d0ac1a212096ada177c3b875d22cdfdd511017 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 9 Feb 2026 14:33:04 +0100 Subject: [PATCH 196/243] Add SSA CFG stack layout generation tests --- .../ssa/stackLayoutGenerator/dead_code.yul | 79 ++++++++++++ .../ssa/stackLayoutGenerator/function.yul | 106 ++++++++++++++++ .../ssa/stackLayoutGenerator/multi_return.yul | 59 +++++++++ .../ssa/stackLayoutGenerator/nested_for.yul | 119 ++++++++++++++++++ .../ssa/stackLayoutGenerator/recursion.yul | 71 +++++++++++ .../ssa/stackLayoutGenerator/simple_for.yul | 100 +++++++++++++++ .../ssa/stackLayoutGenerator/simple_if.yul | 76 +++++++++++ .../ssa/stackLayoutGenerator/switch.yul | 95 ++++++++++++++ 8 files changed, 705 insertions(+) create mode 100644 test/libyul/ssa/stackLayoutGenerator/dead_code.yul create mode 100644 test/libyul/ssa/stackLayoutGenerator/function.yul create mode 100644 test/libyul/ssa/stackLayoutGenerator/multi_return.yul create mode 100644 test/libyul/ssa/stackLayoutGenerator/nested_for.yul create mode 100644 test/libyul/ssa/stackLayoutGenerator/recursion.yul create mode 100644 test/libyul/ssa/stackLayoutGenerator/simple_for.yul create mode 100644 test/libyul/ssa/stackLayoutGenerator/simple_if.yul create mode 100644 test/libyul/ssa/stackLayoutGenerator/switch.yul diff --git a/test/libyul/ssa/stackLayoutGenerator/dead_code.yul b/test/libyul/ssa/stackLayoutGenerator/dead_code.yul new file mode 100644 index 0000000000..adba2a345a --- /dev/null +++ b/test/libyul/ssa/stackLayoutGenerator/dead_code.yul @@ -0,0 +1,79 @@ +{ + let x := calldataload(0) + if x { + revert(0, 0) + } + let y := calldataload(32) + if y { + revert(0, 0) + } + sstore(x, y) +} +// ---- +// digraph SSACFG { +// nodesep=0.7; +// graph[fontname="DejaVu Sans", rankdir=LR] +// node[shape=box,fontname="DejaVu Sans"]; +// +// Entry [label="Entry"]; +// Entry -> Block0_0; +// Block0_0 [label="\ +// IN: []\l\ +// \l\ +// [lit0]\l\ +// calldataload\l\ +// [v0]\l\ +// \l\ +// OUT: [v0, v0]\l\ +// "]; +// Block0_0 -> Block0_0Exit; +// Block0_0Exit [label="{ If v0 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0_0Exit:0 -> Block0_2 [style="solid"]; +// Block0_0Exit:1 -> Block0_1 [style="solid"]; +// Block0_1 [label="\ +// IN: [v0]\l\ +// \l\ +// [JUNK, lit0, lit0]\l\ +// revert\l\ +// [JUNK]\l\ +// \l\ +// OUT: [JUNK]\l\ +// "]; +// Block0_1Exit [label="Terminated"]; +// Block0_1 -> Block0_1Exit; +// Block0_2 [label="\ +// IN: [v0]\l\ +// \l\ +// [v0, lit1]\l\ +// calldataload\l\ +// [v0, v1]\l\ +// \l\ +// OUT: [v0, v1, v1]\l\ +// "]; +// Block0_2 -> Block0_2Exit; +// Block0_2Exit [label="{ If v1 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0_2Exit:0 -> Block0_5 [style="solid"]; +// Block0_2Exit:1 -> Block0_4 [style="solid"]; +// Block0_4 [label="\ +// IN: [v0, v1]\l\ +// \l\ +// [JUNK, JUNK, lit0, lit0]\l\ +// revert\l\ +// [JUNK, JUNK]\l\ +// \l\ +// OUT: [JUNK, JUNK]\l\ +// "]; +// Block0_4Exit [label="Terminated"]; +// Block0_4 -> Block0_4Exit; +// Block0_5 [label="\ +// IN: [v0, v1]\l\ +// \l\ +// [v0, v1, v0]\l\ +// sstore\l\ +// [v0]\l\ +// \l\ +// OUT: [v0]\l\ +// "]; +// Block0_5Exit [label="MainExit"]; +// Block0_5 -> Block0_5Exit; +// } diff --git a/test/libyul/ssa/stackLayoutGenerator/function.yul b/test/libyul/ssa/stackLayoutGenerator/function.yul new file mode 100644 index 0000000000..a134c36f8b --- /dev/null +++ b/test/libyul/ssa/stackLayoutGenerator/function.yul @@ -0,0 +1,106 @@ +{ + function f(a, b) -> r { + let x := add(a,b) + r := sub(x,a) + } + function g() { + sstore(0x01, 0x0101) + } + function h(x) { + h(f(x, 0)) + g() + } + function i() -> v, w { + v := 0x0202 + w := 0x0303 + } + let x, y := i() + h(x) + h(y) + // This call of g() is unreachable too as the one in h() but we wanna cover both cases. + g() +} +// ---- +// digraph SSACFG { +// nodesep=0.7; +// graph[fontname="DejaVu Sans", rankdir=LR] +// node[shape=box,fontname="DejaVu Sans"]; +// +// Entry [label="Entry"]; +// Entry -> Block0_0; +// Block0_0 [label="\ +// IN: []\l\ +// \l\ +// [FunctionCallReturnLabel[0]]\l\ +// i\l\ +// [FunctionCallReturnLabel[0], v0, v1]\l\ +// \l\ +// [v0, JUNK, v0]\l\ +// h\l\ +// [v0, JUNK]\l\ +// \l\ +// OUT: [v0, JUNK]\l\ +// "]; +// Block0_0Exit [label="Terminated"]; +// Block0_0 -> Block0_0Exit; +// FunctionEntry_f_0 [label="function f: +// r := f(v0, v1)"]; +// FunctionEntry_f_0 -> Block1_0; +// Block1_0 [label="\ +// IN: [v1, v0]\l\ +// \l\ +// [v0, v1, v0]\l\ +// add\l\ +// [v0, v2]\l\ +// \l\ +// [v0, v2]\l\ +// sub\l\ +// [v3]\l\ +// \l\ +// OUT: [v3]\l\ +// "]; +// Block1_0Exit [label="FunctionReturn[v3]"]; +// Block1_0 -> Block1_0Exit; +// FunctionEntry_g_0 [label="function g: +// g()"]; +// FunctionEntry_g_0 -> Block2_0; +// Block2_0 [label="\ +// IN: []\l\ +// \l\ +// [lit0, lit1]\l\ +// sstore\l\ +// []\l\ +// \l\ +// OUT: []\l\ +// "]; +// Block2_0Exit [label="FunctionReturn[]"]; +// Block2_0 -> Block2_0Exit; +// FunctionEntry_h_0 [label="function h: +// h(v0)"]; +// FunctionEntry_h_0 -> Block3_0; +// Block3_0 [label="\ +// IN: [v0]\l\ +// \l\ +// [v0, FunctionCallReturnLabel[0], lit0, v0]\l\ +// f\l\ +// [v0, FunctionCallReturnLabel[0], v1]\l\ +// \l\ +// [JUNK, v1]\l\ +// h\l\ +// [JUNK]\l\ +// \l\ +// OUT: [JUNK]\l\ +// "]; +// Block3_0Exit [label="Terminated"]; +// Block3_0 -> Block3_0Exit; +// FunctionEntry_i_0 [label="function i: +// v, w := i()"]; +// FunctionEntry_i_0 -> Block4_0; +// Block4_0 [label="\ +// IN: []\l\ +// \l\ +// OUT: []\l\ +// "]; +// Block4_0Exit [label="FunctionReturn[0x0202, 0x0303]"]; +// Block4_0 -> Block4_0Exit; +// } diff --git a/test/libyul/ssa/stackLayoutGenerator/multi_return.yul b/test/libyul/ssa/stackLayoutGenerator/multi_return.yul new file mode 100644 index 0000000000..c1f2b94d4b --- /dev/null +++ b/test/libyul/ssa/stackLayoutGenerator/multi_return.yul @@ -0,0 +1,59 @@ +{ + function pair(a) -> x, y { + x := add(a, 1) + y := add(a, 2) + } + let p, q := pair(42) + let r, s := pair(p) + sstore(q, add(r, s)) +} +// ---- +// digraph SSACFG { +// nodesep=0.7; +// graph[fontname="DejaVu Sans", rankdir=LR] +// node[shape=box,fontname="DejaVu Sans"]; +// +// Entry [label="Entry"]; +// Entry -> Block0_0; +// Block0_0 [label="\ +// IN: []\l\ +// \l\ +// [FunctionCallReturnLabel[0], lit0]\l\ +// pair\l\ +// [FunctionCallReturnLabel[0], v0, v1]\l\ +// \l\ +// [v0, v1, FunctionCallReturnLabel[1], v0]\l\ +// pair\l\ +// [v0, v1, FunctionCallReturnLabel[1], v2, v3]\l\ +// \l\ +// [JUNK, v1, v2, v3, v2]\l\ +// add\l\ +// [JUNK, v1, v2, v4]\l\ +// \l\ +// [JUNK, v1, JUNK, v4, v1]\l\ +// sstore\l\ +// [JUNK, v1, JUNK]\l\ +// \l\ +// OUT: [JUNK, v1, JUNK]\l\ +// "]; +// Block0_0Exit [label="MainExit"]; +// Block0_0 -> Block0_0Exit; +// FunctionEntry_pair_0 [label="function pair: +// x, y := pair(v0)"]; +// FunctionEntry_pair_0 -> Block1_0; +// Block1_0 [label="\ +// IN: [v0]\l\ +// \l\ +// [v0, lit1, v0]\l\ +// add\l\ +// [v0, v1]\l\ +// \l\ +// [v1, lit2, v0]\l\ +// add\l\ +// [v1, v2]\l\ +// \l\ +// OUT: [v1, v2]\l\ +// "]; +// Block1_0Exit [label="FunctionReturn[v1, v2]"]; +// Block1_0 -> Block1_0Exit; +// } diff --git a/test/libyul/ssa/stackLayoutGenerator/nested_for.yul b/test/libyul/ssa/stackLayoutGenerator/nested_for.yul new file mode 100644 index 0000000000..f595c19b25 --- /dev/null +++ b/test/libyul/ssa/stackLayoutGenerator/nested_for.yul @@ -0,0 +1,119 @@ +{ + let s + for {let i := 0} lt(i, 10) {i := add(i, 1)} { + for {let j := 0} lt(j, 10) {j := add(j, 1)} { + s := add(s, mul(i, j)) + } + } + mstore(0, s) +} +// ---- +// digraph SSACFG { +// nodesep=0.7; +// graph[fontname="DejaVu Sans", rankdir=LR] +// node[shape=box,fontname="DejaVu Sans"]; +// +// Entry [label="Entry"]; +// Entry -> Block0_0; +// Block0_0 [label="\ +// IN: []\l\ +// \l\ +// OUT: []\l\ +// "]; +// Block0_0 -> Block0_0Exit [arrowhead=none]; +// Block0_0Exit [label="Jump" shape=oval]; +// Block0_0Exit -> Block0_1 [style="solid"]; +// Block0_1 [label="\ +// IN: [phi0, phi4]\l\ +// \l\ +// [phi0, phi4, lit1, phi0]\l\ +// lt\l\ +// [phi0, phi4, v0]\l\ +// \l\ +// OUT: [phi0, phi4, v0]\l\ +// "]; +// Block0_1 -> Block0_1Exit; +// Block0_1Exit [label="{ If v0 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0_1Exit:0 -> Block0_4 [style="solid"]; +// Block0_1Exit:1 -> Block0_2 [style="solid"]; +// Block0_2 [label="\ +// IN: [phi0, phi4]\l\ +// \l\ +// OUT: [phi0, phi4]\l\ +// "]; +// Block0_2 -> Block0_2Exit [arrowhead=none]; +// Block0_2Exit [label="Jump" shape=oval]; +// Block0_2Exit -> Block0_5 [style="solid"]; +// Block0_4 [label="\ +// IN: [JUNK, phi4]\l\ +// \l\ +// [JUNK, phi4, lit0]\l\ +// mstore\l\ +// [JUNK]\l\ +// \l\ +// OUT: [JUNK]\l\ +// "]; +// Block0_4Exit [label="MainExit"]; +// Block0_4 -> Block0_4Exit; +// Block0_5 [label="\ +// IN: [phi0, JUNK, phi1, phi3]\l\ +// \l\ +// [phi0, JUNK, phi1, phi3, lit1, phi1]\l\ +// lt\l\ +// [phi0, JUNK, phi1, phi3, v1]\l\ +// \l\ +// OUT: [phi0, JUNK, phi1, phi3, v1]\l\ +// "]; +// Block0_5 -> Block0_5Exit; +// Block0_5Exit [label="{ If v1 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0_5Exit:0 -> Block0_8 [style="solid"]; +// Block0_5Exit:1 -> Block0_6 [style="solid"]; +// Block0_6 [label="\ +// IN: [phi0, JUNK, phi1, phi3]\l\ +// \l\ +// [phi0, JUNK, phi1, phi3, phi1, phi0]\l\ +// mul\l\ +// [phi0, JUNK, phi1, phi3, v2]\l\ +// \l\ +// [phi0, JUNK, phi1, v2, phi3]\l\ +// add\l\ +// [phi0, JUNK, phi1, v3]\l\ +// \l\ +// OUT: [phi0, JUNK, phi1, v3]\l\ +// "]; +// Block0_6 -> Block0_6Exit [arrowhead=none]; +// Block0_6Exit [label="Jump" shape=oval]; +// Block0_6Exit -> Block0_7 [style="solid"]; +// Block0_8 [label="\ +// IN: [phi0, JUNK, JUNK, phi3]\l\ +// \l\ +// OUT: [phi0, JUNK, JUNK, phi3]\l\ +// "]; +// Block0_8 -> Block0_8Exit [arrowhead=none]; +// Block0_8Exit [label="Jump" shape=oval]; +// Block0_8Exit -> Block0_3 [style="solid"]; +// Block0_7 [label="\ +// IN: [phi0, JUNK, phi1, v3]\l\ +// \l\ +// [phi0, JUNK, v3, lit2, phi1]\l\ +// add\l\ +// [phi0, JUNK, v3, v4]\l\ +// \l\ +// OUT: [phi0, JUNK, v3, v4]\l\ +// "]; +// Block0_7 -> Block0_7Exit [arrowhead=none]; +// Block0_7Exit [label="Jump" shape=oval]; +// Block0_7Exit -> Block0_5 [style="solid"]; +// Block0_3 [label="\ +// IN: [phi0, JUNK, JUNK, phi3]\l\ +// \l\ +// [phi3, JUNK, JUNK, lit2, phi0]\l\ +// add\l\ +// [phi3, JUNK, JUNK, v5]\l\ +// \l\ +// OUT: [phi3, JUNK, JUNK, v5]\l\ +// "]; +// Block0_3 -> Block0_3Exit [arrowhead=none]; +// Block0_3Exit [label="Jump" shape=oval]; +// Block0_3Exit -> Block0_1 [style="solid"]; +// } diff --git a/test/libyul/ssa/stackLayoutGenerator/recursion.yul b/test/libyul/ssa/stackLayoutGenerator/recursion.yul new file mode 100644 index 0000000000..4a33f172d0 --- /dev/null +++ b/test/libyul/ssa/stackLayoutGenerator/recursion.yul @@ -0,0 +1,71 @@ +{ + function sum(n) -> s { + if n { + s := add(n, sum(sub(n, 1))) + } + } + mstore(0, sum(10)) +} +// ---- +// digraph SSACFG { +// nodesep=0.7; +// graph[fontname="DejaVu Sans", rankdir=LR] +// node[shape=box,fontname="DejaVu Sans"]; +// +// Entry [label="Entry"]; +// Entry -> Block0_0; +// Block0_0 [label="\ +// IN: []\l\ +// \l\ +// [FunctionCallReturnLabel[0], lit0]\l\ +// sum\l\ +// [FunctionCallReturnLabel[0], v0]\l\ +// \l\ +// [v0, lit1]\l\ +// mstore\l\ +// []\l\ +// \l\ +// OUT: []\l\ +// "]; +// Block0_0Exit [label="MainExit"]; +// Block0_0 -> Block0_0Exit; +// FunctionEntry_sum_0 [label="function sum: +// s := sum(v0)"]; +// FunctionEntry_sum_0 -> Block1_0; +// Block1_0 [label="\ +// IN: [v0]\l\ +// \l\ +// OUT: [v0, v0]\l\ +// "]; +// Block1_0 -> Block1_0Exit; +// Block1_0Exit [label="{ If v0 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1_0Exit:0 -> Block1_2 [style="solid"]; +// Block1_0Exit:1 -> Block1_1 [style="solid"]; +// Block1_1 [label="\ +// IN: [v0]\l\ +// \l\ +// [v0, lit1, v0]\l\ +// sub\l\ +// [v0, v1]\l\ +// \l\ +// [v0, FunctionCallReturnLabel[0], v1]\l\ +// sum\l\ +// [v0, FunctionCallReturnLabel[0], v2]\l\ +// \l\ +// [v2, v0]\l\ +// add\l\ +// [v3]\l\ +// \l\ +// OUT: [v3]\l\ +// "]; +// Block1_1 -> Block1_1Exit [arrowhead=none]; +// Block1_1Exit [label="Jump" shape=oval]; +// Block1_1Exit -> Block1_2 [style="solid"]; +// Block1_2 [label="\ +// IN: [JUNK, phi0]\l\ +// \l\ +// OUT: [JUNK, phi0]\l\ +// "]; +// Block1_2Exit [label="FunctionReturn[phi0]"]; +// Block1_2 -> Block1_2Exit; +// } diff --git a/test/libyul/ssa/stackLayoutGenerator/simple_for.yul b/test/libyul/ssa/stackLayoutGenerator/simple_for.yul new file mode 100644 index 0000000000..f245c5f170 --- /dev/null +++ b/test/libyul/ssa/stackLayoutGenerator/simple_for.yul @@ -0,0 +1,100 @@ +{ + let x + for {let i := 0} x {i := add(i, 1)} { + if mload(i) { + x := add(x, i) + x := add(x, mload(32)) + } + } + mstore(x, 33) +} +// ---- +// digraph SSACFG { +// nodesep=0.7; +// graph[fontname="DejaVu Sans", rankdir=LR] +// node[shape=box,fontname="DejaVu Sans"]; +// +// Entry [label="Entry"]; +// Entry -> Block0_0; +// Block0_0 [label="\ +// IN: []\l\ +// \l\ +// OUT: []\l\ +// "]; +// Block0_0 -> Block0_0Exit [arrowhead=none]; +// Block0_0Exit [label="Jump" shape=oval]; +// Block0_0Exit -> Block0_1 [style="solid"]; +// Block0_1 [label="\ +// IN: [phi0, phi1]\l\ +// \l\ +// OUT: [phi0, phi1, phi0]\l\ +// "]; +// Block0_1 -> Block0_1Exit; +// Block0_1Exit [label="{ If phi0 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0_1Exit:0 -> Block0_4 [style="solid"]; +// Block0_1Exit:1 -> Block0_2 [style="solid"]; +// Block0_2 [label="\ +// IN: [phi0, phi1]\l\ +// \l\ +// [phi0, phi1, phi1]\l\ +// mload\l\ +// [phi0, phi1, v0]\l\ +// \l\ +// OUT: [phi0, phi1, v0]\l\ +// "]; +// Block0_2 -> Block0_2Exit; +// Block0_2Exit [label="{ If v0 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0_2Exit:0 -> Block0_6 [style="solid"]; +// Block0_2Exit:1 -> Block0_5 [style="solid"]; +// Block0_4 [label="\ +// IN: [phi0, JUNK]\l\ +// \l\ +// [phi0, JUNK, lit3, phi0]\l\ +// mstore\l\ +// [phi0, JUNK]\l\ +// \l\ +// OUT: [phi0, JUNK]\l\ +// "]; +// Block0_4Exit [label="MainExit"]; +// Block0_4 -> Block0_4Exit; +// Block0_5 [label="\ +// IN: [phi0, phi1]\l\ +// \l\ +// [phi1, phi1, phi0]\l\ +// add\l\ +// [phi1, v1]\l\ +// \l\ +// [phi1, v1, lit1]\l\ +// mload\l\ +// [phi1, v1, v2]\l\ +// \l\ +// [phi1, v2, v1]\l\ +// add\l\ +// [phi1, v3]\l\ +// \l\ +// OUT: [phi1, v3]\l\ +// "]; +// Block0_5 -> Block0_5Exit [arrowhead=none]; +// Block0_5Exit [label="Jump" shape=oval]; +// Block0_5Exit -> Block0_6 [style="solid"]; +// Block0_6 [label="\ +// IN: [phi3, phi1]\l\ +// \l\ +// OUT: [phi3, phi1]\l\ +// "]; +// Block0_6 -> Block0_6Exit [arrowhead=none]; +// Block0_6Exit [label="Jump" shape=oval]; +// Block0_6Exit -> Block0_3 [style="solid"]; +// Block0_3 [label="\ +// IN: [phi3, phi1]\l\ +// \l\ +// [phi3, lit2, phi1]\l\ +// add\l\ +// [phi3, v4]\l\ +// \l\ +// OUT: [phi3, v4]\l\ +// "]; +// Block0_3 -> Block0_3Exit [arrowhead=none]; +// Block0_3Exit [label="Jump" shape=oval]; +// Block0_3Exit -> Block0_1 [style="solid"]; +// } diff --git a/test/libyul/ssa/stackLayoutGenerator/simple_if.yul b/test/libyul/ssa/stackLayoutGenerator/simple_if.yul new file mode 100644 index 0000000000..f9e0039e33 --- /dev/null +++ b/test/libyul/ssa/stackLayoutGenerator/simple_if.yul @@ -0,0 +1,76 @@ +{ + function f(a, b) -> c { + if mload(a) { + a := add(a, 2) + revert(a, a) + } + c := add(a, b) + } + mstore(42, f(0, 1)) +} +// ---- +// digraph SSACFG { +// nodesep=0.7; +// graph[fontname="DejaVu Sans", rankdir=LR] +// node[shape=box,fontname="DejaVu Sans"]; +// +// Entry [label="Entry"]; +// Entry -> Block0_0; +// Block0_0 [label="\ +// IN: []\l\ +// \l\ +// [FunctionCallReturnLabel[0], lit0, lit1]\l\ +// f\l\ +// [FunctionCallReturnLabel[0], v0]\l\ +// \l\ +// [v0, lit2]\l\ +// mstore\l\ +// []\l\ +// \l\ +// OUT: []\l\ +// "]; +// Block0_0Exit [label="MainExit"]; +// Block0_0 -> Block0_0Exit; +// FunctionEntry_f_0 [label="function f: +// c := f(v0, v1)"]; +// FunctionEntry_f_0 -> Block1_0; +// Block1_0 [label="\ +// IN: [v1, v0]\l\ +// \l\ +// [v1, v0, v0]\l\ +// mload\l\ +// [v1, v0, v2]\l\ +// \l\ +// OUT: [v1, v0, v2]\l\ +// "]; +// Block1_0 -> Block1_0Exit; +// Block1_0Exit [label="{ If v2 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1_0Exit:0 -> Block1_2 [style="solid"]; +// Block1_0Exit:1 -> Block1_1 [style="solid"]; +// Block1_1 [label="\ +// IN: [v1, v0]\l\ +// \l\ +// [JUNK, v0, lit1, v0]\l\ +// add\l\ +// [JUNK, v0, v3]\l\ +// \l\ +// [JUNK, JUNK, v3, v3]\l\ +// revert\l\ +// [JUNK, JUNK]\l\ +// \l\ +// OUT: [JUNK, JUNK]\l\ +// "]; +// Block1_1Exit [label="Terminated"]; +// Block1_1 -> Block1_1Exit; +// Block1_2 [label="\ +// IN: [v1, v0]\l\ +// \l\ +// [v1, v0]\l\ +// add\l\ +// [v4]\l\ +// \l\ +// OUT: [v4]\l\ +// "]; +// Block1_2Exit [label="FunctionReturn[v4]"]; +// Block1_2 -> Block1_2Exit; +// } diff --git a/test/libyul/ssa/stackLayoutGenerator/switch.yul b/test/libyul/ssa/stackLayoutGenerator/switch.yul new file mode 100644 index 0000000000..898944d374 --- /dev/null +++ b/test/libyul/ssa/stackLayoutGenerator/switch.yul @@ -0,0 +1,95 @@ +{ + let x := 0x0101 + let y := 0x0202 + let z := 0x0303 + switch sload(x) + case 0 { + x := 0x42 + } + case 1 { + y := 0x42 + } + default { + sstore(z, z) + } + + sstore(0x0404, y) +} +// ---- +// digraph SSACFG { +// nodesep=0.7; +// graph[fontname="DejaVu Sans", rankdir=LR] +// node[shape=box,fontname="DejaVu Sans"]; +// +// Entry [label="Entry"]; +// Entry -> Block0_0; +// Block0_0 [label="\ +// IN: []\l\ +// \l\ +// [lit0]\l\ +// sload\l\ +// [v0]\l\ +// \l\ +// [v0, lit3, v0]\l\ +// eq\l\ +// [v0, v1]\l\ +// \l\ +// OUT: [v0, v1]\l\ +// "]; +// Block0_0 -> Block0_0Exit; +// Block0_0Exit [label="{ If v1 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0_0Exit:0 -> Block0_3 [style="solid"]; +// Block0_0Exit:1 -> Block0_2 [style="solid"]; +// Block0_2 [label="\ +// IN: [v0]\l\ +// \l\ +// OUT: [v0]\l\ +// "]; +// Block0_2 -> Block0_2Exit [arrowhead=none]; +// Block0_2Exit [label="Jump" shape=oval]; +// Block0_2Exit -> Block0_1 [style="solid"]; +// Block0_3 [label="\ +// IN: [v0]\l\ +// \l\ +// [lit5, v0]\l\ +// eq\l\ +// [v2]\l\ +// \l\ +// OUT: [v2]\l\ +// "]; +// Block0_3 -> Block0_3Exit; +// Block0_3Exit [label="{ If v2 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block0_3Exit:0 -> Block0_5 [style="solid"]; +// Block0_3Exit:1 -> Block0_4 [style="solid"]; +// Block0_1 [label="\ +// IN: [JUNK, phi0]\l\ +// \l\ +// [JUNK, phi0, lit6]\l\ +// sstore\l\ +// [JUNK]\l\ +// \l\ +// OUT: [JUNK]\l\ +// "]; +// Block0_1Exit [label="MainExit"]; +// Block0_1 -> Block0_1Exit; +// Block0_4 [label="\ +// IN: []\l\ +// \l\ +// OUT: []\l\ +// "]; +// Block0_4 -> Block0_4Exit [arrowhead=none]; +// Block0_4Exit [label="Jump" shape=oval]; +// Block0_4Exit -> Block0_1 [style="solid"]; +// Block0_5 [label="\ +// IN: []\l\ +// \l\ +// [lit2, lit2]\l\ +// sstore\l\ +// []\l\ +// \l\ +// OUT: []\l\ +// "]; +// Block0_5 -> Block0_5Exit [arrowhead=none]; +// Block0_5Exit [label="Jump" shape=oval]; +// Block0_5Exit -> Block0_1 [style="solid"]; +// } From 4b0ced45d4244abc392cfcb3caa6b3f39030dc6e Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Tue, 17 Feb 2026 16:53:50 +0100 Subject: [PATCH 197/243] fix broken doc links --- docs/060-breaking-changes.rst | 2 +- docs/installing-solidity.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/060-breaking-changes.rst b/docs/060-breaking-changes.rst index c2a6ceb1ed..030c59988b 100644 --- a/docs/060-breaking-changes.rst +++ b/docs/060-breaking-changes.rst @@ -119,7 +119,7 @@ Metadata Hash Options The compiler now appends the `IPFS `_ hash of the metadata file to the end of the bytecode by default (for details, see documentation on :doc:`contract metadata `). Before 0.6.0, the compiler appended the -`Swarm `_ hash by default, and in order to still support this behavior, +`Swarm `_ hash by default, and in order to still support this behavior, the new command-line option ``--metadata-hash`` was introduced. It allows you to select the hash to be produced and appended, by passing either ``ipfs`` or ``swarm`` as value to the ``--metadata-hash`` command-line option. Passing the value ``none`` completely removes the hash. diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 7e372ea4e3..a3a50a56e6 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -142,7 +142,7 @@ available for some distributions: Exercise caution when using them. There is also a `snap package `_, however, it is **currently unmaintained**. -It is installable in all the `supported Linux distros `_. To +It is installable in all the `supported Linux distros `_. To install the latest stable version of solc: .. code-block:: bash From ffd11b3cf15118c5f99699eed497a12178165fb7 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 12 Feb 2026 15:20:02 +0100 Subject: [PATCH 198/243] Test cases --- .../args | 1 + .../input.sol | 15 ++ .../output | 214 ++++++++++++++++++ ...fter_inherited_storage_same_value_type.sol | 26 +++ ...storage_mapping_delete_same_value_type.sol | 23 ++ ..._storage_struct_delete_same_value_type.sol | 28 +++ ...fore_inherited_storage_same_value_type.sol | 26 +++ ...storage_mapping_delete_same_value_type.sol | 23 ++ ..._storage_struct_delete_same_value_type.sol | 28 +++ ...storage_bool_array_transient_uint256_1.sol | 26 +++ ...storage_bool_array_transient_uint256_2.sol | 26 +++ .../delete_storage_u256_transient_u256_1.sol | 22 ++ .../delete_storage_u256_transient_u256_2.sol | 22 ++ 13 files changed, 480 insertions(+) create mode 100644 test/cmdlineTests/storage_transient_storage_collision_ir_output/args create mode 100644 test/cmdlineTests/storage_transient_storage_collision_ir_output/input.sol create mode 100644 test/cmdlineTests/storage_transient_storage_collision_ir_output/output create mode 100644 test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_inherited_storage_same_value_type.sol create mode 100644 test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_mapping_delete_same_value_type.sol create mode 100644 test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_struct_delete_same_value_type.sol create mode 100644 test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_inherited_storage_same_value_type.sol create mode 100644 test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_mapping_delete_same_value_type.sol create mode 100644 test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_struct_delete_same_value_type.sol create mode 100644 test/libsolidity/semanticTests/storage/delete_storage_bool_array_transient_uint256_1.sol create mode 100644 test/libsolidity/semanticTests/storage/delete_storage_bool_array_transient_uint256_2.sol create mode 100644 test/libsolidity/semanticTests/storage/delete_storage_u256_transient_u256_1.sol create mode 100644 test/libsolidity/semanticTests/storage/delete_storage_u256_transient_u256_2.sol diff --git a/test/cmdlineTests/storage_transient_storage_collision_ir_output/args b/test/cmdlineTests/storage_transient_storage_collision_ir_output/args new file mode 100644 index 0000000000..b7ec904f10 --- /dev/null +++ b/test/cmdlineTests/storage_transient_storage_collision_ir_output/args @@ -0,0 +1 @@ +--ir-optimized --evm-version cancun --debug-info none diff --git a/test/cmdlineTests/storage_transient_storage_collision_ir_output/input.sol b/test/cmdlineTests/storage_transient_storage_collision_ir_output/input.sol new file mode 100644 index 0000000000..e7d3930152 --- /dev/null +++ b/test/cmdlineTests/storage_transient_storage_collision_ir_output/input.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 +pragma solidity >=0.0; + +contract C { + uint256 transient varTransient; + uint256 public varStorage = 0xeeeeeeeeee; + + function foo() external returns (uint256) { + varTransient = 0xffffffff; + delete varTransient; + delete varStorage; + + return varStorage; + } +} diff --git a/test/cmdlineTests/storage_transient_storage_collision_ir_output/output b/test/cmdlineTests/storage_transient_storage_collision_ir_output/output new file mode 100644 index 0000000000..e3a251b2d8 --- /dev/null +++ b/test/cmdlineTests/storage_transient_storage_collision_ir_output/output @@ -0,0 +1,214 @@ +Optimized IR: +/// @use-src 0:"input.sol" +object "C_25" { + code { + { + mstore(64, memoryguard(0x80)) + if callvalue() + { + revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() + } + constructor_C() + let _1 := allocate_unbounded() + codecopy(_1, dataoffset("C_25_deployed"), datasize("C_25_deployed")) + return(_1, datasize("C_25_deployed")) + } + function allocate_unbounded() -> memPtr + { memPtr := mload(64) } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() + { revert(0, 0) } + function shift_left(value) -> newValue + { newValue := shl(0, value) } + function update_byte_slice_shift(value, toInsert) -> result + { + let mask := not(0) + toInsert := shift_left(toInsert) + value := and(value, not(mask)) + result := or(value, and(toInsert, mask)) + } + function cleanup_rational_by(value) -> cleaned + { cleaned := value } + function cleanup_uint256(value) -> cleaned + { cleaned := value } + function identity(value) -> ret + { ret := value } + function convert_rational_by_to_uint256(value) -> converted + { + converted := cleanup_uint256(identity(cleanup_rational_by(value))) + } + function prepare_store_uint256(value) -> ret + { ret := value } + function update_storage_value_offset_rational_by_to_uint256(slot, value) + { + let convertedValue := convert_rational_by_to_uint256(value) + sstore(slot, update_byte_slice_shift(sload(slot), prepare_store_uint256(convertedValue))) + } + function constructor_C() + { + let expr := 0xeeeeeeeeee + update_storage_value_offset_rational_by_to_uint256(0x00, expr) + } + } + /// @use-src 0:"input.sol" + object "C_25_deployed" { + code { + { + mstore(64, memoryguard(0x80)) + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_unsigned(calldataload(0)) + switch selector + case 0xc2985578 { external_fun_foo() } + case 0xff2558ff { external_fun_varStorage() } + default { } + } + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + } + function shift_right_unsigned(value) -> newValue + { newValue := shr(224, value) } + function allocate_unbounded() -> memPtr + { memPtr := mload(64) } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() + { revert(0, 0) } + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() + { revert(0, 0) } + function abi_decode(headStart, dataEnd) + { + if slt(sub(dataEnd, headStart), 0) + { + revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() + } + } + function cleanup_uint256(value) -> cleaned + { cleaned := value } + function abi_encode_uint256_to_uint256(value, pos) + { + mstore(pos, cleanup_uint256(value)) + } + function abi_encode_uint256(headStart, value0) -> tail + { + tail := add(headStart, 32) + abi_encode_uint256_to_uint256(value0, add(headStart, 0)) + } + function external_fun_foo() + { + if callvalue() + { + revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() + } + abi_decode(4, calldatasize()) + let ret := fun_foo() + let memPos := allocate_unbounded() + let memEnd := abi_encode_uint256(memPos, ret) + return(memPos, sub(memEnd, memPos)) + } + function shift_right_unsigned_dynamic(bits, value) -> newValue + { newValue := shr(bits, value) } + function cleanup_from_storage_uint256(value) -> cleaned + { cleaned := value } + function extract_from_storage_value_dynamict_uint256(slot_value, offset) -> value + { + value := cleanup_from_storage_uint256(shift_right_unsigned_dynamic(mul(offset, 8), slot_value)) + } + function read_from_storage_split_dynamic_uint256(slot, offset) -> value + { + value := extract_from_storage_value_dynamict_uint256(sload(slot), offset) + } + function getter_fun_varStorage() -> ret + { + let slot := 0 + let offset := 0 + ret := read_from_storage_split_dynamic_uint256(slot, offset) + } + function external_fun_varStorage() + { + if callvalue() + { + revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() + } + abi_decode(4, calldatasize()) + let ret := getter_fun_varStorage() + let memPos := allocate_unbounded() + let memEnd := abi_encode_uint256(memPos, ret) + return(memPos, sub(memEnd, memPos)) + } + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + { revert(0, 0) } + function zero_value_for_split_uint256() -> ret + { ret := 0 } + function cleanup_rational_by(value) -> cleaned + { cleaned := value } + function identity(value) -> ret + { ret := value } + function convert_rational_by_to_uint256(value) -> converted + { + converted := cleanup_uint256(identity(cleanup_rational_by(value))) + } + function shift_left(value) -> newValue + { newValue := shl(0, value) } + function update_byte_slice_shift(value, toInsert) -> result + { + let mask := not(0) + toInsert := shift_left(toInsert) + value := and(value, not(mask)) + result := or(value, and(toInsert, mask)) + } + function convert_uint256_to_uint256(value) -> converted + { + converted := cleanup_uint256(identity(cleanup_uint256(value))) + } + function prepare_store_uint256(value) -> ret + { ret := value } + function update_transient_storage_value_offset_uint256_to_uint256(slot, value) + { + let convertedValue := convert_uint256_to_uint256(value) + tstore(slot, update_byte_slice_shift(tload(slot), prepare_store_uint256(convertedValue))) + } + function shift_left_dynamic(bits, value) -> newValue + { newValue := shl(bits, value) } + function update_byte_slice_dynamic32(value, shiftBytes, toInsert) -> result + { + let shiftBits := mul(shiftBytes, 8) + let mask := shift_left_dynamic(shiftBits, not(0)) + toInsert := shift_left_dynamic(shiftBits, toInsert) + value := and(value, not(mask)) + result := or(value, and(toInsert, mask)) + } + function update_transient_storage_value_uint256_to_uint256(slot, offset, value) + { + let convertedValue := convert_uint256_to_uint256(value) + tstore(slot, update_byte_slice_dynamic32(tload(slot), offset, prepare_store_uint256(convertedValue))) + } + function storage_set_to_zero_uint256(slot, offset) + { + let zero := zero_value_for_split_uint256() + update_transient_storage_value_uint256_to_uint256(slot, offset, zero) + } + function shift_right_0_unsigned(value) -> newValue + { newValue := shr(0, value) } + function extract_from_storage_value_offset_uint256(slot_value) -> value + { + value := cleanup_from_storage_uint256(shift_right_0_unsigned(slot_value)) + } + function read_from_storage_split_offset_uint256(slot) -> value + { + value := extract_from_storage_value_offset_uint256(sload(slot)) + } + function fun_foo() -> var + { + let zero_uint256 := zero_value_for_split_uint256() + var := zero_uint256 + let expr := 0xffffffff + let _1 := convert_rational_by_to_uint256(expr) + update_transient_storage_value_offset_uint256_to_uint256(0x00, _1) + storage_set_to_zero_uint256(0x00, 0) + storage_set_to_zero_uint256(0x00, 0) + let _2 := read_from_storage_split_offset_uint256(0x00) + let expr_1 := _2 + var := expr_1 + leave + } + } + data ".metadata" hex"" + } +} diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_inherited_storage_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_inherited_storage_same_value_type.sol new file mode 100644 index 0000000000..f4badbc988 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_inherited_storage_same_value_type.sol @@ -0,0 +1,26 @@ +contract Base { + uint256 public x; + + function deleteX() internal { + delete x; + } +} + +contract C is Base { + uint256 transient t; + + function setAndClear() external { + x = 1; + t = 2; + deleteX(); + delete t; + assert(t == 0); + } +} + +// ==== +// EVMVersion: >=cancun +// compileViaYul: true +// ---- +// setAndClear() -> +// x() -> 1 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_mapping_delete_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_mapping_delete_same_value_type.sol new file mode 100644 index 0000000000..b1a3883215 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_mapping_delete_same_value_type.sol @@ -0,0 +1,23 @@ +contract C { + mapping(uint256 => uint256) m; + uint256 transient t; + + function setAndClear() external { + m[0] = 1; + t = 2; + delete m[0]; + delete t; + assert(t == 2); + } + + function getM() external view returns (uint256) { + return m[0]; + } +} + +// ==== +// EVMVersion: >=cancun +// compileViaYul: true +// ---- +// setAndClear() -> +// getM() -> 0 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_struct_delete_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_struct_delete_same_value_type.sol new file mode 100644 index 0000000000..33a0dc2de4 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_struct_delete_same_value_type.sol @@ -0,0 +1,28 @@ +contract C { + struct S { + uint256 a; + address b; + } + + S s; + uint256 transient t; + + function setAndDelete() external { + s.a = 1; + s.b = address(0x1234); + t = 2; + delete s; + delete t; + assert(t == 2); + } + + function getS() external view returns (uint256, address) { + return (s.a, s.b); + } +} +// ==== +// EVMVersion: >=cancun +// compileViaYul: true +// ---- +// setAndDelete() -> +// getS() -> 0, 0 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_inherited_storage_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_inherited_storage_same_value_type.sol new file mode 100644 index 0000000000..8e40422a90 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_inherited_storage_same_value_type.sol @@ -0,0 +1,26 @@ +contract Base { + uint256 public x; + + function deleteX() internal { + delete x; + } +} + +contract C is Base { + uint256 transient t; + + function setAndClear() external { + x = 1; + t = 2; + delete t; + assert(t == 0); + deleteX(); + } +} + +// ==== +// EVMVersion: >=cancun +// compileViaYul: true +// ---- +// setAndClear() -> +// x() -> 1 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_mapping_delete_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_mapping_delete_same_value_type.sol new file mode 100644 index 0000000000..129fafad75 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_mapping_delete_same_value_type.sol @@ -0,0 +1,23 @@ +contract C { + mapping(uint256 => uint256) m; + uint256 transient t; + + function setAndClear() external { + m[0] = 1; + t = 2; + delete t; + assert(t == 0); + delete m[0]; + } + + function getM() external view returns (uint256) { + return m[0]; + } +} + +// ==== +// EVMVersion: >=cancun +// compileViaYul: true +// ---- +// setAndClear() -> +// getM() -> 1 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_struct_delete_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_struct_delete_same_value_type.sol new file mode 100644 index 0000000000..d499d06183 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_struct_delete_same_value_type.sol @@ -0,0 +1,28 @@ +contract C { + struct S { + uint256 a; + address b; + } + + S s; + uint256 transient t; + + function setAndDelete() external { + s.a = 1; + s.b = address(0x1234); + t = 2; + delete t; + assert(t == 0); + delete s; + } + + function getS() external view returns (uint256, address) { + return (s.a, s.b); + } +} +// ==== +// EVMVersion: >=cancun +// compileViaYul: true +// ---- +// setAndDelete() -> +// getS() -> 1, 0 diff --git a/test/libsolidity/semanticTests/storage/delete_storage_bool_array_transient_uint256_1.sol b/test/libsolidity/semanticTests/storage/delete_storage_bool_array_transient_uint256_1.sol new file mode 100644 index 0000000000..4d84b23f70 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_storage_bool_array_transient_uint256_1.sol @@ -0,0 +1,26 @@ +pragma solidity >=0.0; + +contract C { + bool[3] flags = [true, true, true]; + uint256 transient temp; + + function f() external { + temp = 0xffffffff; + assert(temp == 0xffffffff); + delete temp; + delete flags; + } + + function getFlags() external returns(bool[3] memory) + { + return flags; + } +} + +// ==== +// EVMVersion: >=cancun +// compileViaYul: true +// ---- +// getFlags() -> true, true, true +// f() -> +// getFlags() -> true, true, true diff --git a/test/libsolidity/semanticTests/storage/delete_storage_bool_array_transient_uint256_2.sol b/test/libsolidity/semanticTests/storage/delete_storage_bool_array_transient_uint256_2.sol new file mode 100644 index 0000000000..f95b191c10 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_storage_bool_array_transient_uint256_2.sol @@ -0,0 +1,26 @@ +pragma solidity >=0.0; + +contract C { + bool[3] flags = [true, true, true]; + uint256 transient temp; + + function f() external { + temp = 0xffffffff; + assert(temp == 0xffffffff); + delete flags; + delete temp; + } + + function getFlags() external returns(bool[3] memory) + { + return flags; + } +} + +// ==== +// EVMVersion: >=cancun +// compileViaYul: true +// ---- +// getFlags() -> true, true, true +// f() -> +// getFlags() -> false, false, false diff --git a/test/libsolidity/semanticTests/storage/delete_storage_u256_transient_u256_1.sol b/test/libsolidity/semanticTests/storage/delete_storage_u256_transient_u256_1.sol new file mode 100644 index 0000000000..08f8120be0 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_storage_u256_transient_u256_1.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0 +pragma solidity >=0.0; + +contract C { + uint256 transient varTransient; + uint256 public varStorage = 0xeeeeeeeeee; + + function foo() external { + varTransient = 0xffffffff; + assert(varTransient == 0xffffffff); + delete varTransient; + delete varStorage; + } +} + +// ==== +// EVMVersion: >=cancun +// compileViaYul: true +// ---- +// varStorage() -> 0xeeeeeeeeee +// foo() -> +// varStorage() -> 0xeeeeeeeeee diff --git a/test/libsolidity/semanticTests/storage/delete_storage_u256_transient_u256_2.sol b/test/libsolidity/semanticTests/storage/delete_storage_u256_transient_u256_2.sol new file mode 100644 index 0000000000..03e06747af --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_storage_u256_transient_u256_2.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0 +pragma solidity >=0.0; + +contract C { + uint256 transient varTransient; + uint256 public varStorage = 0xeeeeeeeeee; + + function foo() external { + varTransient = 0xffffffff; + assert(varTransient == 0xffffffff); + delete varStorage; + delete varTransient; + } +} + +// ==== +// EVMVersion: >=cancun +// compileViaYul: true +// ---- +// varStorage() -> 0xeeeeeeeeee +// foo() -> +// varStorage() -> 0 From 580a72900dab63e14be7d171cf88f5cfce4d52a0 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 12 Feb 2026 15:20:49 +0100 Subject: [PATCH 199/243] Changelog --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index d1bc022e3a..9bf32bf258 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,8 @@ ### 0.8.34 (unreleased) +Important Bugfixes: +* Yul IR Code Generation: Fix a bug that could result in clearing a storage variable instead of a transient storage variable at the same position in the layout (and vice-versa). + Language Features: Compiler Features: From 12ede4f2650038b7f4c38ed1ef52b5d2da7d2dda Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 12 Feb 2026 15:22:09 +0100 Subject: [PATCH 200/243] Fix transient/storage collision when deleting --- libsolidity/codegen/YulUtilFunctions.cpp | 17 ++++++++++- .../output | 14 +++++++-- ...fter_inherited_storage_same_value_type.sol | 3 +- ...rage_array_delete_different_base_type.sol} | 9 ++---- ...after_storage_array_pop_same_base_type.sol | 30 +++++++++++++++++++ ..._after_storage_delete_same_value_type.sol} | 10 ++----- ...storage_mapping_delete_same_value_type.sol | 3 +- ..._storage_struct_delete_same_value_type.sol | 8 ++--- ...fore_inherited_storage_same_value_type.sol | 3 +- ...rage_array_delete_different_base_type.sol} | 11 +++---- ...rray_partial_assignment_same_base_type.sol | 27 +++++++++++++++++ ...before_storage_delete_same_value_type.sol} | 12 +++----- ...storage_mapping_delete_same_value_type.sol | 3 +- ..._storage_struct_delete_same_value_type.sol | 8 ++--- 14 files changed, 109 insertions(+), 49 deletions(-) rename test/libsolidity/semanticTests/storage/{delete_storage_bool_array_transient_uint256_2.sol => delete_overlapping_transient_after_storage_array_delete_different_base_type.sol} (75%) create mode 100644 test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_array_pop_same_base_type.sol rename test/libsolidity/semanticTests/storage/{delete_storage_u256_transient_u256_2.sol => delete_overlapping_transient_after_storage_delete_same_value_type.sol} (63%) rename test/libsolidity/semanticTests/storage/{delete_storage_bool_array_transient_uint256_1.sol => delete_overlapping_transient_before_storage_array_delete_different_base_type.sol} (67%) create mode 100644 test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_array_partial_assignment_same_base_type.sol rename test/libsolidity/semanticTests/storage/{delete_storage_u256_transient_u256_1.sol => delete_overlapping_transient_before_storage_delete_same_value_type.sol} (57%) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 4c0a702692..bb18217f2f 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -4357,7 +4357,22 @@ std::string YulUtilFunctions::zeroValueFunction(Type const& _type, bool _splitFu std::string YulUtilFunctions::storageSetToZeroFunction(Type const& _type, VariableDeclaration::Location _location) { - std::string const functionName = "storage_set_to_zero_" + _type.identifier(); + solAssert( + _location == VariableDeclaration::Location::Transient || + _location == VariableDeclaration::Location::Unspecified, + "Invalid location for the storage_set_to_zero function" + ); + + if (dynamic_cast(&_type)) + solAssert( + _location == VariableDeclaration::Location::Unspecified && + _type.dataStoredIn(DataLocation::Storage) + ); + + std::string const functionName = + (_location == VariableDeclaration::Location::Transient ? "transient_"s : "") + + "storage_set_to_zero_" + + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { if (_type.isValueType()) diff --git a/test/cmdlineTests/storage_transient_storage_collision_ir_output/output b/test/cmdlineTests/storage_transient_storage_collision_ir_output/output index e3a251b2d8..dd8abeb333 100644 --- a/test/cmdlineTests/storage_transient_storage_collision_ir_output/output +++ b/test/cmdlineTests/storage_transient_storage_collision_ir_output/output @@ -179,11 +179,21 @@ object "C_25" { let convertedValue := convert_uint256_to_uint256(value) tstore(slot, update_byte_slice_dynamic32(tload(slot), offset, prepare_store_uint256(convertedValue))) } - function storage_set_to_zero_uint256(slot, offset) + function transient_storage_set_to_zero_uint256(slot, offset) { let zero := zero_value_for_split_uint256() update_transient_storage_value_uint256_to_uint256(slot, offset, zero) } + function update_storage_value_uint256_to_uint256(slot, offset, value) + { + let convertedValue := convert_uint256_to_uint256(value) + sstore(slot, update_byte_slice_dynamic32(sload(slot), offset, prepare_store_uint256(convertedValue))) + } + function storage_set_to_zero_uint256(slot, offset) + { + let zero := zero_value_for_split_uint256() + update_storage_value_uint256_to_uint256(slot, offset, zero) + } function shift_right_0_unsigned(value) -> newValue { newValue := shr(0, value) } function extract_from_storage_value_offset_uint256(slot_value) -> value @@ -201,7 +211,7 @@ object "C_25" { let expr := 0xffffffff let _1 := convert_rational_by_to_uint256(expr) update_transient_storage_value_offset_uint256_to_uint256(0x00, _1) - storage_set_to_zero_uint256(0x00, 0) + transient_storage_set_to_zero_uint256(0x00, 0) storage_set_to_zero_uint256(0x00, 0) let _2 := read_from_storage_split_offset_uint256(0x00) let expr_1 := _2 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_inherited_storage_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_inherited_storage_same_value_type.sol index f4badbc988..dae46cd32b 100644 --- a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_inherited_storage_same_value_type.sol +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_inherited_storage_same_value_type.sol @@ -20,7 +20,6 @@ contract C is Base { // ==== // EVMVersion: >=cancun -// compileViaYul: true // ---- // setAndClear() -> -// x() -> 1 +// x() -> 0 diff --git a/test/libsolidity/semanticTests/storage/delete_storage_bool_array_transient_uint256_2.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_array_delete_different_base_type.sol similarity index 75% rename from test/libsolidity/semanticTests/storage/delete_storage_bool_array_transient_uint256_2.sol rename to test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_array_delete_different_base_type.sol index f95b191c10..d9a904f3ca 100644 --- a/test/libsolidity/semanticTests/storage/delete_storage_bool_array_transient_uint256_2.sol +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_array_delete_different_base_type.sol @@ -1,14 +1,12 @@ -pragma solidity >=0.0; - contract C { bool[3] flags = [true, true, true]; uint256 transient temp; - function f() external { + function setAndClear() external { temp = 0xffffffff; - assert(temp == 0xffffffff); delete flags; delete temp; + assert(temp == 0); } function getFlags() external returns(bool[3] memory) @@ -19,8 +17,7 @@ contract C { // ==== // EVMVersion: >=cancun -// compileViaYul: true // ---- // getFlags() -> true, true, true -// f() -> +// setAndClear() -> // getFlags() -> false, false, false diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_array_pop_same_base_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_array_pop_same_base_type.sol new file mode 100644 index 0000000000..76dc10c43c --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_array_pop_same_base_type.sol @@ -0,0 +1,30 @@ +contract C { + uint256[] arr; + uint256 transient t; + + function pushArr() external { + arr.push(1); + } + + function setAndClear() external { + t = 2; + delete t; + assert(t == 0); + arr.pop(); + } + + // Get value at index 0, which should have been cleared after arr.pop() + function getArr() external returns (uint256 value) { + assembly { + mstore(0, arr.slot) + value := sload(keccak256(0x00, 0x20)) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// pushArr() -> +// getArr() -> 1 +// setAndClear() -> +// getArr() -> 0 diff --git a/test/libsolidity/semanticTests/storage/delete_storage_u256_transient_u256_2.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_delete_same_value_type.sol similarity index 63% rename from test/libsolidity/semanticTests/storage/delete_storage_u256_transient_u256_2.sol rename to test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_delete_same_value_type.sol index 03e06747af..ad206c6eee 100644 --- a/test/libsolidity/semanticTests/storage/delete_storage_u256_transient_u256_2.sol +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_delete_same_value_type.sol @@ -1,22 +1,18 @@ -// SPDX-License-Identifier: GPL-2.0 -pragma solidity >=0.0; - contract C { uint256 transient varTransient; uint256 public varStorage = 0xeeeeeeeeee; - function foo() external { + function setAndClear() external { varTransient = 0xffffffff; - assert(varTransient == 0xffffffff); delete varStorage; delete varTransient; + assert(varTransient == 0); } } // ==== // EVMVersion: >=cancun -// compileViaYul: true // ---- // varStorage() -> 0xeeeeeeeeee -// foo() -> +// setAndClear() -> // varStorage() -> 0 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_mapping_delete_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_mapping_delete_same_value_type.sol index b1a3883215..842cd47504 100644 --- a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_mapping_delete_same_value_type.sol +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_mapping_delete_same_value_type.sol @@ -7,7 +7,7 @@ contract C { t = 2; delete m[0]; delete t; - assert(t == 2); + assert(t == 0); } function getM() external view returns (uint256) { @@ -17,7 +17,6 @@ contract C { // ==== // EVMVersion: >=cancun -// compileViaYul: true // ---- // setAndClear() -> // getM() -> 0 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_struct_delete_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_struct_delete_same_value_type.sol index 33a0dc2de4..7038162962 100644 --- a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_struct_delete_same_value_type.sol +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_struct_delete_same_value_type.sol @@ -4,16 +4,14 @@ contract C { address b; } - S s; + S s = S(1, address(0x1234)); uint256 transient t; function setAndDelete() external { - s.a = 1; - s.b = address(0x1234); t = 2; delete s; delete t; - assert(t == 2); + assert(t == 0); } function getS() external view returns (uint256, address) { @@ -22,7 +20,7 @@ contract C { } // ==== // EVMVersion: >=cancun -// compileViaYul: true // ---- +// getS() -> 1, 4660 // setAndDelete() -> // getS() -> 0, 0 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_inherited_storage_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_inherited_storage_same_value_type.sol index 8e40422a90..3af1b66e59 100644 --- a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_inherited_storage_same_value_type.sol +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_inherited_storage_same_value_type.sol @@ -20,7 +20,6 @@ contract C is Base { // ==== // EVMVersion: >=cancun -// compileViaYul: true // ---- // setAndClear() -> -// x() -> 1 +// x() -> 0 diff --git a/test/libsolidity/semanticTests/storage/delete_storage_bool_array_transient_uint256_1.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_array_delete_different_base_type.sol similarity index 67% rename from test/libsolidity/semanticTests/storage/delete_storage_bool_array_transient_uint256_1.sol rename to test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_array_delete_different_base_type.sol index 4d84b23f70..c2e1c1a241 100644 --- a/test/libsolidity/semanticTests/storage/delete_storage_bool_array_transient_uint256_1.sol +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_array_delete_different_base_type.sol @@ -1,13 +1,11 @@ -pragma solidity >=0.0; - contract C { bool[3] flags = [true, true, true]; uint256 transient temp; - function f() external { + function setAndClear() external { temp = 0xffffffff; - assert(temp == 0xffffffff); delete temp; + assert(temp == 0); delete flags; } @@ -19,8 +17,7 @@ contract C { // ==== // EVMVersion: >=cancun -// compileViaYul: true // ---- // getFlags() -> true, true, true -// f() -> -// getFlags() -> true, true, true +// setAndClear() -> +// getFlags() -> false, false, false diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_array_partial_assignment_same_base_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_array_partial_assignment_same_base_type.sol new file mode 100644 index 0000000000..a9d3e0ae73 --- /dev/null +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_array_partial_assignment_same_base_type.sol @@ -0,0 +1,27 @@ +contract C { + uint256[2] small; + uint256[4] large; + uint256 transient t; + + function setAndClear() external { + large = [1,2,3,4]; + small = [10, 20]; + t = 99; + + delete t; + assert(t == 0); + large = small; + } + + function getLarge() external view returns (uint256[4] memory) { + return large; + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// setAndClear() -> +// gas irOptimized: 124683 +// gas legacy: 127807 +// gas legacyOptimized: 124828 +// getLarge() -> 10, 20, 0, 0 diff --git a/test/libsolidity/semanticTests/storage/delete_storage_u256_transient_u256_1.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_delete_same_value_type.sol similarity index 57% rename from test/libsolidity/semanticTests/storage/delete_storage_u256_transient_u256_1.sol rename to test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_delete_same_value_type.sol index 08f8120be0..26e34fc26a 100644 --- a/test/libsolidity/semanticTests/storage/delete_storage_u256_transient_u256_1.sol +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_delete_same_value_type.sol @@ -1,22 +1,18 @@ -// SPDX-License-Identifier: GPL-2.0 -pragma solidity >=0.0; - contract C { uint256 transient varTransient; uint256 public varStorage = 0xeeeeeeeeee; - function foo() external { + function setAndClear() external { varTransient = 0xffffffff; - assert(varTransient == 0xffffffff); delete varTransient; + assert(varTransient == 0); delete varStorage; } } // ==== // EVMVersion: >=cancun -// compileViaYul: true // ---- // varStorage() -> 0xeeeeeeeeee -// foo() -> -// varStorage() -> 0xeeeeeeeeee +// setAndClear() -> +// varStorage() -> 0 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_mapping_delete_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_mapping_delete_same_value_type.sol index 129fafad75..6e262c8a62 100644 --- a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_mapping_delete_same_value_type.sol +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_mapping_delete_same_value_type.sol @@ -17,7 +17,6 @@ contract C { // ==== // EVMVersion: >=cancun -// compileViaYul: true // ---- // setAndClear() -> -// getM() -> 1 +// getM() -> 0 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_struct_delete_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_struct_delete_same_value_type.sol index d499d06183..c2127c9bad 100644 --- a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_struct_delete_same_value_type.sol +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_struct_delete_same_value_type.sol @@ -4,12 +4,10 @@ contract C { address b; } - S s; + S s = S(1, address(0x1234)); uint256 transient t; function setAndDelete() external { - s.a = 1; - s.b = address(0x1234); t = 2; delete t; assert(t == 0); @@ -22,7 +20,7 @@ contract C { } // ==== // EVMVersion: >=cancun -// compileViaYul: true // ---- +// getS() -> 1, 4660 // setAndDelete() -> -// getS() -> 1, 0 +// getS() -> 0, 0 From 57e789309840a528eb868a6de25f56f27c52820e Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Fri, 13 Feb 2026 11:10:47 +0100 Subject: [PATCH 201/243] Bug list entry --- docs/bugs.json | 14 ++++++++++++++ docs/bugs_by_version.json | 12 ++++++++++-- ...after_storage_struct_delete_same_value_type.sol | 2 +- ...efore_storage_struct_delete_same_value_type.sol | 2 +- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/docs/bugs.json b/docs/bugs.json index 0c0b0493d7..5f8d1a1627 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -1,4 +1,18 @@ [ + { + "uid": "SOL-2026-1", + "name": "TransientStorageClearingHelperCollision", + "summary": "Clearing both storage and transient storage variables in the same contract may result in only one of these locations being cleared.", + "description": "The IR-based code generator provides a set of Yul helper functions for basic operations, such as clearing, copying, encoding or type conversions. Not all functions are used by every contract. The codegen appends them to the generated sources individually, only when an operation that would invoke one of them is encountered. Utility functions are often specialized for different types and locations. Since Yul does not support generic functions, specialization is done by generating multiple versions of the same function, with the information distinguishing the variants embedded in their names. However, if not all the necessary bits of distinguishing information are properly accounted for, two helpers may end up with the same name, causing a collision. In this situation the codegen includes only one of them, with calls to both variants invoking it. This happened with the ``set_to_zero`` helper used when an area of transient or persistent storage needs to be cleared. The helper name was missing the location information, which resulted in a collision between the persistent and transient storage variants for the same type. This meant that contracts clearing both locations would actually clear only one, leaving the other untouched. Which location ended up being cleared depended on the order in which the code generator processed the input. The necessary condition to trigger the bug was the use of ``delete`` operator on a transient storage variable. This was due to value types being the only types supported in transient storage and ``delete`` being the only operation invoking the helper allowed on such types. The other necessary condition was clearing of persistent storage and in this case the range of affected operations was wider: operator ``delete``, array ``pop()`` or assignment that resulted in a longer array being overwritten with a shorter one. The cleared variable itself also did not necessarily have to be of the same type. It was enough that a matching value type was nested in it. It also did not always have to be the exact same value type - clearing operations on reference types are usually performed at slot granularity, treating every slot as ``uint256`` rather than clearing every value packed into it individually. To trigger the bug both operations had to be present within the same piece of bytecode. Independent contracts, not related through inheritance, would not affect each other this way. The presence of one operation only in creation code and the other only in deployed code would not trigger the bug either.", + "link": "https://blog.soliditylang.org/2026/02/18/transient-storage-clearing-helper-collision-bug/", + "introduced": "0.8.28", + "fixed": "0.8.34", + "severity": "high", + "conditions": { + "viaIR": true, + "evmVersion": ">=cancun" + } + }, { "uid": "SOL-2025-1", "name": "LostStorageArrayWriteOnSlotOverflow", diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 8a4d2fe23b..c76c6e7c17 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -2013,12 +2013,14 @@ }, "0.8.28": { "bugs": [ + "TransientStorageClearingHelperCollision", "LostStorageArrayWriteOnSlotOverflow" ], "released": "2024-10-09" }, "0.8.29": { "bugs": [ + "TransientStorageClearingHelperCollision", "LostStorageArrayWriteOnSlotOverflow" ], "released": "2025-03-12" @@ -2039,22 +2041,28 @@ }, "0.8.30": { "bugs": [ + "TransientStorageClearingHelperCollision", "LostStorageArrayWriteOnSlotOverflow" ], "released": "2025-05-07" }, "0.8.31": { "bugs": [ + "TransientStorageClearingHelperCollision", "LostStorageArrayWriteOnSlotOverflow" ], "released": "2025-12-03" }, "0.8.32": { - "bugs": [], + "bugs": [ + "TransientStorageClearingHelperCollision" + ], "released": "2025-12-18" }, "0.8.33": { - "bugs": [], + "bugs": [ + "TransientStorageClearingHelperCollision" + ], "released": "2025-12-18" }, "0.8.4": { diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_struct_delete_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_struct_delete_same_value_type.sol index 7038162962..04b8d47cfe 100644 --- a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_struct_delete_same_value_type.sol +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_after_storage_struct_delete_same_value_type.sol @@ -21,6 +21,6 @@ contract C { // ==== // EVMVersion: >=cancun // ---- -// getS() -> 1, 4660 +// getS() -> 1, 0x1234 // setAndDelete() -> // getS() -> 0, 0 diff --git a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_struct_delete_same_value_type.sol b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_struct_delete_same_value_type.sol index c2127c9bad..1c9cc49650 100644 --- a/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_struct_delete_same_value_type.sol +++ b/test/libsolidity/semanticTests/storage/delete_overlapping_transient_before_storage_struct_delete_same_value_type.sol @@ -21,6 +21,6 @@ contract C { // ==== // EVMVersion: >=cancun // ---- -// getS() -> 1, 4660 +// getS() -> 1, 0x1234 // setAndDelete() -> // getS() -> 0, 0 From e958997006159edca53cd25d6cbd2bc8869b0acd Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 18 Feb 2026 09:45:19 +0100 Subject: [PATCH 202/243] Changelog for 0.8.34 --- Changelog.md | 5 +---- docs/bugs_by_version.json | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 9bf32bf258..e4b13172cf 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,15 +1,12 @@ -### 0.8.34 (unreleased) +### 0.8.34 (2026-02-18) Important Bugfixes: * Yul IR Code Generation: Fix a bug that could result in clearing a storage variable instead of a transient storage variable at the same position in the layout (and vice-versa). -Language Features: Compiler Features: * Yul Optimizer: Remove redundant prerequisite steps from the default optimizer sequence. -Bugfixes: - ### 0.8.33 (2025-12-18) diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index c76c6e7c17..c5576d4b29 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -2065,6 +2065,10 @@ ], "released": "2025-12-18" }, + "0.8.34": { + "bugs": [], + "released": "2026-02-18" + }, "0.8.4": { "bugs": [ "LostStorageArrayWriteOnSlotOverflow", From 6a8f003a8da7a7142d9ec6a8595a95d6897b891d Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 18 Feb 2026 13:38:11 +0100 Subject: [PATCH 203/243] Set version to 0.8.35 --- CMakeLists.txt | 2 +- Changelog.md | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 82afc5b67d..410acd86fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.8.34") +set(PROJECT_VERSION "0.8.35") # OSX target needed in order to support std::visit set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) diff --git a/Changelog.md b/Changelog.md index e4b13172cf..f6e4733f6e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,12 @@ +### 0.8.35 (unreleased) + +Language Features: + +Compiler Features: + +Bugfixes: + + ### 0.8.34 (2026-02-18) Important Bugfixes: From 98f84a78dc7e212b365944a0fed4a99a66c503c6 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Tue, 17 Feb 2026 18:00:32 +0100 Subject: [PATCH 204/243] update docker image of scripts/build_emscripten.sh the newer image contains ccache --- scripts/build_emscripten.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build_emscripten.sh b/scripts/build_emscripten.sh index aae4b5c1f3..e6e8c0a0dc 100755 --- a/scripts/build_emscripten.sh +++ b/scripts/build_emscripten.sh @@ -33,9 +33,9 @@ if (( $# != 0 )); then params="$(printf "%q " "${@}")" fi -# ghcr.io/argotorg/solidity-buildpack-deps:emscripten-21 +# ghcr.io/argotorg/solidity-buildpack-deps:emscripten-22 # NOTE: Without `safe.directory` git would assume it's not safe to operate on /root/project since it's owned by a different user. # See https://github.blog/2022-04-12-git-security-vulnerability-announced/ docker run -v "$(pwd):/root/project" -w /root/project \ - ghcr.io/argotorg/solidity-buildpack-deps@sha256:fc53d68a4680ffa7d5f70164e13a903478964f15bcc07434d74833a05f4fbc19 \ + ghcr.io/argotorg/solidity-buildpack-deps@sha256:b9c953144d82cca5553f65626bc43b8af456dbc8966f8732ff9522f33ca8d722 \ /bin/bash -c "git config --global --add safe.directory /root/project && ./scripts/ci/build_emscripten.sh ${params}" From 30f20186f642439ee6e491eb608061ab4ad43dfc Mon Sep 17 00:00:00 2001 From: Olexandr88 Date: Thu, 19 Feb 2026 16:38:07 +0200 Subject: [PATCH 205/243] optimiser: avoid double lookup in translateIdentifier (#16468) * optimiser: avoid double lookup in translateIdentifier Signed-off-by: Olexandr88 * Update libyul/optimiser/ASTCopier.cpp Co-authored-by: clonker <1685266+clonker@users.noreply.github.com> --------- Signed-off-by: Olexandr88 Co-authored-by: clonker <1685266+clonker@users.noreply.github.com> --- libyul/optimiser/ASTCopier.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libyul/optimiser/ASTCopier.cpp b/libyul/optimiser/ASTCopier.cpp index 9e85651ad3..2a913f8f9c 100644 --- a/libyul/optimiser/ASTCopier.cpp +++ b/libyul/optimiser/ASTCopier.cpp @@ -180,7 +180,5 @@ NameWithDebugData ASTCopier::translate(NameWithDebugData const& _typedName) YulName FunctionCopier::translateIdentifier(YulName _name) { - if (m_translations.count(_name)) - return m_translations.at(_name); - return _name; + return util::valueOrDefault(m_translations, _name, _name); } From e050973fa5eaf3b8c9be71432abfec95f4f67473 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Tue, 24 Feb 2026 10:53:00 +0100 Subject: [PATCH 206/243] Remove jump tables from SSA CFG --- libyul/backends/evm/ssa/LivenessAnalysis.cpp | 5 - libyul/backends/evm/ssa/SSACFG.h | 15 +- libyul/backends/evm/ssa/SSACFGBuilder.cpp | 176 ++++++------------ libyul/backends/evm/ssa/SSACFGBuilder.h | 7 - .../backends/evm/ssa/io/DotExporterBase.cpp | 22 --- libyul/backends/evm/ssa/io/JSONExporter.cpp | 3 - 6 files changed, 60 insertions(+), 168 deletions(-) diff --git a/libyul/backends/evm/ssa/LivenessAnalysis.cpp b/libyul/backends/evm/ssa/LivenessAnalysis.cpp index d97ff00971..2e93a5f148 100644 --- a/libyul/backends/evm/ssa/LivenessAnalysis.cpp +++ b/libyul/backends/evm/ssa/LivenessAnalysis.cpp @@ -145,11 +145,6 @@ LivenessAnalysis::LivenessData LivenessAnalysis::blockExitValues(SSACFG::BlockId for (auto const& valueId: _functionReturn.returnValues | ranges::views::filter(excludingLiteralsFilter())) result.insert(valueId); }, - [&](SSACFG::BasicBlock::JumpTable const& _jt) - { - if (excludingLiteralsFilter()(_jt.value)) - result.insert(_jt.value); - }, [](SSACFG::BasicBlock::Jump const&) {}, [&](SSACFG::BasicBlock::ConditionalJump const& _conditionalJump) { diff --git a/libyul/backends/evm/ssa/SSACFG.h b/libyul/backends/evm/ssa/SSACFG.h index 49d5a4e946..dcb0a26495 100644 --- a/libyul/backends/evm/ssa/SSACFG.h +++ b/libyul/backends/evm/ssa/SSACFG.h @@ -135,13 +135,6 @@ class SSACFG langutil::DebugData::ConstPtr debugData{}; BlockId target; }; - struct JumpTable - { - langutil::DebugData::ConstPtr debugData{}; - ValueId value; - std::map cases; - BlockId defaultCase; - }; struct FunctionReturn { langutil::DebugData::ConstPtr debugData{}; @@ -152,7 +145,7 @@ class SSACFG std::set entries; std::set phis; std::vector operations; - std::variant exit = MainExit{}; + std::variant exit = MainExit{}; template void forEachExit(Callable&& _callable) const { @@ -163,12 +156,6 @@ class SSACFG _callable(conditionalJump->nonZero); _callable(conditionalJump->zero); } - else if (auto* jumpTable = std::get_if(&exit)) - { - for (auto _case: jumpTable->cases | ranges::views::values) - _callable(_case); - _callable(jumpTable->defaultCase); - } } bool isMainExitBlock() const diff --git a/libyul/backends/evm/ssa/SSACFGBuilder.cpp b/libyul/backends/evm/ssa/SSACFGBuilder.cpp index 59e28b1b82..bc39c74fb7 100644 --- a/libyul/backends/evm/ssa/SSACFGBuilder.cpp +++ b/libyul/backends/evm/ssa/SSACFGBuilder.cpp @@ -139,10 +139,6 @@ SSACFG::ValueId SSACFGBuilder::tryRemoveTrivialPhi(SSACFG::ValueId _phi) if (_condJump.condition == _phi) _condJump.condition = same; }, - [_phi, same](SSACFG::BasicBlock::JumpTable& _jumpTable) { - if (_jumpTable.value == _phi) - _jumpTable.value = same; - }, [](SSACFG::BasicBlock::Jump&) {}, [](SSACFG::BasicBlock::MainExit&) {}, [](SSACFG::BasicBlock::Terminated&) {} @@ -172,7 +168,6 @@ void SSACFGBuilder::cleanUnreachable() _addChild(_jump.zero); _addChild(_jump.nonZero); }, - [](SSACFG::BasicBlock::JumpTable const&) { yulAssert(false); }, [](SSACFG::BasicBlock::FunctionReturn const&) {}, [](SSACFG::BasicBlock::Terminated const&) {}, [](SSACFG::BasicBlock::MainExit const&) {} @@ -312,113 +307,77 @@ void SSACFGBuilder::operator()(Switch const& _switch) { auto expression = std::visit(*this, *_switch.expression); - auto useJumpTableForSwitch = [](Switch const&) { - // TODO: check for EOF support & tight switch values. - return false; - }; - if (useJumpTableForSwitch(_switch)) + if (auto const* constantExpression = std::get_if(_switch.expression.get())) { - // TODO: also generate a subtraction to shift tight, but non-zero switch cases - or, alternative, - // transform to zero-based tight switches on Yul if possible. - std::map cases; - std::optional defaultCase; - std::vector>> children; - for (auto const& _case: _switch.cases) + Case const* matchedCase = nullptr; + // select case that matches (or default if available) + for (auto const& switchCase: _switch.cases) { - auto blockId = m_graph.makeBlock(debugDataOf(_case.body)); - if (_case.value) - cases[_case.value->value.value()] = blockId; - else - defaultCase = blockId; - children.emplace_back(blockId, std::ref(_case.body)); + if (!switchCase.value) + matchedCase = &switchCase; + if (switchCase.value && switchCase.value->value.value() == constantExpression->value.value()) + { + matchedCase = &switchCase; + break; + } } - auto afterSwitch = m_graph.makeBlock(debugDataOf(currentBlock())); - - tableJump(debugDataOf(_switch), expression, cases, defaultCase ? *defaultCase : afterSwitch); - for (auto [blockId, block]: children) + if (matchedCase) { - sealBlock(blockId); - m_currentBlock = blockId; - (*this)(block); - jump(debugDataOf(currentBlock()), afterSwitch); + // inject directly into the current block + (*this)(matchedCase->body); } - sealBlock(afterSwitch); - m_currentBlock = afterSwitch; + return; } - else - { - if (auto const* constantExpression = std::get_if(_switch.expression.get())) - { - Case const* matchedCase = nullptr; - // select case that matches (or default if available) - for (auto const& switchCase: _switch.cases) - { - if (!switchCase.value) - matchedCase = &switchCase; - if (switchCase.value && switchCase.value->value.value() == constantExpression->value.value()) - { - matchedCase = &switchCase; - break; - } - } - if (matchedCase) - { - // inject directly into the current block - (*this)(matchedCase->body); - } - return; - } - - std::optional equalityBuiltinHandle = m_dialect.equalityFunctionHandle(); - yulAssert(equalityBuiltinHandle); - auto makeValueCompare = [&](Case const& _case) { - FunctionCall const& ghostCall = m_graph.ghostCalls.emplace_back(FunctionCall{ + std::optional equalityBuiltinHandle = m_dialect.equalityFunctionHandle(); + yulAssert(equalityBuiltinHandle); + + auto makeValueCompare = [&](Case const& _case) { + FunctionCall const& ghostCall = m_graph.ghostCalls.emplace_back(FunctionCall{ + debugDataOf(_case), + BuiltinName{{}, *equalityBuiltinHandle}, + {*_case.value /* skip second argument */ } + }); + auto outputValue = m_graph.newVariable(m_currentBlock); + currentBlock().operations.emplace_back(SSACFG::Operation{ + {outputValue}, + SSACFG::BuiltinCall{ debugDataOf(_case), - BuiltinName{{}, *equalityBuiltinHandle}, - {*_case.value /* skip second argument */ } - }); - auto outputValue = m_graph.newVariable(m_currentBlock); - currentBlock().operations.emplace_back(SSACFG::Operation{ - {outputValue}, - SSACFG::BuiltinCall{ - debugDataOf(_case), - m_dialect.builtin(*equalityBuiltinHandle), - ghostCall - }, - {m_graph.newLiteral(debugDataOf(_case), _case.value->value.value()), expression} - }); - return outputValue; - }; + m_dialect.builtin(*equalityBuiltinHandle), + ghostCall + }, + {m_graph.newLiteral(debugDataOf(_case), _case.value->value.value()), expression} + }); + return outputValue; + }; - auto afterSwitch = m_graph.makeBlock(debugDataOf(currentBlock())); - yulAssert(!_switch.cases.empty(), ""); - for (auto const& switchCase: _switch.cases | ranges::views::drop_last(1)) - { - yulAssert(switchCase.value, ""); - auto caseBranch = m_graph.makeBlock(debugDataOf(switchCase.body)); - auto elseBranch = m_graph.makeBlock(debugDataOf(_switch)); - - conditionalJump(debugDataOf(switchCase), makeValueCompare(switchCase), caseBranch, elseBranch); - sealBlock(caseBranch); - sealBlock(elseBranch); - m_currentBlock = caseBranch; - (*this)(switchCase.body); - jump(debugDataOf(switchCase.body), afterSwitch); - m_currentBlock = elseBranch; - } - Case const& switchCase = _switch.cases.back(); - if (switchCase.value) - { - auto caseBranch = m_graph.makeBlock(debugDataOf(switchCase.body)); - conditionalJump(debugDataOf(switchCase), makeValueCompare(switchCase), caseBranch, afterSwitch); - sealBlock(caseBranch); - m_currentBlock = caseBranch; - } + auto afterSwitch = m_graph.makeBlock(debugDataOf(currentBlock())); + yulAssert(!_switch.cases.empty(), ""); + for (auto const& switchCase: _switch.cases | ranges::views::drop_last(1)) + { + yulAssert(switchCase.value, ""); + auto caseBranch = m_graph.makeBlock(debugDataOf(switchCase.body)); + auto elseBranch = m_graph.makeBlock(debugDataOf(_switch)); + + conditionalJump(debugDataOf(switchCase), makeValueCompare(switchCase), caseBranch, elseBranch); + sealBlock(caseBranch); + sealBlock(elseBranch); + m_currentBlock = caseBranch; (*this)(switchCase.body); jump(debugDataOf(switchCase.body), afterSwitch); - sealBlock(afterSwitch); + m_currentBlock = elseBranch; } + Case const& switchCase = _switch.cases.back(); + if (switchCase.value) + { + auto caseBranch = m_graph.makeBlock(debugDataOf(switchCase.body)); + conditionalJump(debugDataOf(switchCase), makeValueCompare(switchCase), caseBranch, afterSwitch); + sealBlock(caseBranch); + m_currentBlock = caseBranch; + } + (*this)(switchCase.body); + jump(debugDataOf(switchCase.body), afterSwitch); + sealBlock(afterSwitch); } void SSACFGBuilder::operator()(ForLoop const& _loop) { @@ -756,23 +715,6 @@ void SSACFGBuilder::jump( m_currentBlock = _target; } -void SSACFGBuilder::tableJump( - langutil::DebugData::ConstPtr _debugData, - SSACFG::ValueId _value, - std::map _cases, - SSACFG::BlockId _defaultCase) -{ - for (auto caseBlock: _cases | ranges::views::values) - { - yulAssert(!blockInfo(caseBlock).sealed); - m_graph.block(caseBlock).entries.insert(m_currentBlock); - } - yulAssert(!blockInfo(_defaultCase).sealed); - m_graph.block(_defaultCase).entries.insert(m_currentBlock); - currentBlock().exit = SSACFG::BasicBlock::JumpTable{std::move(_debugData), _value, std::move(_cases), _defaultCase}; - m_currentBlock = {}; -} - FunctionDefinition const* SSACFGBuilder::findFunctionDefinition(Scope::Function const* _function) const { auto it = std::find_if( diff --git a/libyul/backends/evm/ssa/SSACFGBuilder.h b/libyul/backends/evm/ssa/SSACFGBuilder.h index 7b497db2ac..331d8b52f4 100644 --- a/libyul/backends/evm/ssa/SSACFGBuilder.h +++ b/libyul/backends/evm/ssa/SSACFGBuilder.h @@ -147,13 +147,6 @@ class SSACFGBuilder SSACFG::BlockId _target ); - void tableJump( - langutil::DebugData::ConstPtr _debugData, - SSACFG::ValueId _value, - std::map _cases, - SSACFG::BlockId _defaultCase - ); - FunctionDefinition const* findFunctionDefinition(Scope::Function const* _function) const; }; diff --git a/libyul/backends/evm/ssa/io/DotExporterBase.cpp b/libyul/backends/evm/ssa/io/DotExporterBase.cpp index 23541422a5..9a62dcc833 100644 --- a/libyul/backends/evm/ssa/io/DotExporterBase.cpp +++ b/libyul/backends/evm/ssa/io/DotExporterBase.cpp @@ -21,13 +21,9 @@ #include #include -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wtautological-compare" #include -#pragma GCC diagnostic pop #include -#include using namespace solidity; using namespace solidity::util; @@ -117,24 +113,6 @@ void DotExporterBase::writeBlock(std::ostream& _out, SSACFG::BlockId const _id) _out << formatEdge(_id, _conditionalJump.zero, "0"); _out << formatEdge(_id, _conditionalJump.nonZero, "1"); }, - [&](SSACFG::BasicBlock::JumpTable const& jt) - { - _out << fmt::format("{} -> {}Exit;\n", formatBlockHandle(_id), formatBlockHandle(_id)); - std::string options; - for (auto const& jumpCase: jt.cases) - { - if (!options.empty()) - options += " | "; - options += fmt::format("<{0}> {0}", formatNumber(jumpCase.first)); - } - if (!options.empty()) - options += " | "; - options += " default"; - _out << fmt::format("{}Exit [label=\"{{ JT | {{ {} }} }}\" shape=Mrecord];\n", formatBlockHandle(_id), options); - for (auto const& jumpCase: jt.cases) - _out << formatEdge(_id, jumpCase.second, formatNumber(jumpCase.first)); - _out << formatEdge(_id, jt.defaultCase, "default"); - }, [&](SSACFG::BasicBlock::FunctionReturn const& fr) { auto const valueToString = [&](SSACFG::ValueId const& valueId) { return valueId.str(m_cfg); }; diff --git a/libyul/backends/evm/ssa/io/JSONExporter.cpp b/libyul/backends/evm/ssa/io/JSONExporter.cpp index df4e4d8148..fdd400d94d 100644 --- a/libyul/backends/evm/ssa/io/JSONExporter.cpp +++ b/libyul/backends/evm/ssa/io/JSONExporter.cpp @@ -160,9 +160,6 @@ Json exportBlock(SSACFG const& _cfg, SSACFG::BlockId _entryId, LivenessAnalysis }, [&](SSACFG::BasicBlock::Terminated const&) { exitBlockJson["type"] = "Terminated"; - }, - [&](SSACFG::BasicBlock::JumpTable const&) { - yulAssert(false); } }, _cfg.block(_blockId).exit); blockJson["exit"] = exitBlockJson; From f04245962572b71ba88fb0310fce8e83e762bd50 Mon Sep 17 00:00:00 2001 From: r0qs <457348+r0qs@users.noreply.github.com> Date: Tue, 24 Feb 2026 18:36:46 +0100 Subject: [PATCH 207/243] Fix wrong indices in nested-array-element-storage-to-storage test --- ...ested_array_element_storage_to_storage.sol | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/test/libsolidity/semanticTests/array/copying/nested_array_element_storage_to_storage.sol b/test/libsolidity/semanticTests/array/copying/nested_array_element_storage_to_storage.sol index 1a3fb70e08..2e3352693a 100644 --- a/test/libsolidity/semanticTests/array/copying/nested_array_element_storage_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/nested_array_element_storage_to_storage.sol @@ -9,7 +9,7 @@ contract C { uint8[][] dst1; uint8[][] dst2; uint8[][2] dst3; - uint8[][] dst4; + uint8[2][] dst4; constructor() { src1[1] = new uint8[][](2); @@ -45,10 +45,10 @@ contract C { dst2 = src2[0]; require(dst2.length == 2); - require(dst2[0][0] == src2[1][0][0]); - require(dst2[0][1] == src2[1][0][1]); - require(dst2[1][0] == src2[1][1][0]); - require(dst2[1][1] == src2[1][1][1]); + require(dst2[0][0] == src2[0][0][0]); + require(dst2[0][1] == src2[0][0][1]); + require(dst2[1][0] == src2[0][1][0]); + require(dst2[1][1] == src2[0][1][1]); } function test3() public { @@ -61,24 +61,22 @@ contract C { function test4() public { dst4 = src4[1]; - require(dst4.length == 2); - require(dst4[0][0] == src4[0][0][0]); - require(dst4[0][1] == src4[0][0][1]); - require(dst4[1][0] == src4[0][1][0]); - require(dst4[1][1] == src4[0][1][1]); + require(dst4.length == 1); + require(dst4[0][0] == src4[1][0][0]); + require(dst4[0][1] == src4[1][0][1]); } } // ---- // test1() -> -// gas irOptimized: 150488 +// gas irOptimized: 150449 // gas legacy: 156275 -// gas legacyOptimized: 151018 -// test2() -> FAILURE -// gas irOptimized: 150389 -// gas legacy: 155998 -// gas legacyOptimized: 150684 +// gas legacyOptimized: 150983 +// test2() -> +// gas irOptimized: 147342 +// gas legacy: 153508 +// gas legacyOptimized: 148067 // test3() -> // gas irOptimized: 124300 // gas legacy: 130649 // gas legacyOptimized: 125142 -// test4() -> FAILURE +// test4() -> From 36b7ff50194ded41d6c104c7b27753886b4c53a7 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Tue, 24 Feb 2026 15:40:14 +0100 Subject: [PATCH 208/243] Use unordered maps for FunctionReferenceResolver --- libyul/FunctionReferenceResolver.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libyul/FunctionReferenceResolver.h b/libyul/FunctionReferenceResolver.h index dcc5f50e5b..1f07abe5dd 100644 --- a/libyul/FunctionReferenceResolver.h +++ b/libyul/FunctionReferenceResolver.h @@ -20,6 +20,8 @@ #include +#include + namespace solidity::yul { @@ -33,15 +35,15 @@ class FunctionReferenceResolver: private ASTWalker { public: explicit FunctionReferenceResolver(Block const& _ast); - std::map const& references() const { return m_functionReferences; } + std::unordered_map const& references() const { return m_functionReferences; } private: using ASTWalker::operator(); void operator()(FunctionCall const& _functionCall) override; void operator()(Block const& _block) override; - std::map m_functionReferences; - std::vector> m_scopes; + std::unordered_map m_functionReferences; + std::vector> m_scopes; }; From f3cc67d4662b75722556ffc83e5d462956a74afe Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Tue, 24 Feb 2026 15:42:02 +0100 Subject: [PATCH 209/243] Optimize control flow side effects collector eliminate the make_shared double allocation + atomic refcount per node use unordered map and set over ordered ones --- Changelog.md | 1 + libyul/ControlFlowSideEffectsCollector.cpp | 4 ++-- libyul/ControlFlowSideEffectsCollector.h | 24 +++++++++---------- .../backends/evm/ControlFlowGraphBuilder.cpp | 2 +- libyul/backends/evm/ControlFlowGraphBuilder.h | 6 +++-- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/Changelog.md b/Changelog.md index f6e4733f6e..ed752bf353 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,7 @@ Important Bugfixes: Compiler Features: +* Yul Optimizer: Improve performance of control flow side effects collector and function references resolver. * Yul Optimizer: Remove redundant prerequisite steps from the default optimizer sequence. diff --git a/libyul/ControlFlowSideEffectsCollector.cpp b/libyul/ControlFlowSideEffectsCollector.cpp index 28089b029f..3c42972852 100644 --- a/libyul/ControlFlowSideEffectsCollector.cpp +++ b/libyul/ControlFlowSideEffectsCollector.cpp @@ -162,8 +162,8 @@ void ControlFlowBuilder::newConnectedNode() ControlFlowNode* ControlFlowBuilder::newNode() { - m_nodes.emplace_back(std::make_shared()); - return m_nodes.back().get(); + m_nodes.emplace_back(); + return &m_nodes.back(); } diff --git a/libyul/ControlFlowSideEffectsCollector.h b/libyul/ControlFlowSideEffectsCollector.h index 2ef7a4bfa0..6228db2e49 100644 --- a/libyul/ControlFlowSideEffectsCollector.h +++ b/libyul/ControlFlowSideEffectsCollector.h @@ -21,10 +21,10 @@ #include #include -#include -#include -#include +#include #include +#include +#include namespace solidity::yul { @@ -56,7 +56,7 @@ class ControlFlowBuilder: private ASTWalker /// Computes the control-flows of all function defined in the block. /// Assumes the functions are hoisted to the topmost block. explicit ControlFlowBuilder(Block const& _ast); - std::map const& functionFlows() const { return m_functionFlows; } + std::unordered_map const& functionFlows() const { return m_functionFlows; } private: using ASTWalker::operator(); @@ -72,14 +72,14 @@ class ControlFlowBuilder: private ASTWalker void newConnectedNode(); ControlFlowNode* newNode(); - std::vector> m_nodes; + std::deque m_nodes; ControlFlowNode* m_currentNode = nullptr; ControlFlowNode const* m_leave = nullptr; ControlFlowNode const* m_break = nullptr; ControlFlowNode const* m_continue = nullptr; - std::map m_functionFlows; + std::unordered_map m_functionFlows; }; @@ -96,7 +96,7 @@ class ControlFlowSideEffectsCollector Block const& _ast ); - std::map const& functionSideEffects() const + std::unordered_map const& functionSideEffects() const { return m_functionSideEffects; } @@ -124,15 +124,15 @@ class ControlFlowSideEffectsCollector Dialect const& m_dialect; ControlFlowBuilder m_cfgBuilder; /// Function references, but only for calls to user-defined functions. - std::map m_functionReferences; + std::unordered_map m_functionReferences; /// Side effects of user-defined functions, is being constructod. - std::map m_functionSideEffects; + std::unordered_map m_functionSideEffects; /// Control flow nodes still to process, per function. - std::map> m_pendingNodes; + std::unordered_map> m_pendingNodes; /// Control flow nodes already processed, per function. - std::map> m_processedNodes; + std::unordered_map> m_processedNodes; /// Set of reachable function calls nodes in each function (including calls to builtins). - std::map> m_functionCalls; + std::unordered_map> m_functionCalls; }; diff --git a/libyul/backends/evm/ControlFlowGraphBuilder.cpp b/libyul/backends/evm/ControlFlowGraphBuilder.cpp index d9456e4c32..614503e183 100644 --- a/libyul/backends/evm/ControlFlowGraphBuilder.cpp +++ b/libyul/backends/evm/ControlFlowGraphBuilder.cpp @@ -258,7 +258,7 @@ std::unique_ptr ControlFlowGraphBuilder::build( ControlFlowGraphBuilder::ControlFlowGraphBuilder( CFG& _graph, AsmAnalysisInfo const& _analysisInfo, - std::map const& _functionSideEffects, + std::unordered_map const& _functionSideEffects, Dialect const& _dialect ): m_graph(_graph), diff --git a/libyul/backends/evm/ControlFlowGraphBuilder.h b/libyul/backends/evm/ControlFlowGraphBuilder.h index 4a2aae98c2..ba3735365d 100644 --- a/libyul/backends/evm/ControlFlowGraphBuilder.h +++ b/libyul/backends/evm/ControlFlowGraphBuilder.h @@ -23,6 +23,8 @@ #include #include +#include + namespace solidity::yul { @@ -56,7 +58,7 @@ class ControlFlowGraphBuilder ControlFlowGraphBuilder( CFG& _graph, AsmAnalysisInfo const& _analysisInfo, - std::map const& _functionSideEffects, + std::unordered_map const& _functionSideEffects, Dialect const& _dialect ); void registerFunction(FunctionDefinition const& _function); @@ -79,7 +81,7 @@ class ControlFlowGraphBuilder ); CFG& m_graph; AsmAnalysisInfo const& m_info; - std::map const& m_functionSideEffects; + std::unordered_map const& m_functionSideEffects; Dialect const& m_dialect; CFG::BasicBlock* m_currentBlock = nullptr; Scope* m_scope = nullptr; From f481d1c5837d25cb944e3a9c3dacd46f8cdd97f7 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Mon, 6 Oct 2025 14:14:05 +0200 Subject: [PATCH 210/243] Introduce experimental flag/option --- libsolidity/interface/CompilerStack.cpp | 6 ++ libsolidity/interface/CompilerStack.h | 4 ++ scripts/ASTImportTest.sh | 4 +- solc/CommandLineInterface.cpp | 1 + solc/CommandLineParser.cpp | 68 ++++++++++++++++++ solc/CommandLineParser.h | 3 + .../args | 2 +- .../asm_json_yul_export_evm_asm_import/args | 2 +- test/cmdlineTests/ast_ir/args | 2 +- .../ast_json_import_wrong_evmVersion/args | 2 +- .../eof_unavailable_before_osaka/args | 2 +- .../args | 1 + .../err | 1 + .../exit | 1 + .../input.sol | 6 ++ .../ethdebug_eof_container_osaka/args | 2 +- test/cmdlineTests/ethdebug_on_abstract/args | 2 +- test/cmdlineTests/ethdebug_on_interface/args | 2 +- .../args | 1 + .../err | 1 + .../exit | 1 + .../input.sol | 6 ++ .../experimental_flag_with_standard_json/args | 1 + .../experimental_flag_with_standard_json/err | 1 + .../experimental_flag_with_standard_json/exit | 1 + .../input.json | 0 .../import_asm_json_all_valid_flags/args | 2 +- .../args | 2 +- .../args | 2 +- .../args | 2 +- .../import_asm_json_invalid_data_not_hex/args | 2 +- .../args | 2 +- .../args | 2 +- .../import_asm_json_invalid_value/args | 2 +- .../args | 2 +- .../import_asm_json_no_optimize/args | 2 +- .../import_asm_json_no_value/args | 2 +- .../import_asm_json_non_unique_sources/args | 2 +- .../import_asm_json_optimize/args | 2 +- .../args | 2 +- .../args | 2 +- .../import_asm_json_unrecognized_field/args | 2 +- .../import_asm_json_untagged_jumpdest/args | 2 +- .../import_asm_json_verbatim/args | 2 +- .../import_asm_json_yul_more_subobjects/args | 2 +- .../import_asm_json_yul_subobjects/args | 2 +- .../args | 2 +- .../strict_asm_eof_container_osaka/args | 2 +- .../strict_asm_eof_dataloadn_osaka/args | 2 +- .../args | 2 +- .../strict_asm_yul_cfg_json_export/args | 2 +- .../undeployable_contract_empty_outputs/args | 2 +- test/cmdlineTests/yul_cfg_json_export/args | 2 +- test/lsp.py | 7 +- test/solc/CommandLineInterface.cpp | 67 +++++++++--------- test/solc/CommandLineParser.cpp | 70 +++++++++++++++++-- 56 files changed, 244 insertions(+), 77 deletions(-) create mode 100644 test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/args create mode 100644 test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/err create mode 100644 test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/exit create mode 100644 test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/input.sol create mode 100644 test/cmdlineTests/experimental_feature_without_experimental_flag/args create mode 100644 test/cmdlineTests/experimental_feature_without_experimental_flag/err create mode 100644 test/cmdlineTests/experimental_feature_without_experimental_flag/exit create mode 100644 test/cmdlineTests/experimental_feature_without_experimental_flag/input.sol create mode 100644 test/cmdlineTests/experimental_flag_with_standard_json/args create mode 100644 test/cmdlineTests/experimental_flag_with_standard_json/err create mode 100644 test/cmdlineTests/experimental_flag_with_standard_json/exit create mode 100644 test/cmdlineTests/experimental_flag_with_standard_json/input.json diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 975ed3c198..d9e703fc3f 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -229,6 +229,12 @@ void CompilerStack::setViaIR(bool _viaIR) m_viaIR = _viaIR; } +void CompilerStack::setExperimental(bool _experimental) +{ + solAssert(m_stackState < ParsedAndImported, "Must set experimental before parsing."); + m_experimental = _experimental; +} + void CompilerStack::setEVMVersion(langutil::EVMVersion _version) { solAssert(m_stackState < ParsedAndImported, "Must set EVM version before parsing."); diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 2731787a4b..eb765d3c66 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -223,6 +223,9 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac /// Must be set before parsing. void setViaIR(bool _viaIR); + /// Sets the experimental toggle to allow usage of experimental features. + void setExperimental(bool _experimental); + /// Set the EVM version used before running compile. /// When called without an argument it will revert to the default version. /// Must be set before parsing. @@ -607,6 +610,7 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac RevertStrings m_revertStrings = RevertStrings::Default; State m_stopAfter = State::CompilationSuccessful; bool m_viaIR = false; + bool m_experimental = false; langutil::EVMVersion m_evmVersion; std::optional m_eofVersion; ModelCheckerSettings m_modelCheckerSettings; diff --git a/scripts/ASTImportTest.sh b/scripts/ASTImportTest.sh index c7f504087c..370aab9bd6 100755 --- a/scripts/ASTImportTest.sh +++ b/scripts/ASTImportTest.sh @@ -117,7 +117,7 @@ function test_ast_import_export_equivalence local input_files=( "${@:2}" ) local export_command=("$SOLC" --combined-json ast --pretty-json --json-indent 4 "${input_files[@]}") - local import_command=("$SOLC" --import-ast --combined-json ast --pretty-json --json-indent 4 expected.json) + local import_command=("$SOLC" --experimental --import-ast --combined-json ast --pretty-json --json-indent 4 expected.json) local import_via_standard_json_command=("$SOLC" --combined-json ast --pretty-json --json-indent 4 --standard-json standard_json_input.json) # export ast - save ast json as expected result (silently) @@ -228,7 +228,7 @@ function test_evmjson_import_export_equivalence local bin_file_from_asm_import bin_file_from_asm_import="import/$(basename "${asm_json_file}" .json).bin" - local import_options=(--bin --import-asm-json "${asm_json_file}") + local import_options=(--experimental --bin --import-asm-json "${asm_json_file}") run_solc_store_stdout "${bin_file_from_asm_import}" "${import_options[@]}" stripCLIDecorations < "$bin_file_from_asm_import" > tmpfile diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 4a7f34561f..90c67868d5 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -927,6 +927,7 @@ void CommandLineInterface::compile() try { + m_compiler->setExperimental(m_options.experimental); if (m_options.metadata.literalSources) m_compiler->useMetadataLiteralSources(true); m_compiler->setMetadataFormat(m_options.metadata.format); diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 534eb57d10..6720b9fe8d 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -50,6 +50,7 @@ static std::string const g_strEVMVersion = "evm-version"; static std::string const g_strEOFVersion = "experimental-eof-version"; static std::string const g_strViaIR = "via-ir"; static std::string const g_strExperimentalViaIR = "experimental-via-ir"; +static std::string const g_strExperimental = "experimental"; static std::string const g_strGas = "gas"; static std::string const g_strHelp = "help"; static std::string const g_strImportAst = "import-ast"; @@ -157,6 +158,32 @@ void CommandLineParser::checkMutuallyExclusive(std::vector const& _ } } +void CommandLineParser::checkExperimental(std::vector const& _optionNames) const +{ + if (!m_args.contains(g_strExperimental) && countEnabledOptions(_optionNames) > 0) + { + solThrow( + CommandLineValidationError, + "The following options are only available in experimental mode: " + joinOptionNames(_optionNames) + ". " + + "To enable experimental mode, use the --experimental flag." + ); + } + + if (m_args.count(g_strEVMVersion)) + { + std::optional versionOption = EVMVersion::fromString(m_args[g_strEVMVersion].as()); + if (versionOption && versionOption->isExperimental()) + // TODO: Cover with test when the Amsterdam version is introduced + solThrow( + CommandLineValidationError, + fmt::format( + "EVM version '{}' is experimental and can only be selected in experimental mode." + "To enable experimental mode, use the --experimental flag", versionOption->name() + ) + ); + } +} + std::ostream& operator<<(std::ostream& _out, CompilerOutputs const& _selection) { std::vector serializedSelection; @@ -879,6 +906,17 @@ General Information)").c_str(), ; desc.add(smtCheckerOptions); + po::options_description experimentalOptions("Experimental options"); + experimentalOptions.add_options() + ( + g_strExperimental.c_str(), + "Enable experimental mode. Note that enabling experimental mode by itself will not " + "enable any experimental features, but will simply allow for such features to be enabled in the " + "usual manner, whether by using specific flags or pragmas." + ) + ; + desc.add(experimentalOptions); + desc.add_options()(g_strInputFile.c_str(), po::value>(), "input file"); return desc; } @@ -922,6 +960,9 @@ void CommandLineParser::processArgs() else if (m_args.count(g_strColor) > 0) m_options.formatting.coloredOutput = true; + if (m_args.contains(g_strExperimental)) + m_options.experimental = true; + checkMutuallyExclusive({ g_strHelp, g_strLicense, @@ -935,6 +976,18 @@ void CommandLineParser::processArgs() g_strImportEvmAssemblerJson, }); + checkExperimental({ + g_strLSP, + g_strImportAst, + g_strImportEvmAssemblerJson, + "ir-ast-json", + "ir-optimized-ast-json", + "yul-cfg-json", + "ethdebug", + "ethdebug-runtime", + g_strEOFVersion, + }); + if (m_args.count(g_strHelp) > 0) m_options.input.mode = InputMode::Help; else if (m_args.count(g_strLicense) > 0) @@ -963,6 +1016,14 @@ void CommandLineParser::processArgs() ) return; + if (m_options.experimental && m_options.input.mode == InputMode::StandardJson) + solThrow( + CommandLineValidationError, + "Standard JSON input mode is incompatible with the --" + g_strExperimental + " flag. " + "Instead, please use the 'settings.experimental' setting in your Standard JSON input file to " + "enable experimental mode." + ); + if (m_args.count(g_strYul) > 0) solThrow( CommandLineValidationError, @@ -1035,6 +1096,7 @@ void CommandLineParser::processArgs() g_strPrettyJson, "srcmap", "srcmap-runtime", + g_strExperimental, }; for (auto const& [optionName, optionValue]: m_args) @@ -1102,6 +1164,12 @@ void CommandLineParser::processArgs() if (!m_options.output.debugInfoSelection.has_value()) solThrow(CommandLineValidationError, "Invalid value for --" + g_strDebugInfo + " option: " + optionValue); + if (!m_options.experimental && m_options.output.debugInfoSelection->ethdebug) + solThrow( + CommandLineValidationError, + "--" + g_strDebugInfo + " ethdebug can only be used by toggling the --" + g_strExperimental + " flag." + ); + if (m_options.output.debugInfoSelection->snippet && !m_options.output.debugInfoSelection->location) solThrow(CommandLineValidationError, "To use 'snippet' with --" + g_strDebugInfo + " you must select also 'location'."); } diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index 46743232b4..6c32b5ee07 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -272,6 +272,8 @@ struct CommandLineOptions bool initialize = false; ModelCheckerSettings settings; } modelChecker; + + bool experimental = false; }; /// Parses the command-line arguments and produces a filled-out CommandLineOptions structure. @@ -323,6 +325,7 @@ class CommandLineParser void parseOutputSelection(); void checkMutuallyExclusive(std::vector const& _optionNames); + void checkExperimental(std::vector const& _optionNames) const; size_t countEnabledOptions(std::vector const& _optionNames) const; static std::string joinOptionNames(std::vector const& _optionNames, std::string _separator = ", "); diff --git a/test/cmdlineTests/asm_json_import_sourcelist_with_null_elements/args b/test/cmdlineTests/asm_json_import_sourcelist_with_null_elements/args index c1bc80dabf..5634b014dd 100644 --- a/test/cmdlineTests/asm_json_import_sourcelist_with_null_elements/args +++ b/test/cmdlineTests/asm_json_import_sourcelist_with_null_elements/args @@ -1 +1 @@ ---pretty-json --json-indent 4 --import-asm-json - +--pretty-json --json-indent 4 --experimental --import-asm-json - diff --git a/test/cmdlineTests/asm_json_yul_export_evm_asm_import/args b/test/cmdlineTests/asm_json_yul_export_evm_asm_import/args index 15c9628316..7000d519f7 100644 --- a/test/cmdlineTests/asm_json_yul_export_evm_asm_import/args +++ b/test/cmdlineTests/asm_json_yul_export_evm_asm_import/args @@ -1 +1 @@ ---import-asm-json - --opcodes --asm --bin --asm-json --pretty-json --json-indent 4 +--experimental --import-asm-json - --opcodes --asm --bin --asm-json --pretty-json --json-indent 4 diff --git a/test/cmdlineTests/ast_ir/args b/test/cmdlineTests/ast_ir/args index d254bff83d..ab35cc76b3 100644 --- a/test/cmdlineTests/ast_ir/args +++ b/test/cmdlineTests/ast_ir/args @@ -1 +1 @@ ---ir-ast-json --ir-optimized-ast-json --optimize --pretty-json --json-indent 4 +--experimental --ir-ast-json --ir-optimized-ast-json --optimize --pretty-json --json-indent 4 diff --git a/test/cmdlineTests/ast_json_import_wrong_evmVersion/args b/test/cmdlineTests/ast_json_import_wrong_evmVersion/args index d8fddfec65..d6692d825c 100644 --- a/test/cmdlineTests/ast_json_import_wrong_evmVersion/args +++ b/test/cmdlineTests/ast_json_import_wrong_evmVersion/args @@ -1 +1 @@ ---evm-version=homestead --import-ast --combined-json ast --pretty-json +--experimental --evm-version=homestead --import-ast --combined-json ast --pretty-json diff --git a/test/cmdlineTests/eof_unavailable_before_osaka/args b/test/cmdlineTests/eof_unavailable_before_osaka/args index 90e7e00e25..7e9b7ee936 100644 --- a/test/cmdlineTests/eof_unavailable_before_osaka/args +++ b/test/cmdlineTests/eof_unavailable_before_osaka/args @@ -1 +1 @@ ---optimize --experimental-eof-version 1 --evm-version cancun +--experimental --optimize --experimental-eof-version 1 --evm-version cancun diff --git a/test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/args b/test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/args new file mode 100644 index 0000000000..7b4e521b02 --- /dev/null +++ b/test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/args @@ -0,0 +1 @@ +--debug-info ethdebug diff --git a/test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/err b/test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/err new file mode 100644 index 0000000000..d8327f90bf --- /dev/null +++ b/test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/err @@ -0,0 +1 @@ +Error: --debug-info ethdebug can only be used by toggling the --experimental flag. diff --git a/test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/exit b/test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/input.sol b/test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/input.sol new file mode 100644 index 0000000000..415509ef9a --- /dev/null +++ b/test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/input.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 +pragma solidity >=0.0; + +contract C { + function f() public {} +} diff --git a/test/cmdlineTests/ethdebug_eof_container_osaka/args b/test/cmdlineTests/ethdebug_eof_container_osaka/args index 16466b842d..49c5bc8382 100644 --- a/test/cmdlineTests/ethdebug_eof_container_osaka/args +++ b/test/cmdlineTests/ethdebug_eof_container_osaka/args @@ -1 +1 @@ - --experimental-eof-version 1 --evm-version osaka --ethdebug --via-ir --pretty-json --json-indent 4 +--experimental --experimental-eof-version 1 --evm-version osaka --ethdebug --via-ir --pretty-json --json-indent 4 diff --git a/test/cmdlineTests/ethdebug_on_abstract/args b/test/cmdlineTests/ethdebug_on_abstract/args index c6018b329a..c345d483a3 100644 --- a/test/cmdlineTests/ethdebug_on_abstract/args +++ b/test/cmdlineTests/ethdebug_on_abstract/args @@ -1 +1 @@ ---ethdebug --via-ir --pretty-json --json-indent 4 +--experimental --ethdebug --via-ir --pretty-json --json-indent 4 diff --git a/test/cmdlineTests/ethdebug_on_interface/args b/test/cmdlineTests/ethdebug_on_interface/args index c6018b329a..c345d483a3 100644 --- a/test/cmdlineTests/ethdebug_on_interface/args +++ b/test/cmdlineTests/ethdebug_on_interface/args @@ -1 +1 @@ ---ethdebug --via-ir --pretty-json --json-indent 4 +--experimental --ethdebug --via-ir --pretty-json --json-indent 4 diff --git a/test/cmdlineTests/experimental_feature_without_experimental_flag/args b/test/cmdlineTests/experimental_feature_without_experimental_flag/args new file mode 100644 index 0000000000..a5ec4617b5 --- /dev/null +++ b/test/cmdlineTests/experimental_feature_without_experimental_flag/args @@ -0,0 +1 @@ +--ir-ast-json diff --git a/test/cmdlineTests/experimental_feature_without_experimental_flag/err b/test/cmdlineTests/experimental_feature_without_experimental_flag/err new file mode 100644 index 0000000000..1b73066ce6 --- /dev/null +++ b/test/cmdlineTests/experimental_feature_without_experimental_flag/err @@ -0,0 +1 @@ +Error: The following options are only available in experimental mode: --lsp, --import-ast, --import-asm-json, --ir-ast-json, --ir-optimized-ast-json, --yul-cfg-json, --ethdebug, --ethdebug-runtime, --experimental-eof-version. To enable experimental mode, use the --experimental flag. diff --git a/test/cmdlineTests/experimental_feature_without_experimental_flag/exit b/test/cmdlineTests/experimental_feature_without_experimental_flag/exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/test/cmdlineTests/experimental_feature_without_experimental_flag/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/experimental_feature_without_experimental_flag/input.sol b/test/cmdlineTests/experimental_feature_without_experimental_flag/input.sol new file mode 100644 index 0000000000..0bea95d1ef --- /dev/null +++ b/test/cmdlineTests/experimental_feature_without_experimental_flag/input.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 +pragma solidity >=0.0; + +contract C { + function f() public {} +} diff --git a/test/cmdlineTests/experimental_flag_with_standard_json/args b/test/cmdlineTests/experimental_flag_with_standard_json/args new file mode 100644 index 0000000000..1c31d6967e --- /dev/null +++ b/test/cmdlineTests/experimental_flag_with_standard_json/args @@ -0,0 +1 @@ +--experimental diff --git a/test/cmdlineTests/experimental_flag_with_standard_json/err b/test/cmdlineTests/experimental_flag_with_standard_json/err new file mode 100644 index 0000000000..81d8411e5a --- /dev/null +++ b/test/cmdlineTests/experimental_flag_with_standard_json/err @@ -0,0 +1 @@ +Error: Standard JSON input mode is incompatible with the --experimental flag. Instead, please use the 'settings.experimental' setting in your Standard JSON input file to enable experimental mode. diff --git a/test/cmdlineTests/experimental_flag_with_standard_json/exit b/test/cmdlineTests/experimental_flag_with_standard_json/exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/test/cmdlineTests/experimental_flag_with_standard_json/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/experimental_flag_with_standard_json/input.json b/test/cmdlineTests/experimental_flag_with_standard_json/input.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/cmdlineTests/import_asm_json_all_valid_flags/args b/test/cmdlineTests/import_asm_json_all_valid_flags/args index e6010eac3b..a1e89d7ae4 100644 --- a/test/cmdlineTests/import_asm_json_all_valid_flags/args +++ b/test/cmdlineTests/import_asm_json_all_valid_flags/args @@ -1 +1 @@ ---pretty-json --json-indent 4 --combined-json bin,bin-runtime,opcodes,asm,srcmap,srcmap-runtime --asm --bin --bin-runtime --asm-json --import-asm-json - +--experimental --pretty-json --json-indent 4 --combined-json bin,bin-runtime,opcodes,asm,srcmap,srcmap-runtime --asm --bin --bin-runtime --asm-json --import-asm-json - diff --git a/test/cmdlineTests/import_asm_json_difficulty_prevrandao/args b/test/cmdlineTests/import_asm_json_difficulty_prevrandao/args index 2b4b3a1534..a31c9320b0 100644 --- a/test/cmdlineTests/import_asm_json_difficulty_prevrandao/args +++ b/test/cmdlineTests/import_asm_json_difficulty_prevrandao/args @@ -1 +1 @@ ---import-asm-json - --opcodes --asm +--experimental --import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/import_asm_json_eof_unavailable_before_osaka/args b/test/cmdlineTests/import_asm_json_eof_unavailable_before_osaka/args index b5d6267fa5..7293bf52ab 100644 --- a/test/cmdlineTests/import_asm_json_eof_unavailable_before_osaka/args +++ b/test/cmdlineTests/import_asm_json_eof_unavailable_before_osaka/args @@ -1 +1 @@ ---import-asm-json --experimental-eof-version 1 --evm-version cancun +--experimental --import-asm-json --experimental-eof-version 1 --evm-version cancun diff --git a/test/cmdlineTests/import_asm_json_hex_subassembly_indices/args b/test/cmdlineTests/import_asm_json_hex_subassembly_indices/args index 2b4b3a1534..a31c9320b0 100644 --- a/test/cmdlineTests/import_asm_json_hex_subassembly_indices/args +++ b/test/cmdlineTests/import_asm_json_hex_subassembly_indices/args @@ -1 +1 @@ ---import-asm-json - --opcodes --asm +--experimental --import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/import_asm_json_invalid_data_not_hex/args b/test/cmdlineTests/import_asm_json_invalid_data_not_hex/args index 2b4b3a1534..a31c9320b0 100644 --- a/test/cmdlineTests/import_asm_json_invalid_data_not_hex/args +++ b/test/cmdlineTests/import_asm_json_invalid_data_not_hex/args @@ -1 +1 @@ ---import-asm-json - --opcodes --asm +--experimental --import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/import_asm_json_invalid_data_not_object/args b/test/cmdlineTests/import_asm_json_invalid_data_not_object/args index 2b4b3a1534..a31c9320b0 100644 --- a/test/cmdlineTests/import_asm_json_invalid_data_not_object/args +++ b/test/cmdlineTests/import_asm_json_invalid_data_not_object/args @@ -1 +1 @@ ---import-asm-json - --opcodes --asm +--experimental --import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/import_asm_json_invalid_jumptype_instruction/args b/test/cmdlineTests/import_asm_json_invalid_jumptype_instruction/args index 2b4b3a1534..a31c9320b0 100644 --- a/test/cmdlineTests/import_asm_json_invalid_jumptype_instruction/args +++ b/test/cmdlineTests/import_asm_json_invalid_jumptype_instruction/args @@ -1 +1 @@ ---import-asm-json - --opcodes --asm +--experimental --import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/import_asm_json_invalid_value/args b/test/cmdlineTests/import_asm_json_invalid_value/args index 2b4b3a1534..a31c9320b0 100644 --- a/test/cmdlineTests/import_asm_json_invalid_value/args +++ b/test/cmdlineTests/import_asm_json_invalid_value/args @@ -1 +1 @@ ---import-asm-json - --opcodes --asm +--experimental --import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/import_asm_json_missing_subobject_indices/args b/test/cmdlineTests/import_asm_json_missing_subobject_indices/args index 2b4b3a1534..a31c9320b0 100644 --- a/test/cmdlineTests/import_asm_json_missing_subobject_indices/args +++ b/test/cmdlineTests/import_asm_json_missing_subobject_indices/args @@ -1 +1 @@ ---import-asm-json - --opcodes --asm +--experimental --import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/import_asm_json_no_optimize/args b/test/cmdlineTests/import_asm_json_no_optimize/args index 0af885908f..9dcc671fb0 100644 --- a/test/cmdlineTests/import_asm_json_no_optimize/args +++ b/test/cmdlineTests/import_asm_json_no_optimize/args @@ -1 +1 @@ ---import-asm-json - --asm +--experimental --import-asm-json - --asm diff --git a/test/cmdlineTests/import_asm_json_no_value/args b/test/cmdlineTests/import_asm_json_no_value/args index 2b4b3a1534..a31c9320b0 100644 --- a/test/cmdlineTests/import_asm_json_no_value/args +++ b/test/cmdlineTests/import_asm_json_no_value/args @@ -1 +1 @@ ---import-asm-json - --opcodes --asm +--experimental --import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/import_asm_json_non_unique_sources/args b/test/cmdlineTests/import_asm_json_non_unique_sources/args index d27cc7bb21..772c307ada 100644 --- a/test/cmdlineTests/import_asm_json_non_unique_sources/args +++ b/test/cmdlineTests/import_asm_json_non_unique_sources/args @@ -1 +1 @@ ---asm-json --import-asm-json - +--experimental --asm-json --import-asm-json - diff --git a/test/cmdlineTests/import_asm_json_optimize/args b/test/cmdlineTests/import_asm_json_optimize/args index b9370e6e4f..1a72742610 100644 --- a/test/cmdlineTests/import_asm_json_optimize/args +++ b/test/cmdlineTests/import_asm_json_optimize/args @@ -1 +1 @@ ---optimize --import-asm-json - --asm +--experimental --optimize --import-asm-json - --asm diff --git a/test/cmdlineTests/import_asm_json_out_of_range_data_index/args b/test/cmdlineTests/import_asm_json_out_of_range_data_index/args index 2b4b3a1534..a31c9320b0 100644 --- a/test/cmdlineTests/import_asm_json_out_of_range_data_index/args +++ b/test/cmdlineTests/import_asm_json_out_of_range_data_index/args @@ -1 +1 @@ ---import-asm-json - --opcodes --asm +--experimental --import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/import_asm_json_random_order_data_index/args b/test/cmdlineTests/import_asm_json_random_order_data_index/args index 2b4b3a1534..a31c9320b0 100644 --- a/test/cmdlineTests/import_asm_json_random_order_data_index/args +++ b/test/cmdlineTests/import_asm_json_random_order_data_index/args @@ -1 +1 @@ ---import-asm-json - --opcodes --asm +--experimental --import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/import_asm_json_unrecognized_field/args b/test/cmdlineTests/import_asm_json_unrecognized_field/args index 2b4b3a1534..a31c9320b0 100644 --- a/test/cmdlineTests/import_asm_json_unrecognized_field/args +++ b/test/cmdlineTests/import_asm_json_unrecognized_field/args @@ -1 +1 @@ ---import-asm-json - --opcodes --asm +--experimental --import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/import_asm_json_untagged_jumpdest/args b/test/cmdlineTests/import_asm_json_untagged_jumpdest/args index 2b4b3a1534..a31c9320b0 100644 --- a/test/cmdlineTests/import_asm_json_untagged_jumpdest/args +++ b/test/cmdlineTests/import_asm_json_untagged_jumpdest/args @@ -1 +1 @@ ---import-asm-json - --opcodes --asm +--experimental --import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/import_asm_json_verbatim/args b/test/cmdlineTests/import_asm_json_verbatim/args index 2b4b3a1534..a31c9320b0 100644 --- a/test/cmdlineTests/import_asm_json_verbatim/args +++ b/test/cmdlineTests/import_asm_json_verbatim/args @@ -1 +1 @@ ---import-asm-json - --opcodes --asm +--experimental --import-asm-json - --opcodes --asm diff --git a/test/cmdlineTests/import_asm_json_yul_more_subobjects/args b/test/cmdlineTests/import_asm_json_yul_more_subobjects/args index 58dfc8cc73..7de136d4bd 100644 --- a/test/cmdlineTests/import_asm_json_yul_more_subobjects/args +++ b/test/cmdlineTests/import_asm_json_yul_more_subobjects/args @@ -1 +1 @@ ---import-asm-json - --opcodes +--experimental --import-asm-json - --opcodes diff --git a/test/cmdlineTests/import_asm_json_yul_subobjects/args b/test/cmdlineTests/import_asm_json_yul_subobjects/args index 58dfc8cc73..7de136d4bd 100644 --- a/test/cmdlineTests/import_asm_json_yul_subobjects/args +++ b/test/cmdlineTests/import_asm_json_yul_subobjects/args @@ -1 +1 @@ ---import-asm-json - --opcodes +--experimental --import-asm-json - --opcodes diff --git a/test/cmdlineTests/standard_cli_import_ast_storage_layout_specifier_missing_expression/args b/test/cmdlineTests/standard_cli_import_ast_storage_layout_specifier_missing_expression/args index f0b85640bd..811190e2ad 100644 --- a/test/cmdlineTests/standard_cli_import_ast_storage_layout_specifier_missing_expression/args +++ b/test/cmdlineTests/standard_cli_import_ast_storage_layout_specifier_missing_expression/args @@ -1 +1 @@ ---import-ast - \ No newline at end of file +--experimental --import-ast - diff --git a/test/cmdlineTests/strict_asm_eof_container_osaka/args b/test/cmdlineTests/strict_asm_eof_container_osaka/args index 60abde3563..1a99e4be68 100644 --- a/test/cmdlineTests/strict_asm_eof_container_osaka/args +++ b/test/cmdlineTests/strict_asm_eof_container_osaka/args @@ -1 +1 @@ - --strict-assembly --experimental-eof-version 1 --evm-version osaka --asm --ir-optimized --bin --debug-info none +--experimental --strict-assembly --experimental-eof-version 1 --evm-version osaka --asm --ir-optimized --bin --debug-info none diff --git a/test/cmdlineTests/strict_asm_eof_dataloadn_osaka/args b/test/cmdlineTests/strict_asm_eof_dataloadn_osaka/args index 60abde3563..1a99e4be68 100644 --- a/test/cmdlineTests/strict_asm_eof_dataloadn_osaka/args +++ b/test/cmdlineTests/strict_asm_eof_dataloadn_osaka/args @@ -1 +1 @@ - --strict-assembly --experimental-eof-version 1 --evm-version osaka --asm --ir-optimized --bin --debug-info none +--experimental --strict-assembly --experimental-eof-version 1 --evm-version osaka --asm --ir-optimized --bin --debug-info none diff --git a/test/cmdlineTests/strict_asm_eof_unavailable_before_osaka/args b/test/cmdlineTests/strict_asm_eof_unavailable_before_osaka/args index 7ec243fdeb..c7b8a5a6ac 100644 --- a/test/cmdlineTests/strict_asm_eof_unavailable_before_osaka/args +++ b/test/cmdlineTests/strict_asm_eof_unavailable_before_osaka/args @@ -1 +1 @@ ---strict-assembly --experimental-eof-version 1 --evm-version cancun +--experimental --strict-assembly --experimental-eof-version 1 --evm-version cancun diff --git a/test/cmdlineTests/strict_asm_yul_cfg_json_export/args b/test/cmdlineTests/strict_asm_yul_cfg_json_export/args index 1147466b54..ecd27ca32a 100644 --- a/test/cmdlineTests/strict_asm_yul_cfg_json_export/args +++ b/test/cmdlineTests/strict_asm_yul_cfg_json_export/args @@ -1 +1 @@ ---strict-assembly --optimize --yul-cfg-json --pretty-json --json-indent 4 +--experimental --strict-assembly --optimize --yul-cfg-json --pretty-json --json-indent 4 diff --git a/test/cmdlineTests/undeployable_contract_empty_outputs/args b/test/cmdlineTests/undeployable_contract_empty_outputs/args index 74c8e867fc..17068d35dd 100644 --- a/test/cmdlineTests/undeployable_contract_empty_outputs/args +++ b/test/cmdlineTests/undeployable_contract_empty_outputs/args @@ -1 +1 @@ ---bin --bin-runtime --opcodes --asm --ir --ir-ast-json --ir-optimized --ir-optimized-ast-json --gas --asm-json --abi --hashes --optimize --pretty-json --json-indent 4 +--experimental --bin --bin-runtime --opcodes --asm --ir --ir-ast-json --ir-optimized --ir-optimized-ast-json --gas --asm-json --abi --hashes --optimize --pretty-json --json-indent 4 diff --git a/test/cmdlineTests/yul_cfg_json_export/args b/test/cmdlineTests/yul_cfg_json_export/args index 4af41df671..b03ca0ef51 100644 --- a/test/cmdlineTests/yul_cfg_json_export/args +++ b/test/cmdlineTests/yul_cfg_json_export/args @@ -1 +1 @@ ---via-ir --optimize --yul-cfg-json --pretty-json --json-indent 4 +--experimental --via-ir --optimize --yul-cfg-json --pretty-json --json-indent 4 diff --git a/test/lsp.py b/test/lsp.py index 11007ae822..266a29fcc8 100755 --- a/test/lsp.py +++ b/test/lsp.py @@ -882,7 +882,12 @@ def main(self) -> int: title: str = test_fn.__name__[5:] print(f"{SGR_TEST_BEGIN}Testing {title} ...{SGR_RESET}") try: - with JsonRpcProcess(self.solc_path, ["--lsp"], trace_io=self.trace_io, print_pid=self.print_solc_pid) as solc: + with JsonRpcProcess( + self.solc_path, + ["--experimental", "--lsp"], + trace_io=self.trace_io, + print_pid=self.print_solc_pid + ) as solc: test_fn(solc) self.test_counter.passed += 1 except ExpectationFailed: diff --git a/test/solc/CommandLineInterface.cpp b/test/solc/CommandLineInterface.cpp index adfd24b02b..d1c43be5bd 100644 --- a/test/solc/CommandLineInterface.cpp +++ b/test/solc/CommandLineInterface.cpp @@ -1534,61 +1534,62 @@ BOOST_AUTO_TEST_CASE(cli_ethdebug_debug_info_ethdebug) createFilesWithParentDirs({tempDir.path() / "input.yul"}, "{}"); static std::vector> erroneousCLIFlagCombinations{ { - {"solc", "--debug-info", "ethdebug", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--debug-info", "ethdebug", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--debug-info", "ethdebug", "--ir-optimized", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--debug-info", "ethdebug", "--ir-optimized", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--debug-info", "ethdebug", "--optimize", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--debug-info", "ethdebug", "--optimize", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--debug-info", "ethdebug", "--ethdebug", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--debug-info", "ethdebug", "--ethdebug", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--ethdebug-runtime", "--strict-assembly", tempDir.path().string() + "/input.yul"}, + {"solc", "--experimental", "--ethdebug-runtime", "--strict-assembly", tempDir.path().string() + "/input.yul"}, }, { - {"solc", "--ethdebug", "--ethdebug-runtime", "--strict-assembly", tempDir.path().string() + "/input.yul"}, + {"solc", "--experimental", "--ethdebug", "--ethdebug-runtime", "--strict-assembly", tempDir.path().string() + "/input.yul"}, }, { - {"solc", "--debug-info", "ethdebug", "--ethdebug", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--debug-info", "ethdebug", "--ethdebug", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--debug-info", "ethdebug", "--ethdebug-runtime", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--debug-info", "ethdebug", "--ethdebug-runtime", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--debug-info", "ethdebug", "--ethdebug", "--ethdebug-runtime", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--debug-info", "ethdebug", "--ethdebug", "--ethdebug-runtime", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--debug-info", "location", "--ethdebug", "--via-ir", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--debug-info", "location", "--ethdebug", "--via-ir", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--debug-info", "location", "--ethdebug-runtime", "--via-ir", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--debug-info", "location", "--ethdebug-runtime", "--via-ir", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--debug-info", "location", "--ethdebug", "--ethdebug-runtime", "--via-ir", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--debug-info", "location", "--ethdebug", "--ethdebug-runtime", "--via-ir", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--debug-info", "all", "--ethdebug", "--via-ir", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--debug-info", "all", "--ethdebug", "--via-ir", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--debug-info", "all", "--ethdebug-runtime", "--via-ir", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--debug-info", "all", "--ethdebug-runtime", "--via-ir", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--debug-info", "all", "--ethdebug", "--ethdebug-runtime", "--via-ir", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--debug-info", "all", "--ethdebug", "--ethdebug-runtime", "--via-ir", tempDir.path().string() + "/input.sol"}, }, }; static std::vector> supportedCLIFlagCombinations{ { - {"solc", "--debug-info", "ethdebug", "--ir", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--debug-info", "ethdebug", "--ir", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--debug-info", "ethdebug", "--ethdebug", "--via-ir", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--debug-info", "ethdebug", "--ethdebug", "--via-ir", tempDir.path().string() + "/input.sol"}, }, { { "solc", + "--experimental", "--debug-info", "ethdebug", "--ethdebug-runtime", @@ -1597,14 +1598,15 @@ BOOST_AUTO_TEST_CASE(cli_ethdebug_debug_info_ethdebug) }, }, { - {"solc", "--debug-info", "ethdebug", "--strict-assembly", tempDir.path().string() + "/input.yul"}, + {"solc", "--experimental", "--debug-info", "ethdebug", "--strict-assembly", tempDir.path().string() + "/input.yul"}, }, { - {"solc", "--ethdebug", "--strict-assembly", tempDir.path().string() + "/input.yul"}, + {"solc", "--experimental", "--ethdebug", "--strict-assembly", tempDir.path().string() + "/input.yul"}, }, { { "solc", + "--experimental", "--debug-info", "ethdebug", "--ethdebug", @@ -1633,11 +1635,12 @@ BOOST_AUTO_TEST_CASE(cli_ethdebug_ethdebug_output) createFilesWithParentDirs({tempDir.path() / "input.sol"}, "pragma solidity >=0.0; contract C { function f() public pure {} }"); static std::vector> erroneousCLIFlagCombinations{ { - {"solc", "--ethdebug", "--ethdebug-runtime", "--via-ir", "--ir-optimized", "--optimize", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--ethdebug", "--ethdebug-runtime", "--via-ir", "--ir-optimized", "--optimize", tempDir.path().string() + "/input.sol"}, }, { { "solc", + "--experimental", "--ethdebug-runtime", "--via-ir", "--ir-optimized", @@ -1646,42 +1649,42 @@ BOOST_AUTO_TEST_CASE(cli_ethdebug_ethdebug_output) }, }, { - {"solc", "--ethdebug", "--via-ir", "--ir-optimized", "--optimize", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--ethdebug", "--via-ir", "--ir-optimized", "--optimize", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--ethdebug", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--ethdebug", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--ethdebug", "--ethdebug-runtime", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--ethdebug", "--ethdebug-runtime", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--ethdebug-runtime", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--ethdebug-runtime", tempDir.path().string() + "/input.sol"}, }, }; static std::vector> supportedCLIFlagCombinations{ { - {"solc", "--ethdebug", "--via-ir", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--ethdebug", "--via-ir", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--ethdebug-runtime", "--via-ir", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--ethdebug-runtime", "--via-ir", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--ethdebug", "--ethdebug-runtime", "--via-ir", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--ethdebug", "--ethdebug-runtime", "--via-ir", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--ethdebug", "--via-ir", "--ir", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--ethdebug", "--via-ir", "--ir", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--ethdebug-runtime", "--via-ir", "--ir", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--ethdebug-runtime", "--via-ir", "--ir", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--ethdebug", "--ethdebug-runtime", "--via-ir", "--ir", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--ethdebug", "--ethdebug-runtime", "--via-ir", "--ir", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--ethdebug", "--via-ir", "--ir-optimized", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--ethdebug", "--via-ir", "--ir-optimized", tempDir.path().string() + "/input.sol"}, }, { - {"solc", "--ethdebug-runtime", "--via-ir", "--ir-optimized", tempDir.path().string() + "/input.sol"}, + {"solc", "--experimental", "--ethdebug-runtime", "--via-ir", "--ir-optimized", tempDir.path().string() + "/input.sol"}, }, }; diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index 85eeaebe31..705ae3f73c 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -110,6 +110,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) "/tmp=/usr/lib/", "a:b=c/d", ":contract.sol=", + "--experimental", "--base-path=/home/user/", "--include-path=/usr/lib/include/", "--include-path=/home/user/include", @@ -163,6 +164,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) }; CommandLineOptions expectedOptions; + expectedOptions.experimental = true; expectedOptions.input.mode = inputMode; expectedOptions.input.paths = {"contract.sol", "/tmp/projects/token.sol", "/home/user/lib/dex.sol", "file", "input.json"}; expectedOptions.input.remappings = { @@ -249,7 +251,7 @@ BOOST_AUTO_TEST_CASE(no_import_callback) {"solc", "--standard-json", "--no-import-callback", "input.json"}, {"solc", "--assemble", "--no-import-callback", "input.yul"}, {"solc", "--strict-assembly", "--no-import-callback", "input.yul"}, - {"solc", "--import-ast", "--no-import-callback", "ast.json"}, + {"solc", "--experimental", "--import-ast", "--no-import-callback", "ast.json"}, {"solc", "--link", "--no-import-callback", "input.bin"}, }; @@ -482,6 +484,8 @@ BOOST_AUTO_TEST_CASE(optimizer_flags) for (auto const& [optimizerFlags, expectedOptimizerSettings]: settingsMap) { std::vector commandLine = {"solc", inputModeFlag, "file"}; + if (inputMode == InputMode::CompilerWithASTImport) + commandLine.emplace_back("--experimental"); commandLine += optimizerFlags; BOOST_CHECK(parseCommandLine(commandLine).optimiserSettings() == expectedOptimizerSettings); } @@ -630,34 +634,34 @@ BOOST_AUTO_TEST_CASE(invalid_optimizer_sequence_without_optimize) BOOST_AUTO_TEST_CASE(ethdebug) { - CommandLineOptions commandLineOptions = parseCommandLine({"solc", "contract.sol", "--debug-info", "ethdebug", "--ethdebug", "--via-ir"}); + CommandLineOptions commandLineOptions = parseCommandLine({"solc", "contract.sol", "--experimental", "--debug-info", "ethdebug", "--ethdebug", "--via-ir"}); BOOST_CHECK_EQUAL(commandLineOptions.compiler.outputs.ethdebug, true); BOOST_CHECK_EQUAL(commandLineOptions.compiler.outputs.ethdebugRuntime, false); BOOST_CHECK_EQUAL(commandLineOptions.output.debugInfoSelection.has_value(), true); BOOST_CHECK_EQUAL(commandLineOptions.output.debugInfoSelection->ethdebug, true); - commandLineOptions = parseCommandLine({"solc", "contract.sol", "--debug-info", "ethdebug", "--ethdebug-runtime", "--via-ir"}); + commandLineOptions = parseCommandLine({"solc", "contract.sol", "--experimental", "--debug-info", "ethdebug", "--ethdebug-runtime", "--via-ir"}); BOOST_CHECK_EQUAL(commandLineOptions.compiler.outputs.ethdebug, false); BOOST_CHECK_EQUAL(commandLineOptions.compiler.outputs.ethdebugRuntime, true); BOOST_CHECK_EQUAL(commandLineOptions.output.debugInfoSelection.has_value(), true); BOOST_CHECK_EQUAL(commandLineOptions.output.debugInfoSelection->ethdebug, true); - commandLineOptions = parseCommandLine({"solc", "contract.sol", "--ethdebug", "--via-ir"}); + commandLineOptions = parseCommandLine({"solc", "contract.sol", "--experimental", "--ethdebug", "--via-ir"}); BOOST_CHECK_EQUAL(commandLineOptions.compiler.outputs.ethdebug, true); BOOST_CHECK_EQUAL(commandLineOptions.compiler.outputs.ethdebugRuntime, false); // debug-info "ethdebug" selected implicitly, // if compiled with --ethdebug or --ethdebug-runtime and no debug-info was selected. BOOST_CHECK_EQUAL(commandLineOptions.output.debugInfoSelection.has_value(), true); BOOST_CHECK_EQUAL(commandLineOptions.output.debugInfoSelection->ethdebug, true); - commandLineOptions = parseCommandLine({"solc", "contract.sol", "--ethdebug-runtime", "--via-ir"}); + commandLineOptions = parseCommandLine({"solc", "contract.sol", "--experimental", "--ethdebug-runtime", "--via-ir"}); BOOST_CHECK_EQUAL(commandLineOptions.compiler.outputs.ethdebug, false); BOOST_CHECK_EQUAL(commandLineOptions.compiler.outputs.ethdebugRuntime, true); BOOST_CHECK_EQUAL(commandLineOptions.output.debugInfoSelection.has_value(), true); BOOST_CHECK_EQUAL(commandLineOptions.output.debugInfoSelection->ethdebug, true); - commandLineOptions = parseCommandLine({"solc", "contract.sol", "--ethdebug", "--ethdebug-runtime", "--via-ir"}); + commandLineOptions = parseCommandLine({"solc", "contract.sol", "--experimental", "--ethdebug", "--ethdebug-runtime", "--via-ir"}); BOOST_CHECK_EQUAL(commandLineOptions.compiler.outputs.ethdebug, true); BOOST_CHECK_EQUAL(commandLineOptions.compiler.outputs.ethdebugRuntime, true); BOOST_CHECK_EQUAL(commandLineOptions.output.debugInfoSelection.has_value(), true); BOOST_CHECK_EQUAL(commandLineOptions.output.debugInfoSelection->ethdebug, true); - commandLineOptions = parseCommandLine({"solc", "contract.sol", "--debug-info", "ethdebug", "--ir"}); + commandLineOptions = parseCommandLine({"solc", "contract.sol", "--experimental", "--debug-info", "ethdebug", "--ir"}); BOOST_CHECK_EQUAL(commandLineOptions.compiler.outputs.ethdebug, false); BOOST_CHECK_EQUAL(commandLineOptions.compiler.outputs.ethdebugRuntime, false); BOOST_CHECK_EQUAL(commandLineOptions.compiler.outputs.ir, true); @@ -665,6 +669,58 @@ BOOST_AUTO_TEST_CASE(ethdebug) BOOST_CHECK_EQUAL(commandLineOptions.output.debugInfoSelection->ethdebug, true); } +BOOST_AUTO_TEST_CASE(experimental_features_without_experimental_flag) +{ + std::vector const experimentalFeatures { + "--lsp", + "--import-ast", + "--import-asm-json", + "--ir-ast-json", + "--ir-optimized-ast-json", + "--yul-cfg-json", + "--ethdebug", + "--ethdebug-runtime" + }; + + std::string const expectedErrorMessage { + "The following options are only available in experimental mode: --lsp, --import-ast, --import-asm-json, " + "--ir-ast-json, --ir-optimized-ast-json, --yul-cfg-json, --ethdebug, --ethdebug-runtime, --experimental-eof-version. " + "To enable experimental mode, use the --experimental flag." + }; + + auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) { return _exception.what() == expectedErrorMessage; }; + + for (auto const& experimentalFeature: experimentalFeatures) + { + std::vector const commandLineOptions{"solc", experimentalFeature, "contract.sol"}; + BOOST_CHECK_EXCEPTION(parseCommandLine(commandLineOptions), CommandLineValidationError, hasCorrectMessage); + } + + std::vector const commandLineOptions{"solc", "--experimental-eof-version", "1", "contract.sol"}; + BOOST_CHECK_EXCEPTION(parseCommandLine(commandLineOptions), CommandLineValidationError, hasCorrectMessage); +} + +BOOST_AUTO_TEST_CASE(debug_info_ethdebug_without_experimental_flag) +{ + std::string const expectedErrorMessage { "--debug-info ethdebug can only be used by toggling the --experimental flag." }; + auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) { return _exception.what() == expectedErrorMessage; }; + + std::vector const commandLineOptions{"solc", "--debug-info", "ethdebug", "contract.sol"}; + BOOST_CHECK_EXCEPTION(parseCommandLine(commandLineOptions), CommandLineValidationError, hasCorrectMessage); +} + +BOOST_AUTO_TEST_CASE(experimental_flag_with_standard_json) +{ + std::string const expectedErrorMessage { + "Standard JSON input mode is incompatible with the --experimental flag. " + "Instead, please use the 'settings.experimental' setting in your Standard JSON input file to enable experimental mode." + }; + auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) { return _exception.what() == expectedErrorMessage; }; + + std::vector const commandLineOptions{"solc", "--experimental", "--standard-json", "input.json"}; + BOOST_CHECK_EXCEPTION(parseCommandLine(commandLineOptions), CommandLineValidationError, hasCorrectMessage); +} + BOOST_AUTO_TEST_SUITE_END() } // namespace solidity::frontend::test From adc2c30992d75c539e1d9934326d4091f368c0b5 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Mon, 20 Oct 2025 12:26:14 +0200 Subject: [PATCH 211/243] Cover experimental pragmas and tests --- libsolidity/analysis/SyntaxChecker.cpp | 6 ++++++ libsolidity/analysis/SyntaxChecker.h | 10 ++++++++-- libsolidity/interface/CompilerStack.cpp | 7 ++++++- test/libsolidity/Assembly.cpp | 2 +- test/libsolidity/FunctionDependencyGraphTest.cpp | 2 ++ test/libsolidity/FunctionDependencyGraphTest.h | 2 -- test/libsolidity/Metadata.cpp | 2 ++ test/libsolidity/SemanticTest.cpp | 1 + test/libsolidity/SolidityExpressionCompiler.cpp | 2 +- test/libsolidity/SyntaxTest.cpp | 4 +++- test/libsolidity/SyntaxTest.h | 1 + test/libsolidity/semanticTests/experimental/stub.sol | 1 + .../semanticTests/experimental/type_class.sol | 1 + test/libsolidity/smtCheckerTests/options/pragma.sol | 2 ++ .../struct_constructor_fixed_bytes_from_string_1.sol | 2 ++ .../abiEncoder/conflicting_settings_reverse.sol | 2 ++ .../conflicting_settings_reverse_experimental.sol | 2 ++ .../syntaxTests/abiEncoder/same_setting_twice.sol | 2 ++ .../syntaxTests/abiEncoder/selected_twice_v2.sol | 2 ++ .../experimental/builtin/builtin_type_definition.sol | 1 + .../builtin/builtin_type_definition_duplicate.sol | 1 + .../builtin/builtin_type_definition_unknown.sol | 1 + .../forall/undeclared_type_variable_in_function.sol | 1 + ...undeclared_type_variable_in_quantified_function.sol | 1 + .../forall/undeclared_type_variable_in_type_class.sol | 1 + .../experimental/import/experimental_solidity.sol | 1 + ...xperimental_solidity_multisource_not_all_enable.sol | 1 + .../import/experimental_solidity_out_of_order_1.sol | 1 + .../import/experimental_solidity_out_of_order_2.sol | 1 + .../import/experimental_solidity_wrong_evm_version.sol | 1 + .../experimental/import/parsing_stdlib_import_1.sol | 1 + .../experimental/import/parsing_stdlib_import_2.sol | 1 + .../experimental/import/parsing_stdlib_import_3.sol | 1 + .../experimental/import/parsing_stdlib_import_4.sol | 1 + .../inference/import_and_call_stdlib_function.sol | 1 + .../instantiation_invalid_type_constructor.sol | 1 + ...ntiation_member_type_does_not_match_declaration.sol | 1 + .../inference/instantiation_not_for_class.sol | 1 + .../experimental/inference/invalid_type_referenced.sol | 1 + .../monomorphic_function_call_type_mismatch.sol | 1 + .../inference/polymorphic_function_call.sol | 1 + .../polymorphic_function_call_let_polymorphism.sol | 1 + .../polymorphic_function_call_type_mismatch.sol | 1 + .../experimental/inference/polymorphic_type.sol | 1 + .../inference/polymorphic_type_abs_and_rep.sol | 1 + .../polymorphic_type_instantiation_and_operators.sol | 1 + ...ariable_multi_use_function_parameter_and_return.sol | 1 + .../type_variable_multi_use_function_parameters.sol | 1 + .../experimental/parsing/forall_free_function.sol | 1 + .../parsing/forall_free_function_no_type_var.sol | 1 + .../parsing/forall_free_function_with_sorts.sol | 1 + .../experimental/parsing/forall_type_class.sol | 1 + .../parsing/forall_type_class_instantiation.sol | 1 + .../pragma/experimental_empty_string_literal.sol | 2 ++ .../pragma/experimental_multiple_experimental.sol | 2 ++ .../pragma/experimental_multiple_same_line.sol | 2 ++ .../pragma/experimental_pragma_duplicate.sol | 2 ++ .../syntaxTests/pragma/experimental_pragma_empty.sol | 2 ++ .../experimental_pragma_unknown_string_literal.sol | 2 ++ .../experimental_pragma_without_experimental_mode.sol | 4 ++++ .../syntaxTests/pragma/experimental_test_warning.sol | 2 ++ .../pragma/experimental_unknown_number_literal.sol | 2 ++ .../experimental_unknown_quoted_string_literal.sol | 2 ++ 63 files changed, 101 insertions(+), 8 deletions(-) create mode 100644 test/libsolidity/syntaxTests/pragma/experimental_pragma_without_experimental_mode.sol diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 9bf784b365..8253eec49a 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -83,6 +83,12 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) else if (_pragma.literals()[0] == "experimental") { solAssert(m_sourceUnit, ""); + if (!m_experimental) + m_errorReporter.syntaxError( + 2816_error, + _pragma.location(), + "Experimental pragmas can only be used if the experimental mode has been enabled." + ); std::vector literals(_pragma.literals().begin() + 1, _pragma.literals().end()); if (literals.empty()) m_errorReporter.syntaxError( diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index 69bda229f7..9babfe47e4 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -45,9 +45,12 @@ class SyntaxChecker: private ASTConstVisitor { public: /// @param _errorReporter provides the error logging functionality. - SyntaxChecker(langutil::ErrorReporter& _errorReporter, bool _useYulOptimizer): + /// @param _useYulOptimizer indicates whether Yul optimizer is enabled. + /// @param _experimental indicates whether the experimental toggle (option) is enabled. + SyntaxChecker(langutil::ErrorReporter& _errorReporter, bool _useYulOptimizer, bool _experimental): m_errorReporter(_errorReporter), - m_useYulOptimizer(_useYulOptimizer) + m_useYulOptimizer(_useYulOptimizer), + m_experimental(_experimental) {} bool checkSyntax(ASTNode const& _astRoot); @@ -112,6 +115,9 @@ class SyntaxChecker: private ASTConstVisitor /// Flag that indicates whether we are inside an unchecked block. bool m_uncheckedArithmetic = false; + /// Flag that indicates whether experimental mode is toggled. + bool m_experimental = false; + int m_inLoopDepth = 0; std::optional m_currentContractKind; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index d9e703fc3f..2ed72db0ff 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -473,7 +473,7 @@ bool CompilerStack::analyze() { bool experimentalSolidity = isExperimentalSolidity(); - SyntaxChecker syntaxChecker(m_errorReporter, m_optimiserSettings.runYulOptimiser); + SyntaxChecker syntaxChecker(m_errorReporter, m_optimiserSettings.runYulOptimiser, m_experimental); for (Source const* source: m_sourceOrder) if (source->ast && !syntaxChecker.checkSyntax(*source->ast)) noErrors = false; @@ -767,6 +767,10 @@ bool CompilerStack::compile(State _stopAfter) std::map> otherCompilers; for (Source const* source: m_sourceOrder) + { + if (source->ast && !m_experimental) + solAssert(source->ast->annotation().experimentalFeatures.empty()); + for (ASTPointer const& node: source->ast->nodes()) if (auto contract = dynamic_cast(node.get())) if (isRequestedContract(*contract)) @@ -801,6 +805,7 @@ bool CompilerStack::compile(State _stopAfter) if (m_errorReporter.hasErrors()) return false; } + } solAssert(!m_errorReporter.hasErrors()); m_stackState = CompilationSuccessful; diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index bf9c6b16af..85d6a39820 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -66,7 +66,7 @@ evmasm::AssemblyItems compileContract(std::shared_ptr _sourceCode) BOOST_CHECK(!!sourceUnit); Scoper::assignScopes(*sourceUnit); - BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit)); + BOOST_REQUIRE(SyntaxChecker(errorReporter, false, false).checkSyntax(*sourceUnit)); GlobalContext globalContext(solidity::test::CommonOptions::get().evmVersion()); NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter, false); DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion()); diff --git a/test/libsolidity/FunctionDependencyGraphTest.cpp b/test/libsolidity/FunctionDependencyGraphTest.cpp index c7992d2a2c..ded643e682 100644 --- a/test/libsolidity/FunctionDependencyGraphTest.cpp +++ b/test/libsolidity/FunctionDependencyGraphTest.cpp @@ -42,6 +42,8 @@ TestCase::TestResult FunctionDependencyGraphTest::run(std::ostream& _stream, std compiler().setSources(StringMap{{"", m_source}}); compiler().setViaIR(true); compiler().setOptimiserSettings(OptimiserSettings::none()); + // Experimental on by default as this is an extension of the initial experimental Solidity + compiler().setExperimental(true); if (!compiler().compile(CompilerStack::AnalysisSuccessful)) { printPrefixed(_stream, formatErrors(filteredErrors(), _formatted), _linePrefix); diff --git a/test/libsolidity/FunctionDependencyGraphTest.h b/test/libsolidity/FunctionDependencyGraphTest.h index 83f3eb0ab3..4b6b78a970 100644 --- a/test/libsolidity/FunctionDependencyGraphTest.h +++ b/test/libsolidity/FunctionDependencyGraphTest.h @@ -24,8 +24,6 @@ #include #include -#include -#include namespace solidity::frontend::test { diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index 3b7d101d59..0b6bae28db 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -104,6 +104,7 @@ BOOST_AUTO_TEST_CASE(metadata_stamp) compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize); compilerStack.setMetadataHash(metadataHash); + compilerStack.setExperimental(true); // Experimental pragma requires it BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); bytes const& bytecode = compilerStack.runtimeObject("test").bytecode; std::string const& metadata = compilerStack.metadata("test"); @@ -177,6 +178,7 @@ BOOST_AUTO_TEST_CASE(metadata_stamp_experimental) compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize); compilerStack.setMetadataHash(metadataHash); + compilerStack.setExperimental(true); // Experimental pragma requires it BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); bytes const& bytecode = compilerStack.runtimeObject("test").bytecode; std::string const& metadata = compilerStack.metadata("test"); diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index 890a51596a..296467ae20 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -112,6 +112,7 @@ SemanticTest::SemanticTest( m_revertStrings = revertStrings.value(); m_allowNonExistingFunctions = m_reader.boolSetting("allowNonExistingFunctions", false); + m_compiler.setExperimental(m_reader.boolSetting("experimental", false)); parseExpectations(m_reader.stream()); soltestAssert(!m_tests.empty(), "No tests specified in " + _filename); diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index b6d40eed02..d64ffac5b7 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -126,7 +126,7 @@ bytes compileFirstExpression( ErrorReporter errorReporter(errors); GlobalContext globalContext(solidity::test::CommonOptions::get().evmVersion()); Scoper::assignScopes(*sourceUnit); - BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit)); + BOOST_REQUIRE(SyntaxChecker(errorReporter, false, false).checkSyntax(*sourceUnit)); NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter, false); resolver.registerDeclarations(*sourceUnit); BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*sourceUnit), "Resolving names failed"); diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 8944710542..23b92bcb1d 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -51,13 +51,14 @@ SyntaxTest::SyntaxTest( auto const eofEnabled = solidity::test::CommonOptions::get().eofVersion().has_value(); m_compileViaYul = m_reader.stringSetting("compileViaYul", eofEnabled ? "true" : "false"); - if (!util::contains(compileViaYulAllowedValues, m_compileViaYul)) + if (!compileViaYulAllowedValues.contains(m_compileViaYul)) BOOST_THROW_EXCEPTION(std::runtime_error("Invalid compileViaYul value: " + m_compileViaYul + ".")); if (m_compileViaYul == "false" && eofEnabled) m_shouldRun = false; m_optimiseYul = m_reader.boolSetting("optimize-yul", true); + m_experimental = m_reader.boolSetting("experimental", false); static std::map const pipelineStages = { {"parsing", PipelineStage::Parsing}, @@ -81,6 +82,7 @@ void SyntaxTest::setupCompiler(CompilerStack& _compiler) OptimiserSettings::minimal() ); _compiler.setViaIR(m_compileViaYul == "true"); + _compiler.setExperimental(m_experimental); _compiler.setMetadataFormat(CompilerStack::MetadataFormat::NoMetadata); _compiler.setMetadataHash(CompilerStack::MetadataHash::None); } diff --git a/test/libsolidity/SyntaxTest.h b/test/libsolidity/SyntaxTest.h index 085a32f944..9c52acd9bc 100644 --- a/test/libsolidity/SyntaxTest.h +++ b/test/libsolidity/SyntaxTest.h @@ -53,6 +53,7 @@ class SyntaxTest: public AnalysisFramework, public solidity::test::CommonSyntaxT virtual void filterObtainedErrors(); bool m_optimiseYul{}; + bool m_experimental{false}; std::string m_compileViaYul{}; langutil::Error::Severity m_minSeverity{}; PipelineStage m_stopAfter; diff --git a/test/libsolidity/semanticTests/experimental/stub.sol b/test/libsolidity/semanticTests/experimental/stub.sol index d2d0999bfb..7cc1c30334 100644 --- a/test/libsolidity/semanticTests/experimental/stub.sol +++ b/test/libsolidity/semanticTests/experimental/stub.sol @@ -89,6 +89,7 @@ contract C { } } // ==== +// experimental: true // EVMVersion: >=constantinople // ==== // compileViaYul: true diff --git a/test/libsolidity/semanticTests/experimental/type_class.sol b/test/libsolidity/semanticTests/experimental/type_class.sol index 7331c0dbd7..4f101a1474 100644 --- a/test/libsolidity/semanticTests/experimental/type_class.sol +++ b/test/libsolidity/semanticTests/experimental/type_class.sol @@ -61,6 +61,7 @@ contract C { } // ==== +// experimental: true // EVMVersion: >=constantinople // compileViaYul: true // bytecodeFormat: legacy diff --git a/test/libsolidity/smtCheckerTests/options/pragma.sol b/test/libsolidity/smtCheckerTests/options/pragma.sol index b3e35ee022..af2f644ad5 100644 --- a/test/libsolidity/smtCheckerTests/options/pragma.sol +++ b/test/libsolidity/smtCheckerTests/options/pragma.sol @@ -4,6 +4,8 @@ contract C { assert(x > 0); } } +// ==== +// experimental: true // ---- // Warning 5523: (0-31): The SMTChecker pragma has been deprecated and will be removed in the future. Please use the "model checker engine" compiler setting to activate the SMTChecker instead. If the pragma is enabled, all engines will be used. // Warning 6328: (90-103): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\n\nTransaction trace:\nC.constructor()\nC.f(0) diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_fixed_bytes_from_string_1.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_fixed_bytes_from_string_1.sol index 30d6da4a46..f31b9ca88e 100644 --- a/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_fixed_bytes_from_string_1.sol +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_fixed_bytes_from_string_1.sol @@ -9,6 +9,8 @@ contract C { assert(S({a:2, b:""}).a == 0); // should fail } } +// ==== +// experimental: true // ---- // Warning 5523: (0-31): The SMTChecker pragma has been deprecated and will be removed in the future. Please use the "model checker engine" compiler setting to activate the SMTChecker instead. If the pragma is enabled, all engines will be used. // Warning 6328: (178-207): CHC: Assertion violation happens here. diff --git a/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse.sol b/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse.sol index feacbbeb31..19a29b524f 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse.sol @@ -1,5 +1,7 @@ pragma abicoder v1; pragma abicoder v2; +// ==== +// experimental: true // ---- // Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // SyntaxError 3845: (20-39): ABI coder has already been selected for this source unit. diff --git a/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse_experimental.sol b/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse_experimental.sol index 884d03132e..022aa76a22 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse_experimental.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse_experimental.sol @@ -1,5 +1,7 @@ pragma abicoder v1; pragma experimental ABIEncoderV2; +// ==== +// experimental: true // ---- // Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // SyntaxError 8273: (20-53): ABI coder v1 has already been selected through "pragma abicoder v1". diff --git a/test/libsolidity/syntaxTests/abiEncoder/same_setting_twice.sol b/test/libsolidity/syntaxTests/abiEncoder/same_setting_twice.sol index 0303bcb149..fcdc2d5bbc 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/same_setting_twice.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/same_setting_twice.sol @@ -1,4 +1,6 @@ pragma experimental ABIEncoderV2; pragma abicoder v2; +// ==== +// experimental: true // ---- // SyntaxError 3845: (34-53): ABI coder has already been selected for this source unit. diff --git a/test/libsolidity/syntaxTests/abiEncoder/selected_twice_v2.sol b/test/libsolidity/syntaxTests/abiEncoder/selected_twice_v2.sol index 0b296da3a8..848efa83e9 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/selected_twice_v2.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/selected_twice_v2.sol @@ -1,3 +1,5 @@ pragma abicoder v2; pragma experimental ABIEncoderV2; +// ==== +// experimental: true // ---- diff --git a/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition.sol b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition.sol index 74a659cd8b..f4d53061f1 100644 --- a/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition.sol +++ b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition.sol @@ -30,6 +30,7 @@ contract C { } } // ==== +// experimental: true // EVMVersion: >=constantinople // compileViaYul: true // bytecodeFormat: legacy diff --git a/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_duplicate.sol b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_duplicate.sol index 28f6c39fd7..720bb8c272 100644 --- a/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_duplicate.sol +++ b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_duplicate.sol @@ -9,6 +9,7 @@ type word2 = __builtin("word"); type fun1(T, U) = __builtin("fun"); type fun2(T, U) = __builtin("fun"); // ==== +// experimental: true // EVMVersion: >=constantinople // ---- // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_unknown.sol b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_unknown.sol index 90072f1983..0dc19b7f84 100644 --- a/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_unknown.sol +++ b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_unknown.sol @@ -2,6 +2,7 @@ pragma experimental solidity; type someUnknownType = __builtin("someUnknownType"); // ==== +// experimental: true // EVMVersion: >=constantinople // ---- // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/experimental/forall/undeclared_type_variable_in_function.sol b/test/libsolidity/syntaxTests/experimental/forall/undeclared_type_variable_in_function.sol index da11699691..ef6331740e 100644 --- a/test/libsolidity/syntaxTests/experimental/forall/undeclared_type_variable_in_function.sol +++ b/test/libsolidity/syntaxTests/experimental/forall/undeclared_type_variable_in_function.sol @@ -7,6 +7,7 @@ function f(p: P, q: T(Q)) { let s: S; } // ==== +// experimental: true // EVMVersion: >=constantinople // ---- // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/experimental/forall/undeclared_type_variable_in_quantified_function.sol b/test/libsolidity/syntaxTests/experimental/forall/undeclared_type_variable_in_quantified_function.sol index 91b7345693..5113860bdc 100644 --- a/test/libsolidity/syntaxTests/experimental/forall/undeclared_type_variable_in_quantified_function.sol +++ b/test/libsolidity/syntaxTests/experimental/forall/undeclared_type_variable_in_quantified_function.sol @@ -5,6 +5,7 @@ function f(b: B) { let c: C; } // ==== +// experimental: true // EVMVersion: >=constantinople // ---- // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/experimental/forall/undeclared_type_variable_in_type_class.sol b/test/libsolidity/syntaxTests/experimental/forall/undeclared_type_variable_in_type_class.sol index 8af0cce826..c786035165 100644 --- a/test/libsolidity/syntaxTests/experimental/forall/undeclared_type_variable_in_type_class.sol +++ b/test/libsolidity/syntaxTests/experimental/forall/undeclared_type_variable_in_type_class.sol @@ -10,6 +10,7 @@ instantiation T(Y): C { function f(self: T(Z)) {} } // ==== +// experimental: true // EVMVersion: >=constantinople // ---- // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/experimental/import/experimental_solidity.sol b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity.sol index a84ac0e759..011043f66f 100644 --- a/test/libsolidity/syntaxTests/experimental/import/experimental_solidity.sol +++ b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity.sol @@ -1,5 +1,6 @@ pragma experimental solidity; // ==== +// experimental: true // EVMVersion: >=constantinople // compileViaYul: true // ---- diff --git a/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_multisource_not_all_enable.sol b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_multisource_not_all_enable.sol index fe4125e843..79f226535e 100644 --- a/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_multisource_not_all_enable.sol +++ b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_multisource_not_all_enable.sol @@ -19,6 +19,7 @@ contract D { A a; } // ==== +// experimental: true // EVMVersion: >=constantinople // ---- // ParserError 2141: (B.sol:0-29): File declares "pragma experimental solidity". If you want to enable the experimental mode, all source units must include the pragma. diff --git a/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_out_of_order_1.sol b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_out_of_order_1.sol index d6741b0d3a..1ceecba27e 100644 --- a/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_out_of_order_1.sol +++ b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_out_of_order_1.sol @@ -2,6 +2,7 @@ contract A {} pragma experimental solidity; // ==== +// experimental: true // EVMVersion: >=constantinople // ---- // ParserError 8185: (45-45): Experimental pragma "solidity" can only be set at the beginning of the source unit. diff --git a/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_out_of_order_2.sol b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_out_of_order_2.sol index 940bd3fbb3..6d1ba15e2b 100644 --- a/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_out_of_order_2.sol +++ b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_out_of_order_2.sol @@ -10,6 +10,7 @@ struct A uint256 x; } // ==== +// experimental: true // EVMVersion: >=constantinople // ---- // ParserError 8185: (83-89): Experimental pragma "solidity" can only be set at the beginning of the source unit. diff --git a/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_wrong_evm_version.sol b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_wrong_evm_version.sol index 3e070535b0..be5a30d528 100644 --- a/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_wrong_evm_version.sol +++ b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_wrong_evm_version.sol @@ -3,6 +3,7 @@ pragma experimental solidity; contract C {} // ==== +// experimental: true // EVMVersion: =constantinople // compileViaYul: true // ---- diff --git a/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_2.sol b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_2.sol index bdc66e4ad3..cc3146664c 100644 --- a/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_2.sol +++ b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_2.sol @@ -2,6 +2,7 @@ pragma experimental solidity; import std.stub as stub; // ==== +// experimental: true // EVMVersion: >=constantinople // compileViaYul: true // ---- diff --git a/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_3.sol b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_3.sol index 1bb340536c..26b9589d0f 100644 --- a/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_3.sol +++ b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_3.sol @@ -2,6 +2,7 @@ pragma experimental solidity; import { identity } from std.stub; // ==== +// experimental: true // EVMVersion: >=constantinople // compileViaYul: true // ---- diff --git a/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_4.sol b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_4.sol index 64a67fe6c9..36df8c7c16 100644 --- a/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_4.sol +++ b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_4.sol @@ -2,6 +2,7 @@ pragma experimental solidity; import * as stub from std.stub; // ==== +// experimental: true // EVMVersion: >=constantinople // compileViaYul: true // ---- diff --git a/test/libsolidity/syntaxTests/experimental/inference/import_and_call_stdlib_function.sol b/test/libsolidity/syntaxTests/experimental/inference/import_and_call_stdlib_function.sol index 72af2133f2..bbb255568d 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/import_and_call_stdlib_function.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/import_and_call_stdlib_function.sol @@ -11,6 +11,7 @@ contract C } // ==== +// experimental: true // EVMVersion: >=constantinople // compileViaYul: true // bytecodeFormat: legacy diff --git a/test/libsolidity/syntaxTests/experimental/inference/instantiation_invalid_type_constructor.sol b/test/libsolidity/syntaxTests/experimental/inference/instantiation_invalid_type_constructor.sol index 0245deb1f4..15c4009326 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/instantiation_invalid_type_constructor.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/instantiation_invalid_type_constructor.sol @@ -6,6 +6,7 @@ function f() {} instantiation f: C {} // ==== +// experimental: true // EVMVersion: >=constantinople // compileViaYul: true // ---- diff --git a/test/libsolidity/syntaxTests/experimental/inference/instantiation_member_type_does_not_match_declaration.sol b/test/libsolidity/syntaxTests/experimental/inference/instantiation_member_type_does_not_match_declaration.sol index 1c845b8eee..47d282a1a2 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/instantiation_member_type_does_not_match_declaration.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/instantiation_member_type_does_not_match_declaration.sol @@ -11,6 +11,7 @@ instantiation T: C { function f(self: U) {} } // ==== +// experimental: true // EVMVersion: >=constantinople // compileViaYul: true // ---- diff --git a/test/libsolidity/syntaxTests/experimental/inference/instantiation_not_for_class.sol b/test/libsolidity/syntaxTests/experimental/inference/instantiation_not_for_class.sol index c48fff74f9..89217a56d8 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/instantiation_not_for_class.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/instantiation_not_for_class.sol @@ -5,6 +5,7 @@ type U; instantiation T: U {} // ==== +// experimental: true // EVMVersion: >=constantinople // compileViaYul: true // ---- diff --git a/test/libsolidity/syntaxTests/experimental/inference/invalid_type_referenced.sol b/test/libsolidity/syntaxTests/experimental/inference/invalid_type_referenced.sol index ac097bf392..98bd7890ce 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/invalid_type_referenced.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/invalid_type_referenced.sol @@ -7,6 +7,7 @@ class Self: C function g(self: Self, x: f); } // ==== +// experimental: true // EVMVersion: >=constantinople // ---- // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/experimental/inference/monomorphic_function_call_type_mismatch.sol b/test/libsolidity/syntaxTests/experimental/inference/monomorphic_function_call_type_mismatch.sol index e7af573aee..d32b2ba0ed 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/monomorphic_function_call_type_mismatch.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/monomorphic_function_call_type_mismatch.sol @@ -9,6 +9,7 @@ function run(a: U, b: T) { f(a, b); } // ==== +// experimental: true // EVMVersion: >=constantinople // ---- // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol index c9c7e01109..a6c44c0f31 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol @@ -11,6 +11,7 @@ function run(a: T, b: U(T), c: U(U(T))) { f(b, b, c); } // ==== +// experimental: true // EVMVersion: >=constantinople // compileViaYul: true // ---- diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call_let_polymorphism.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call_let_polymorphism.sol index 1a43d0807a..159796a2be 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call_let_polymorphism.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call_let_polymorphism.sol @@ -13,6 +13,7 @@ function run(a: T, b: U) { g(b); } // ==== +// experimental: true // EVMVersion: >=constantinople // ---- // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call_type_mismatch.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call_type_mismatch.sol index 74f102a881..433a74c4b9 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call_type_mismatch.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call_type_mismatch.sol @@ -10,6 +10,7 @@ function run(a: T(V)) { f(a); } // ==== +// experimental: true // EVMVersion: >=constantinople // ---- // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol index 85f810a9dc..88e0da7de3 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol @@ -13,6 +13,7 @@ function run() { let y: T(V, Y, Z: D); } // ==== +// experimental: true // EVMVersion: >=constantinople // compileViaYul: true // ---- diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type_abs_and_rep.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type_abs_and_rep.sol index ebebf6dafc..b5311bd1b8 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type_abs_and_rep.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type_abs_and_rep.sol @@ -18,6 +18,7 @@ function fun() { U.abs(t); } // ==== +// experimental: true // EVMVersion: >=constantinople // compileViaYul: true // ---- diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type_instantiation_and_operators.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type_instantiation_and_operators.sol index c26436e06c..93323dee03 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type_instantiation_and_operators.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type_instantiation_and_operators.sol @@ -46,6 +46,7 @@ function fun(a: T(int: P3), b: T(str: P4)) { C.foo(b, b); } // ==== +// experimental: true // EVMVersion: >=constantinople // compileViaYul: true // ---- diff --git a/test/libsolidity/syntaxTests/experimental/inference/type_variable_multi_use_function_parameter_and_return.sol b/test/libsolidity/syntaxTests/experimental/inference/type_variable_multi_use_function_parameter_and_return.sol index 00cd7b1207..1fe995332f 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/type_variable_multi_use_function_parameter_and_return.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/type_variable_multi_use_function_parameter_and_return.sol @@ -10,6 +10,7 @@ function test(t: T, u: U) { t = f(u); } // ==== +// experimental: true // EVMVersion: >=constantinople // ---- // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/experimental/inference/type_variable_multi_use_function_parameters.sol b/test/libsolidity/syntaxTests/experimental/inference/type_variable_multi_use_function_parameters.sol index 7122cdd9ef..47ccc6754d 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/type_variable_multi_use_function_parameters.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/type_variable_multi_use_function_parameters.sol @@ -10,6 +10,7 @@ function test(t: T, u: U) { f(t, u); } // ==== +// experimental: true // EVMVersion: >=constantinople // ---- // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/experimental/parsing/forall_free_function.sol b/test/libsolidity/syntaxTests/experimental/parsing/forall_free_function.sol index ba22a9fae4..2b5236f790 100644 --- a/test/libsolidity/syntaxTests/experimental/parsing/forall_free_function.sol +++ b/test/libsolidity/syntaxTests/experimental/parsing/forall_free_function.sol @@ -6,6 +6,7 @@ function f(a: A) {} forall (A, B) function g(a: A, b: B) {} // ==== +// experimental: true // EVMVersion: >=constantinople // compileViaYul: true // ---- diff --git a/test/libsolidity/syntaxTests/experimental/parsing/forall_free_function_no_type_var.sol b/test/libsolidity/syntaxTests/experimental/parsing/forall_free_function_no_type_var.sol index 66753fdd0f..a6f5dd61fb 100644 --- a/test/libsolidity/syntaxTests/experimental/parsing/forall_free_function_no_type_var.sol +++ b/test/libsolidity/syntaxTests/experimental/parsing/forall_free_function_no_type_var.sol @@ -3,6 +3,7 @@ pragma experimental solidity; forall () function f(x: ()) {} // ==== +// experimental: true // EVMVersion: >=constantinople // compileViaYul: true // ---- diff --git a/test/libsolidity/syntaxTests/experimental/parsing/forall_free_function_with_sorts.sol b/test/libsolidity/syntaxTests/experimental/parsing/forall_free_function_with_sorts.sol index 1ab09fd2bd..0e69f79b33 100644 --- a/test/libsolidity/syntaxTests/experimental/parsing/forall_free_function_with_sorts.sol +++ b/test/libsolidity/syntaxTests/experimental/parsing/forall_free_function_with_sorts.sol @@ -9,6 +9,7 @@ function f(a: A: Class1, b: B: Class1) {} forall A: Class1 function g(a: A) {} // ==== +// experimental: true // EVMVersion: >=constantinople // compileViaYul: true // ---- diff --git a/test/libsolidity/syntaxTests/experimental/parsing/forall_type_class.sol b/test/libsolidity/syntaxTests/experimental/parsing/forall_type_class.sol index aa0012eac1..0dd5b74b5e 100644 --- a/test/libsolidity/syntaxTests/experimental/parsing/forall_type_class.sol +++ b/test/libsolidity/syntaxTests/experimental/parsing/forall_type_class.sol @@ -3,6 +3,7 @@ pragma experimental solidity; forall (A, B) class Self: C {} // ==== +// experimental: true // EVMVersion: >=constantinople // ---- // ParserError 5709: (45-50): Expected a function definition. diff --git a/test/libsolidity/syntaxTests/experimental/parsing/forall_type_class_instantiation.sol b/test/libsolidity/syntaxTests/experimental/parsing/forall_type_class_instantiation.sol index 31573447a3..5d90e0e32e 100644 --- a/test/libsolidity/syntaxTests/experimental/parsing/forall_type_class_instantiation.sol +++ b/test/libsolidity/syntaxTests/experimental/parsing/forall_type_class_instantiation.sol @@ -7,6 +7,7 @@ class Self: C {} forall (A, B) instantiation T: C {} // ==== +// experimental: true // EVMVersion: >=constantinople // ---- // ParserError 5709: (72-85): Expected a function definition. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_empty_string_literal.sol b/test/libsolidity/syntaxTests/pragma/experimental_empty_string_literal.sol index 963ef5884b..c97f464952 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_empty_string_literal.sol +++ b/test/libsolidity/syntaxTests/pragma/experimental_empty_string_literal.sol @@ -1,3 +1,5 @@ pragma experimental ""; +// ==== +// experimental: true // ---- // SyntaxError 3250: (0-23): Empty experimental feature name is invalid. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_multiple_experimental.sol b/test/libsolidity/syntaxTests/pragma/experimental_multiple_experimental.sol index 49fe597ec3..204c4eada0 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_multiple_experimental.sol +++ b/test/libsolidity/syntaxTests/pragma/experimental_multiple_experimental.sol @@ -1,4 +1,6 @@ pragma experimental experimental __test; +// ==== +// experimental: true // ---- // SyntaxError 6022: (0-40): Stray arguments. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_multiple_same_line.sol b/test/libsolidity/syntaxTests/pragma/experimental_multiple_same_line.sol index f88c0e0d84..f6210a871c 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_multiple_same_line.sol +++ b/test/libsolidity/syntaxTests/pragma/experimental_multiple_same_line.sol @@ -1,3 +1,5 @@ pragma experimental unsupportedName unsupportedName; +// ==== +// experimental: true // ---- // SyntaxError 6022: (0-52): Stray arguments. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_pragma_duplicate.sol b/test/libsolidity/syntaxTests/pragma/experimental_pragma_duplicate.sol index 042b5464ce..caebd64479 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_pragma_duplicate.sol +++ b/test/libsolidity/syntaxTests/pragma/experimental_pragma_duplicate.sol @@ -1,5 +1,7 @@ pragma experimental __test; pragma experimental __test; +// ==== +// experimental: true // ---- // Warning 2264: (0-27): Experimental features are turned on. Do not use experimental features on live deployments. // SyntaxError 1231: (28-55): Duplicate experimental feature name. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_pragma_empty.sol b/test/libsolidity/syntaxTests/pragma/experimental_pragma_empty.sol index d45e7e6afe..2360d4e4db 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_pragma_empty.sol +++ b/test/libsolidity/syntaxTests/pragma/experimental_pragma_empty.sol @@ -1,3 +1,5 @@ pragma experimental; +// ==== +// experimental: true // ---- // SyntaxError 9679: (0-20): Experimental feature name is missing. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_pragma_unknown_string_literal.sol b/test/libsolidity/syntaxTests/pragma/experimental_pragma_unknown_string_literal.sol index 76be60ce8a..4a52ec831d 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_pragma_unknown_string_literal.sol +++ b/test/libsolidity/syntaxTests/pragma/experimental_pragma_unknown_string_literal.sol @@ -1,3 +1,5 @@ pragma experimental unsupportedName; +// ==== +// experimental: true // ---- // SyntaxError 8491: (0-36): Unsupported experimental feature name. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_pragma_without_experimental_mode.sol b/test/libsolidity/syntaxTests/pragma/experimental_pragma_without_experimental_mode.sol new file mode 100644 index 0000000000..d5ba45b701 --- /dev/null +++ b/test/libsolidity/syntaxTests/pragma/experimental_pragma_without_experimental_mode.sol @@ -0,0 +1,4 @@ +pragma experimental __test; +// ---- +// SyntaxError 2816: (0-27): Experimental pragmas can only be used if the experimental mode has been enabled. +// Warning 2264: (0-27): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_test_warning.sol b/test/libsolidity/syntaxTests/pragma/experimental_test_warning.sol index 31f46fbb8f..2706c2f315 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_test_warning.sol +++ b/test/libsolidity/syntaxTests/pragma/experimental_test_warning.sol @@ -1,3 +1,5 @@ pragma experimental __test; +// ==== +// experimental: true // ---- // Warning 2264: (0-27): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_unknown_number_literal.sol b/test/libsolidity/syntaxTests/pragma/experimental_unknown_number_literal.sol index f8e8267a0d..4329b387b9 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_unknown_number_literal.sol +++ b/test/libsolidity/syntaxTests/pragma/experimental_unknown_number_literal.sol @@ -1,3 +1,5 @@ pragma experimental 123; +// ==== +// experimental: true // ---- // SyntaxError 8491: (0-24): Unsupported experimental feature name. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_unknown_quoted_string_literal.sol b/test/libsolidity/syntaxTests/pragma/experimental_unknown_quoted_string_literal.sol index a08a0489b2..9c69275107 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_unknown_quoted_string_literal.sol +++ b/test/libsolidity/syntaxTests/pragma/experimental_unknown_quoted_string_literal.sol @@ -1,3 +1,5 @@ pragma experimental "unsupportedName"; +// ==== +// experimental: true // ---- // SyntaxError 8491: (0-38): Unsupported experimental feature name. From 168b4a386a8c0bf5753d815d77ea3a21e102ad61 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 20 Nov 2025 07:44:36 +0100 Subject: [PATCH 212/243] Introduce experimental option in standard JSON --- libsolidity/interface/StandardCompiler.cpp | 59 +++- libsolidity/interface/StandardCompiler.h | 1 + scripts/ASTImportTest.sh | 2 +- .../input.json | 1 + .../input.json | 1 + .../standard_import_asm_json/input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../standard_import_ast/input.json | 1 + .../input.json | 1 + .../standard_import_with_comments/input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../standard_ir_ast_requested/input.json | 1 + .../input.json | 8 + .../output.json | 11 + .../standard_no_experimental_eof/input.json | 12 + .../standard_no_experimental_eof/output.json | 11 + .../input.json | 19 ++ .../output.json | 11 + .../input.json | 15 + .../output.json | 11 + .../input.json | 17 + .../output.json | 11 + .../input.json | 23 ++ .../output.json | 11 + .../input.json | 12 + .../output.json | 11 + .../input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../standard_yul_cfg_json_export/input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../input.json | 1 + .../standard_yul_ethdebug_eof/input.json | 1 + .../standard_yul_ethdebug_ir/input.json | 1 + .../input.json | 1 + test/ethdebugSchemaTests/input_file.json | 1 + test/ethdebugSchemaTests/input_file_eof.json | 1 + test/libsolidity/StandardCompiler.cpp | 315 +++++++++++++++--- 56 files changed, 548 insertions(+), 50 deletions(-) create mode 100644 test/cmdlineTests/standard_no_experimental_ast_import/input.json create mode 100644 test/cmdlineTests/standard_no_experimental_ast_import/output.json create mode 100644 test/cmdlineTests/standard_no_experimental_eof/input.json create mode 100644 test/cmdlineTests/standard_no_experimental_eof/output.json create mode 100644 test/cmdlineTests/standard_no_experimental_ethdebug/input.json create mode 100644 test/cmdlineTests/standard_no_experimental_ethdebug/output.json create mode 100644 test/cmdlineTests/standard_no_experimental_ir_ast_output/input.json create mode 100644 test/cmdlineTests/standard_no_experimental_ir_ast_output/output.json create mode 100644 test/cmdlineTests/standard_no_experimental_yul_cfg_json_export/input.json create mode 100644 test/cmdlineTests/standard_no_experimental_yul_cfg_json_export/output.json create mode 100644 test/cmdlineTests/standard_no_experimental_yul_ethdebug/input.json create mode 100644 test/cmdlineTests/standard_no_experimental_yul_ethdebug/output.json create mode 100644 test/cmdlineTests/standard_no_experimental_yul_ir_ast_requested/input.json create mode 100644 test/cmdlineTests/standard_no_experimental_yul_ir_ast_requested/output.json diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index d5502228bd..419c3fac5f 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -46,6 +46,8 @@ #include #include +#include + using namespace solidity; using namespace solidity::yul; using namespace solidity::frontend; @@ -298,10 +300,28 @@ bool isEthdebugRequested(Json const& _outputSelection) if (!_outputSelection.is_object()) return false; + static std::array const ethdebugArtifacts{"ethdebug", "evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"}; + for (auto const& fileRequests: _outputSelection) for (auto const& requests: fileRequests) for (auto const& request: requests) - if (request == "evm.bytecode.ethdebug" || request == "evm.deployedBytecode.ethdebug") + if (ranges::contains(ethdebugArtifacts, request.get())) + return true; + + return false; +} + +bool isExperimentalArtifactRequested(Json const& _outputSelection) +{ + static std::array const experimentalArtifacts{"irAst", "irOptimizedAst", "yulCFGJson"}; + + if (isEthdebugRequested(_outputSelection)) + return true; + + for (auto const& fileRequests: _outputSelection) + for (auto const& requests: fileRequests) + for (auto const& request: requests) + if (ranges::contains(experimentalArtifacts, request.get())) return true; return false; @@ -430,7 +450,7 @@ std::optional checkAuxiliaryInputKeys(Json const& _input) std::optional checkSettingsKeys(Json const& _input) { - static std::set keys{"debug", "evmVersion", "eofVersion", "libraries", "metadata", "modelChecker", "optimizer", "outputSelection", "remappings", "stopAfter", "viaIR"}; + static std::set keys{"debug", "evmVersion", "experimental", "eofVersion", "libraries", "metadata", "modelChecker", "optimizer", "outputSelection", "remappings", "stopAfter", "viaIR"}; return checkKeys(_input, keys, "settings"); } @@ -803,6 +823,13 @@ std::variant StandardCompiler::parseI if (auto result = checkSettingsKeys(settings)) return *result; + if (settings.contains("experimental")) + { + if (!settings["experimental"].is_boolean()) + return formatFatalError(Error::Type::JSONError, "\"settings.experimental\" must be a Boolean."); + ret.experimental = settings["experimental"].get(); + } + if (settings.contains("stopAfter")) { if (!settings["stopAfter"].is_string()) @@ -1213,6 +1240,22 @@ std::variant StandardCompiler::parseI if (ret.optimiserSettings.runYulOptimiser) solUnimplemented("Optimization is not yet supported with ethdebug."); + if (!ret.experimental) + { + if (ret.language == "SolidityAST" || ret.language == "EVMAssembly") + return formatFatalError(Error::Type::FatalError, "'SolidityAST' and 'EVMAssembly' inputs are experimental, and can only be used by toggling the 'settings.experimental' option."); + + if (ret.evmVersion.isExperimental()) + // TODO: Cover with test when the Amsterdam version is introduced + return formatFatalError(Error::Type::FatalError, fmt::format("EVM version {} is experimental, and can only be used by toggling the 'settings.experimental' option.", ret.evmVersion.name())); + + if (isExperimentalArtifactRequested(ret.outputSelection)) + return formatFatalError(Error::Type::FatalError, "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option."); + + if (ret.eofVersion) + return formatFatalError(Error::Type::FatalError, "'eofVersion' is experimental, and can only be used by toggling the 'settings.experimental' option."); + } + return {std::move(ret)}; } @@ -1238,6 +1281,7 @@ Json StandardCompiler::importEVMAssembly(StandardCompiler::InputsAndSettings _in solAssert(_inputsAndSettings.language == "EVMAssembly"); solAssert(_inputsAndSettings.sources.empty()); solAssert(_inputsAndSettings.jsonSources.size() == 1); + solAssert(_inputsAndSettings.experimental); if (!isBinaryRequested(_inputsAndSettings.outputSelection)) return Json::object(); @@ -1373,6 +1417,7 @@ Json StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inpu compilerStack.setMetadataHash(_inputsAndSettings.metadataHash); compilerStack.selectContracts(pipelineConfig(_inputsAndSettings.outputSelection)); compilerStack.setModelCheckerSettings(_inputsAndSettings.modelCheckerSettings); + compilerStack.setExperimental(_inputsAndSettings.experimental); Json errors = std::move(_inputsAndSettings.errors); @@ -1772,7 +1817,10 @@ Json StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) if (evmArtifactRequested(kind, "linkReferences")) bytecodeJSON["linkReferences"] = formatLinkReferences(selectedObject.bytecode->linkReferences); if (evmArtifactRequested(kind, "ethdebug")) + { + solAssert(_inputsAndSettings.experimental); bytecodeJSON["ethdebug"] = selectedObject.ethdebug; + } if (isDeployed && evmArtifactRequested(kind, "immutableReferences")) bytecodeJSON["immutableReferences"] = formatImmutableReferences(selectedObject.bytecode->immutableReferences); output["contracts"][sourceName][contractName]["evm"][kind] = bytecodeJSON; @@ -1784,11 +1832,16 @@ Json StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "evm.assembly", wildcardMatchesExperimental)) output["contracts"][sourceName][contractName]["evm"]["assembly"] = object.assembly->assemblyString(stack.debugInfoSelection()); if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "yulCFGJson", wildcardMatchesExperimental)) + { + solAssert(_inputsAndSettings.experimental, ""); output["contracts"][sourceName][contractName]["yulCFGJson"] = stack.cfgJson(); + } if (isEthdebugRequested(_inputsAndSettings.outputSelection)) + { + solAssert(_inputsAndSettings.experimental, ""); output["ethdebug"] = evmasm::ethdebug::resources({sourceName}, VersionString); - + } return output; } diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h index 665e8b4871..493e637cd6 100644 --- a/libsolidity/interface/StandardCompiler.h +++ b/libsolidity/interface/StandardCompiler.h @@ -89,6 +89,7 @@ class StandardCompiler Json outputSelection; ModelCheckerSettings modelCheckerSettings = ModelCheckerSettings{}; bool viaIR = false; + bool experimental = false; }; /// Parses the input json (and potentially invokes the read callback) and either returns diff --git a/scripts/ASTImportTest.sh b/scripts/ASTImportTest.sh index 370aab9bd6..3eff305624 100755 --- a/scripts/ASTImportTest.sh +++ b/scripts/ASTImportTest.sh @@ -139,7 +139,7 @@ function test_ast_import_export_equivalence echo ". += {\"sources\":" > _ast_json.json jq .sources expected.json >> _ast_json.json echo "}" >> _ast_json.json - echo "{\"language\": \"SolidityAST\", \"settings\": {\"outputSelection\": {\"*\": {\"\": [\"ast\"]}}}}" > standard_json.json + echo "{\"language\": \"SolidityAST\", \"settings\": {\"experimental\": true, \"outputSelection\": {\"*\": {\"\": [\"ast\"]}}}}" > standard_json.json jq --from-file _ast_json.json standard_json.json > standard_json_input.json # (re)import ast via standard json - and export it again as obtained result (silently) diff --git a/test/cmdlineTests/import_asm_json_standard_json_no_optimize/input.json b/test/cmdlineTests/import_asm_json_standard_json_no_optimize/input.json index d2ba25938f..c21bbe63f8 100644 --- a/test/cmdlineTests/import_asm_json_standard_json_no_optimize/input.json +++ b/test/cmdlineTests/import_asm_json_standard_json_no_optimize/input.json @@ -28,6 +28,7 @@ } }, "settings": { + "experimental": true, "outputSelection": { "*": { "": ["evm.assembly"] diff --git a/test/cmdlineTests/import_asm_json_standard_json_optimize/input.json b/test/cmdlineTests/import_asm_json_standard_json_optimize/input.json index d1d4582595..6cb30f6f5c 100644 --- a/test/cmdlineTests/import_asm_json_standard_json_optimize/input.json +++ b/test/cmdlineTests/import_asm_json_standard_json_optimize/input.json @@ -28,6 +28,7 @@ } }, "settings": { + "experimental": true, "outputSelection": { "*": { "": ["evm.assembly"] diff --git a/test/cmdlineTests/standard_import_asm_json/input.json b/test/cmdlineTests/standard_import_asm_json/input.json index 112f480459..a457d48e2f 100644 --- a/test/cmdlineTests/standard_import_asm_json/input.json +++ b/test/cmdlineTests/standard_import_asm_json/input.json @@ -19,6 +19,7 @@ } }, "settings": { + "experimental": true, "outputSelection": { "*": { "": ["*"] diff --git a/test/cmdlineTests/standard_import_asm_json_immutable_references/input.json b/test/cmdlineTests/standard_import_asm_json_immutable_references/input.json index 0619f8d03d..d193ff8a21 100644 --- a/test/cmdlineTests/standard_import_asm_json_immutable_references/input.json +++ b/test/cmdlineTests/standard_import_asm_json_immutable_references/input.json @@ -34,6 +34,7 @@ } }, "settings": { + "experimental": true, "outputSelection": { "*": { "": [ diff --git a/test/cmdlineTests/standard_import_asm_json_invalid_opcode/input.json b/test/cmdlineTests/standard_import_asm_json_invalid_opcode/input.json index 2e3e22cc41..732b05fd8c 100644 --- a/test/cmdlineTests/standard_import_asm_json_invalid_opcode/input.json +++ b/test/cmdlineTests/standard_import_asm_json_invalid_opcode/input.json @@ -19,6 +19,7 @@ } }, "settings": { + "experimental": true, "outputSelection": { "*": { "": ["evm.bytecode"] diff --git a/test/cmdlineTests/standard_import_asm_json_link_references/input.json b/test/cmdlineTests/standard_import_asm_json_link_references/input.json index 47693fa148..4c5b45c148 100644 --- a/test/cmdlineTests/standard_import_asm_json_link_references/input.json +++ b/test/cmdlineTests/standard_import_asm_json_link_references/input.json @@ -26,6 +26,7 @@ } }, "settings": { + "experimental": true, "outputSelection": { "*": { "": [ diff --git a/test/cmdlineTests/standard_import_asm_json_no_output_selection/input.json b/test/cmdlineTests/standard_import_asm_json_no_output_selection/input.json index 1cd9c3c7d5..ce746eb200 100644 --- a/test/cmdlineTests/standard_import_asm_json_no_output_selection/input.json +++ b/test/cmdlineTests/standard_import_asm_json_no_output_selection/input.json @@ -19,6 +19,7 @@ } }, "settings": { + "experimental": true, "outputSelection": { "*": { "": [ diff --git a/test/cmdlineTests/standard_import_ast/input.json b/test/cmdlineTests/standard_import_ast/input.json index 5ae7a5297d..b33dcb57e3 100644 --- a/test/cmdlineTests/standard_import_ast/input.json +++ b/test/cmdlineTests/standard_import_ast/input.json @@ -83,6 +83,7 @@ } }, "settings": { + "experimental": true, "outputSelection": { "*": { "": [ diff --git a/test/cmdlineTests/standard_import_ast_select_bytecode/input.json b/test/cmdlineTests/standard_import_ast_select_bytecode/input.json index 6458cb7e15..2bddcea9ec 100644 --- a/test/cmdlineTests/standard_import_ast_select_bytecode/input.json +++ b/test/cmdlineTests/standard_import_ast_select_bytecode/input.json @@ -38,6 +38,7 @@ } }, "settings": { + "experimental": true, "outputSelection": { "*": { "*": [ diff --git a/test/cmdlineTests/standard_import_with_comments/input.json b/test/cmdlineTests/standard_import_with_comments/input.json index 4ed88177cf..ef43cc0e11 100644 --- a/test/cmdlineTests/standard_import_with_comments/input.json +++ b/test/cmdlineTests/standard_import_with_comments/input.json @@ -84,6 +84,7 @@ } }, "settings": { + "experimental": true, "outputSelection": { "*": { "": [ diff --git a/test/cmdlineTests/standard_import_with_comments_simple/input.json b/test/cmdlineTests/standard_import_with_comments_simple/input.json index 21faca8c62..3ff3afc65f 100644 --- a/test/cmdlineTests/standard_import_with_comments_simple/input.json +++ b/test/cmdlineTests/standard_import_with_comments_simple/input.json @@ -76,6 +76,7 @@ } }, "settings": { + "experimental": true, "outputSelection": { "*": { "": ["ast"] diff --git a/test/cmdlineTests/standard_irOptimized_ast_requested/input.json b/test/cmdlineTests/standard_irOptimized_ast_requested/input.json index 23f17f4ae4..128724c849 100644 --- a/test/cmdlineTests/standard_irOptimized_ast_requested/input.json +++ b/test/cmdlineTests/standard_irOptimized_ast_requested/input.json @@ -4,6 +4,7 @@ "C": {"urls": ["in.sol"]} }, "settings": { + "experimental": true, "outputSelection": {"*": {"*": ["irOptimizedAst"]}} } } diff --git a/test/cmdlineTests/standard_ir_ast_requested/input.json b/test/cmdlineTests/standard_ir_ast_requested/input.json index 07cfc13ede..8290990453 100644 --- a/test/cmdlineTests/standard_ir_ast_requested/input.json +++ b/test/cmdlineTests/standard_ir_ast_requested/input.json @@ -4,6 +4,7 @@ "C": {"urls": ["in.sol"]} }, "settings": { + "experimental": true, "outputSelection": {"*": {"*": ["irAst"]}} } } diff --git a/test/cmdlineTests/standard_no_experimental_ast_import/input.json b/test/cmdlineTests/standard_no_experimental_ast_import/input.json new file mode 100644 index 0000000000..8ddd5ef8be --- /dev/null +++ b/test/cmdlineTests/standard_no_experimental_ast_import/input.json @@ -0,0 +1,8 @@ +{ + "language": "SolidityAST", + "sources": { + "A.sol": { + "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" + } + } +} diff --git a/test/cmdlineTests/standard_no_experimental_ast_import/output.json b/test/cmdlineTests/standard_no_experimental_ast_import/output.json new file mode 100644 index 0000000000..792d6b4c19 --- /dev/null +++ b/test/cmdlineTests/standard_no_experimental_ast_import/output.json @@ -0,0 +1,11 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "'SolidityAST' and 'EVMAssembly' inputs are experimental, and can only be used by toggling the 'settings.experimental' option.", + "message": "'SolidityAST' and 'EVMAssembly' inputs are experimental, and can only be used by toggling the 'settings.experimental' option.", + "severity": "error", + "type": "FatalError" + } + ] +} diff --git a/test/cmdlineTests/standard_no_experimental_eof/input.json b/test/cmdlineTests/standard_no_experimental_eof/input.json new file mode 100644 index 0000000000..8892caf272 --- /dev/null +++ b/test/cmdlineTests/standard_no_experimental_eof/input.json @@ -0,0 +1,12 @@ +{ + "language": "Solidity", + "sources": { + "A.sol": { + "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" + } + }, + "settings": { + "eofVersion": 1, + "evmVersion": "osaka" + } +} diff --git a/test/cmdlineTests/standard_no_experimental_eof/output.json b/test/cmdlineTests/standard_no_experimental_eof/output.json new file mode 100644 index 0000000000..653fbc85a8 --- /dev/null +++ b/test/cmdlineTests/standard_no_experimental_eof/output.json @@ -0,0 +1,11 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "'eofVersion' is experimental, and can only be used by toggling the 'settings.experimental' option.", + "message": "'eofVersion' is experimental, and can only be used by toggling the 'settings.experimental' option.", + "severity": "error", + "type": "FatalError" + } + ] +} diff --git a/test/cmdlineTests/standard_no_experimental_ethdebug/input.json b/test/cmdlineTests/standard_no_experimental_ethdebug/input.json new file mode 100644 index 0000000000..b11fdac34b --- /dev/null +++ b/test/cmdlineTests/standard_no_experimental_ethdebug/input.json @@ -0,0 +1,19 @@ +{ + "language": "Solidity", + "sources": { + "A.sol": { + "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" + } + }, + "settings": { + "viaIR": true, + "debug": { + "debugInfo": ["ethdebug"] + }, + "outputSelection": { + "A.sol": { + "A": ["ethdebug","evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"] + } + } + } +} diff --git a/test/cmdlineTests/standard_no_experimental_ethdebug/output.json b/test/cmdlineTests/standard_no_experimental_ethdebug/output.json new file mode 100644 index 0000000000..6f132c1cd9 --- /dev/null +++ b/test/cmdlineTests/standard_no_experimental_ethdebug/output.json @@ -0,0 +1,11 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", + "message": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", + "severity": "error", + "type": "FatalError" + } + ] +} diff --git a/test/cmdlineTests/standard_no_experimental_ir_ast_output/input.json b/test/cmdlineTests/standard_no_experimental_ir_ast_output/input.json new file mode 100644 index 0000000000..4b6c7776ef --- /dev/null +++ b/test/cmdlineTests/standard_no_experimental_ir_ast_output/input.json @@ -0,0 +1,15 @@ +{ + "language": "Solidity", + "sources": { + "A.sol": { + "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" + } + }, + "settings": { + "outputSelection": { + "A.sol": { + "A": ["irAst"] + } + } + } +} diff --git a/test/cmdlineTests/standard_no_experimental_ir_ast_output/output.json b/test/cmdlineTests/standard_no_experimental_ir_ast_output/output.json new file mode 100644 index 0000000000..6f132c1cd9 --- /dev/null +++ b/test/cmdlineTests/standard_no_experimental_ir_ast_output/output.json @@ -0,0 +1,11 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", + "message": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", + "severity": "error", + "type": "FatalError" + } + ] +} diff --git a/test/cmdlineTests/standard_no_experimental_yul_cfg_json_export/input.json b/test/cmdlineTests/standard_no_experimental_yul_cfg_json_export/input.json new file mode 100644 index 0000000000..6944eacb7e --- /dev/null +++ b/test/cmdlineTests/standard_no_experimental_yul_cfg_json_export/input.json @@ -0,0 +1,17 @@ +{ + "language": "Yul", + "sources": + { + "A": + { + "content": "{ let x := mload(0) sstore(add(x, 0), 0) }" + } + }, + "settings": { + "optimizer": { + "enabled": true + }, + "viaIR": true, + "outputSelection": {"*": {"*": ["yulCFGJson"]}} + } +} diff --git a/test/cmdlineTests/standard_no_experimental_yul_cfg_json_export/output.json b/test/cmdlineTests/standard_no_experimental_yul_cfg_json_export/output.json new file mode 100644 index 0000000000..6f132c1cd9 --- /dev/null +++ b/test/cmdlineTests/standard_no_experimental_yul_cfg_json_export/output.json @@ -0,0 +1,11 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", + "message": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", + "severity": "error", + "type": "FatalError" + } + ] +} diff --git a/test/cmdlineTests/standard_no_experimental_yul_ethdebug/input.json b/test/cmdlineTests/standard_no_experimental_yul_ethdebug/input.json new file mode 100644 index 0000000000..9754b3d060 --- /dev/null +++ b/test/cmdlineTests/standard_no_experimental_yul_ethdebug/input.json @@ -0,0 +1,23 @@ +{ + "language": "Yul", + "sources": + { + "A": + { + "content": "{ let x := mload(0) sstore(add(x, 0), 0) }" + } + }, + "settings": + { + "viaIR": true, + "debug": { + "debugInfo": [ + "ethdebug" + ] + }, + "outputSelection": + { + "*": { "*": ["evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"], "": [ "*" ] } + } + } +} diff --git a/test/cmdlineTests/standard_no_experimental_yul_ethdebug/output.json b/test/cmdlineTests/standard_no_experimental_yul_ethdebug/output.json new file mode 100644 index 0000000000..6f132c1cd9 --- /dev/null +++ b/test/cmdlineTests/standard_no_experimental_yul_ethdebug/output.json @@ -0,0 +1,11 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", + "message": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", + "severity": "error", + "type": "FatalError" + } + ] +} diff --git a/test/cmdlineTests/standard_no_experimental_yul_ir_ast_requested/input.json b/test/cmdlineTests/standard_no_experimental_yul_ir_ast_requested/input.json new file mode 100644 index 0000000000..ea91a74ebb --- /dev/null +++ b/test/cmdlineTests/standard_no_experimental_yul_ir_ast_requested/input.json @@ -0,0 +1,12 @@ +{ + "language": "Yul", + "sources": { + "A": + { + "content": "{ let x := mload(0) sstore(add(x, 0), 0) }" + } + }, + "settings": { + "outputSelection": {"*": {"*": ["irAst"]}} + } +} diff --git a/test/cmdlineTests/standard_no_experimental_yul_ir_ast_requested/output.json b/test/cmdlineTests/standard_no_experimental_yul_ir_ast_requested/output.json new file mode 100644 index 0000000000..6f132c1cd9 --- /dev/null +++ b/test/cmdlineTests/standard_no_experimental_yul_ir_ast_requested/output.json @@ -0,0 +1,11 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", + "message": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", + "severity": "error", + "type": "FatalError" + } + ] +} diff --git a/test/cmdlineTests/standard_output_debuginfo_ethdebug_compatible/input.json b/test/cmdlineTests/standard_output_debuginfo_ethdebug_compatible/input.json index 422a54f90d..72f16c0ea3 100644 --- a/test/cmdlineTests/standard_output_debuginfo_ethdebug_compatible/input.json +++ b/test/cmdlineTests/standard_output_debuginfo_ethdebug_compatible/input.json @@ -9,6 +9,7 @@ } }, "settings": { + "experimental": true, "viaIR": true, "debug": { "debugInfo": [ diff --git a/test/cmdlineTests/standard_output_debuginfo_ethdebug_with_interfaces_and_abstracts/input.json b/test/cmdlineTests/standard_output_debuginfo_ethdebug_with_interfaces_and_abstracts/input.json index 06b2d86e96..8d85b0ffa1 100644 --- a/test/cmdlineTests/standard_output_debuginfo_ethdebug_with_interfaces_and_abstracts/input.json +++ b/test/cmdlineTests/standard_output_debuginfo_ethdebug_with_interfaces_and_abstracts/input.json @@ -9,6 +9,7 @@ } }, "settings": { + "experimental": true, "viaIR": true, "debug": { "debugInfo": [ diff --git a/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_and_deployedbytecode_ir/input.json b/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_and_deployedbytecode_ir/input.json index 8ed44ca05d..2fd0aecf8f 100644 --- a/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_and_deployedbytecode_ir/input.json +++ b/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_and_deployedbytecode_ir/input.json @@ -9,6 +9,7 @@ } }, "settings": { + "experimental": true, "viaIR": true, "optimizer": { "enabled": false diff --git a/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_and_deployedbytecode_iroptimized/input.json b/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_and_deployedbytecode_iroptimized/input.json index 27c400f9e9..58fb7b42fb 100644 --- a/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_and_deployedbytecode_iroptimized/input.json +++ b/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_and_deployedbytecode_iroptimized/input.json @@ -9,6 +9,7 @@ } }, "settings": { + "experimental": true, "viaIR": true, "optimizer": { "enabled": false diff --git a/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_ir/input.json b/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_ir/input.json index 35c8f940e8..292c86e375 100644 --- a/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_ir/input.json +++ b/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_ir/input.json @@ -9,6 +9,7 @@ } }, "settings": { + "experimental": true, "viaIR": true, "optimizer": { "enabled": false diff --git a/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_iroptimized/input.json b/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_iroptimized/input.json index e4681967f7..2e4bb8ec92 100644 --- a/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_iroptimized/input.json +++ b/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_iroptimized/input.json @@ -9,6 +9,7 @@ } }, "settings": { + "experimental": true, "viaIR": true, "optimizer": { "enabled": false diff --git a/test/cmdlineTests/standard_output_selection_ethdebug_deployedbytecode_ir/input.json b/test/cmdlineTests/standard_output_selection_ethdebug_deployedbytecode_ir/input.json index 6239a7275e..cd6a3d15bb 100644 --- a/test/cmdlineTests/standard_output_selection_ethdebug_deployedbytecode_ir/input.json +++ b/test/cmdlineTests/standard_output_selection_ethdebug_deployedbytecode_ir/input.json @@ -9,6 +9,7 @@ } }, "settings": { + "experimental": true, "viaIR": true, "optimizer": { "enabled": false diff --git a/test/cmdlineTests/standard_output_selection_ethdebug_deployedbytecode_iroptimized/input.json b/test/cmdlineTests/standard_output_selection_ethdebug_deployedbytecode_iroptimized/input.json index fdb6c95b81..d9a5104f46 100644 --- a/test/cmdlineTests/standard_output_selection_ethdebug_deployedbytecode_iroptimized/input.json +++ b/test/cmdlineTests/standard_output_selection_ethdebug_deployedbytecode_iroptimized/input.json @@ -9,6 +9,7 @@ } }, "settings": { + "experimental": true, "viaIR": true, "optimizer": { "enabled": false diff --git a/test/cmdlineTests/standard_output_selection_ethdebug_ir/input.json b/test/cmdlineTests/standard_output_selection_ethdebug_ir/input.json index 5bc9d54673..f601332693 100644 --- a/test/cmdlineTests/standard_output_selection_ethdebug_ir/input.json +++ b/test/cmdlineTests/standard_output_selection_ethdebug_ir/input.json @@ -9,6 +9,7 @@ } }, "settings": { + "experimental": true, "viaIR": true, "outputSelection": { "*": { diff --git a/test/cmdlineTests/standard_output_selection_ethdebug_iroptimized/input.json b/test/cmdlineTests/standard_output_selection_ethdebug_iroptimized/input.json index 8952a241f6..fb1e11bbcb 100644 --- a/test/cmdlineTests/standard_output_selection_ethdebug_iroptimized/input.json +++ b/test/cmdlineTests/standard_output_selection_ethdebug_iroptimized/input.json @@ -9,6 +9,7 @@ } }, "settings": { + "experimental": true, "viaIR": true, "outputSelection": { "*": { diff --git a/test/cmdlineTests/standard_undeployable_contract_all_outputs/input.json b/test/cmdlineTests/standard_undeployable_contract_all_outputs/input.json index ecb97bef2c..7c56ed8517 100644 --- a/test/cmdlineTests/standard_undeployable_contract_all_outputs/input.json +++ b/test/cmdlineTests/standard_undeployable_contract_all_outputs/input.json @@ -4,6 +4,7 @@ "C": {"urls": ["in.sol"]} }, "settings": { + "experimental": true, "outputSelection": { "*": { "*": ["*", "ir", "irAst", "irOptimized", "irOptimizedAst"] diff --git a/test/cmdlineTests/standard_yul_cfg_json_export/input.json b/test/cmdlineTests/standard_yul_cfg_json_export/input.json index b5353721ee..24c3126a3d 100644 --- a/test/cmdlineTests/standard_yul_cfg_json_export/input.json +++ b/test/cmdlineTests/standard_yul_cfg_json_export/input.json @@ -4,6 +4,7 @@ "C": {"urls": ["in.sol"]} }, "settings": { + "experimental": true, "optimizer": { "enabled": true }, diff --git a/test/cmdlineTests/standard_yul_debug_info_ethdebug_compatible_output/input.json b/test/cmdlineTests/standard_yul_debug_info_ethdebug_compatible_output/input.json index e52867fa32..755d770fe6 100644 --- a/test/cmdlineTests/standard_yul_debug_info_ethdebug_compatible_output/input.json +++ b/test/cmdlineTests/standard_yul_debug_info_ethdebug_compatible_output/input.json @@ -8,6 +8,7 @@ } }, "settings": { + "experimental": true, "debug": {"debugInfo": ["ethdebug"]}, "outputSelection": { "*": {"*": ["ir", "irOptimized", "evm.bytecode.ethdebug"]} diff --git a/test/cmdlineTests/standard_yul_debug_info_ethdebug_compatible_output_eof/input.json b/test/cmdlineTests/standard_yul_debug_info_ethdebug_compatible_output_eof/input.json index a8c3436c1a..b83ec361c1 100644 --- a/test/cmdlineTests/standard_yul_debug_info_ethdebug_compatible_output_eof/input.json +++ b/test/cmdlineTests/standard_yul_debug_info_ethdebug_compatible_output_eof/input.json @@ -8,6 +8,7 @@ } }, "settings": { + "experimental": true, "eofVersion": 1, "evmVersion": "osaka", "debug": {"debugInfo": ["ethdebug"]}, diff --git a/test/cmdlineTests/standard_yul_debug_info_ethdebug_verbatim_unimplemented/input.json b/test/cmdlineTests/standard_yul_debug_info_ethdebug_verbatim_unimplemented/input.json index 16ef6ef33f..eac38e3db6 100644 --- a/test/cmdlineTests/standard_yul_debug_info_ethdebug_verbatim_unimplemented/input.json +++ b/test/cmdlineTests/standard_yul_debug_info_ethdebug_verbatim_unimplemented/input.json @@ -8,6 +8,7 @@ } }, "settings": { + "experimental": true, "debug": {"debugInfo": ["ethdebug"]}, "outputSelection": { "*": {"*": ["evm.bytecode.ethdebug"]} diff --git a/test/cmdlineTests/standard_yul_ethdebug_assign_immutable/input.json b/test/cmdlineTests/standard_yul_ethdebug_assign_immutable/input.json index 652620bedd..89127b977c 100644 --- a/test/cmdlineTests/standard_yul_ethdebug_assign_immutable/input.json +++ b/test/cmdlineTests/standard_yul_ethdebug_assign_immutable/input.json @@ -4,6 +4,7 @@ "C": {"urls": ["in.yul"]} }, "settings": { + "experimental": true, "outputSelection": { "*": {"*": ["evm.bytecode.ethdebug"]} } diff --git a/test/cmdlineTests/standard_yul_ethdebug_bytecode_ir/input.json b/test/cmdlineTests/standard_yul_ethdebug_bytecode_ir/input.json index 67c5fedc7f..9d5829df07 100644 --- a/test/cmdlineTests/standard_yul_ethdebug_bytecode_ir/input.json +++ b/test/cmdlineTests/standard_yul_ethdebug_bytecode_ir/input.json @@ -4,6 +4,7 @@ "C": {"urls": ["in.yul"]} }, "settings": { + "experimental": true, "outputSelection": { "*": {"*": ["evm.bytecode.ethdebug", "ir"]} } diff --git a/test/cmdlineTests/standard_yul_ethdebug_bytecode_iroptimized/input.json b/test/cmdlineTests/standard_yul_ethdebug_bytecode_iroptimized/input.json index 17949761f0..da8378a8b6 100644 --- a/test/cmdlineTests/standard_yul_ethdebug_bytecode_iroptimized/input.json +++ b/test/cmdlineTests/standard_yul_ethdebug_bytecode_iroptimized/input.json @@ -4,6 +4,7 @@ "C": {"urls": ["in.yul"]} }, "settings": { + "experimental": true, "outputSelection": { "*": {"*": ["evm.bytecode.ethdebug", "irOptimized"]} } diff --git a/test/cmdlineTests/standard_yul_ethdebug_deployed_bytecode/input.json b/test/cmdlineTests/standard_yul_ethdebug_deployed_bytecode/input.json index 5c5e9c5f76..fcfcaf3bd9 100644 --- a/test/cmdlineTests/standard_yul_ethdebug_deployed_bytecode/input.json +++ b/test/cmdlineTests/standard_yul_ethdebug_deployed_bytecode/input.json @@ -4,6 +4,7 @@ "C": {"urls": ["in.yul"]} }, "settings": { + "experimental": true, "outputSelection": { "*": {"*": ["evm.deployedBytecode.ethdebug"]} } diff --git a/test/cmdlineTests/standard_yul_ethdebug_deployed_bytecode_and_bytecode/input.json b/test/cmdlineTests/standard_yul_ethdebug_deployed_bytecode_and_bytecode/input.json index 655def5c1a..2bedd407b3 100644 --- a/test/cmdlineTests/standard_yul_ethdebug_deployed_bytecode_and_bytecode/input.json +++ b/test/cmdlineTests/standard_yul_ethdebug_deployed_bytecode_and_bytecode/input.json @@ -4,6 +4,7 @@ "C": {"urls": ["in.yul"]} }, "settings": { + "experimental": true, "outputSelection": { "*": {"*": ["evm.deployedBytecode.ethdebug", "evm.bytecode.ethdebug"]} } diff --git a/test/cmdlineTests/standard_yul_ethdebug_eof/input.json b/test/cmdlineTests/standard_yul_ethdebug_eof/input.json index 4e33c42670..3455e61d7c 100644 --- a/test/cmdlineTests/standard_yul_ethdebug_eof/input.json +++ b/test/cmdlineTests/standard_yul_ethdebug_eof/input.json @@ -4,6 +4,7 @@ "C": {"urls": ["in.yul"]} }, "settings": { + "experimental": true, "eofVersion": 1, "evmVersion": "osaka", "outputSelection": { diff --git a/test/cmdlineTests/standard_yul_ethdebug_ir/input.json b/test/cmdlineTests/standard_yul_ethdebug_ir/input.json index 67c5fedc7f..9d5829df07 100644 --- a/test/cmdlineTests/standard_yul_ethdebug_ir/input.json +++ b/test/cmdlineTests/standard_yul_ethdebug_ir/input.json @@ -4,6 +4,7 @@ "C": {"urls": ["in.yul"]} }, "settings": { + "experimental": true, "outputSelection": { "*": {"*": ["evm.bytecode.ethdebug", "ir"]} } diff --git a/test/cmdlineTests/standard_yul_ethdebug_iroptimize/input.json b/test/cmdlineTests/standard_yul_ethdebug_iroptimize/input.json index b08c9dcd72..2d3610908a 100644 --- a/test/cmdlineTests/standard_yul_ethdebug_iroptimize/input.json +++ b/test/cmdlineTests/standard_yul_ethdebug_iroptimize/input.json @@ -4,6 +4,7 @@ "C": {"urls": ["in.yul"]} }, "settings": { + "experimental": true, "outputSelection": { "*": {"*": ["evm.bytecode.ethdebug", "irOptimize"]} } diff --git a/test/ethdebugSchemaTests/input_file.json b/test/ethdebugSchemaTests/input_file.json index 7daf7afd68..31c06ba8bf 100644 --- a/test/ethdebugSchemaTests/input_file.json +++ b/test/ethdebugSchemaTests/input_file.json @@ -9,6 +9,7 @@ } }, "settings": { + "experimental": true, "viaIR": true, "debug": { "debugInfo": [ diff --git a/test/ethdebugSchemaTests/input_file_eof.json b/test/ethdebugSchemaTests/input_file_eof.json index 0462065327..df0c734c10 100644 --- a/test/ethdebugSchemaTests/input_file_eof.json +++ b/test/ethdebugSchemaTests/input_file_eof.json @@ -9,6 +9,7 @@ } }, "settings": { + "experimental": true, "eofVersion": 1, "evmVersion": "osaka", "viaIR": true, diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 7be21863ab..cffddcfd0e 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -297,6 +297,13 @@ Json generateStandardJson(bool _viaIr, Json const& _debugInfoSelection, Json con return result; } +Json generateExperimentalStandardJson(bool _viaIr, Json const& _debugInfoSelection, Json const& _outputSelection, Code const& _code = SolidityCode(), bool _advancedOutputSelection = false) +{ + Json result = generateStandardJson(_viaIr, _debugInfoSelection, _outputSelection, _code, _advancedOutputSelection); + result["settings"]["experimental"] = true; + return result; +} + } // end anonymous namespace BOOST_AUTO_TEST_SUITE(StandardCompiler) @@ -1926,97 +1933,97 @@ BOOST_AUTO_TEST_CASE(ethdebug_debug_info_ethdebug) { static std::vector>>> tests{ { - generateStandardJson(false, Json::array({"ethdebug"}), Json::array({"*"})), + generateExperimentalStandardJson(false, Json::array({"ethdebug"}), Json::array({"*"})), std::nullopt, }, { - generateStandardJson(true, Json::array({"ethdebug"}), Json::array({"*"})), + generateExperimentalStandardJson(true, Json::array({"ethdebug"}), Json::array({"*"})), std::nullopt, }, { - generateStandardJson(false, Json::array({"ethdebug"}), Json::array({"evm.bytecode.ethdebug"})), + generateExperimentalStandardJson(false, Json::array({"ethdebug"}), Json::array({"evm.bytecode.ethdebug"})), std::nullopt, }, { - generateStandardJson(false, Json::array({"ethdebug"}), Json::array({"evm.deployedBytecode.ethdebug"})), + generateExperimentalStandardJson(false, Json::array({"ethdebug"}), Json::array({"evm.deployedBytecode.ethdebug"})), std::nullopt, }, { - generateStandardJson(false, Json::array({"ethdebug"}), Json::array({"evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"})), + generateExperimentalStandardJson(false, Json::array({"ethdebug"}), Json::array({"evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"})), std::nullopt, }, { - generateStandardJson(false, Json::array({"ethdebug"}), Json::array({"irOptimized"})), + generateExperimentalStandardJson(false, Json::array({"ethdebug"}), Json::array({"irOptimized"})), [](const Json& result) { return result.dump().find("/// ethdebug: enabled") != std::string::npos; } }, { - generateStandardJson(false, Json::array({"ethdebug"}), Json::array({"irOptimized"})), + generateExperimentalStandardJson(false, Json::array({"ethdebug"}), Json::array({"irOptimized"})), [](const Json& result) { return result.dump().find("/// ethdebug: enabled") != std::string::npos; } }, { - generateStandardJson(true, {}, Json::array({"irOptimized", "evm.bytecode.ethdebug"})), + generateExperimentalStandardJson(true, {}, Json::array({"irOptimized", "evm.bytecode.ethdebug"})), [](const Json& result) { return result.dump().find("/// ethdebug: enabled") != std::string::npos; } }, { - generateStandardJson(true, {}, Json::array({"irOptimized", "evm.deployedBytecode.ethdebug"})), + generateExperimentalStandardJson(true, {}, Json::array({"irOptimized", "evm.deployedBytecode.ethdebug"})), [](const Json& result) { return result.dump().find("/// ethdebug: enabled") != std::string::npos; } }, { - generateStandardJson(true, {}, Json::array({"irOptimized", "evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"})), + generateExperimentalStandardJson(true, {}, Json::array({"irOptimized", "evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"})), [](const Json& result) { return result.dump().find("/// ethdebug: enabled") != std::string::npos; } }, { - generateStandardJson(true, {}, Json::array({"irOptimized", "evm.bytecode.ethdebug"})), + generateExperimentalStandardJson(true, {}, Json::array({"irOptimized", "evm.bytecode.ethdebug"})), [](const Json& result) { return result.dump().find("/// ethdebug: enabled") != std::string::npos; } }, { - generateStandardJson(true, {}, Json::array({"irOptimized", "evm.deployedBytecode.ethdebug"})), + generateExperimentalStandardJson(true, {}, Json::array({"irOptimized", "evm.deployedBytecode.ethdebug"})), [](const Json& result) { return result.dump().find("/// ethdebug: enabled") != std::string::npos; } }, { - generateStandardJson(true, {}, Json::array({"irOptimized", "evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"})), + generateExperimentalStandardJson(true, {}, Json::array({"irOptimized", "evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"})), [](const Json& result) { return result.dump().find("/// ethdebug: enabled") != std::string::npos; } }, { - generateStandardJson(true, Json::array({"ethdebug"}), Json::array({"irOptimized"}), YulCode()), + generateExperimentalStandardJson(true, Json::array({"ethdebug"}), Json::array({"irOptimized"}), YulCode()), [](const Json& result) { return result.dump().find("/// ethdebug: enabled") != std::string::npos; } }, { - generateStandardJson(true, Json::array({"ethdebug"}), Json::array({"irOptimized"}), YulCode()), + generateExperimentalStandardJson(true, Json::array({"ethdebug"}), Json::array({"irOptimized"}), YulCode()), {} }, { - generateStandardJson(true, Json::array({"ethdebugs"}), Json::array({"irOptimized"}), YulCode()), {} + generateExperimentalStandardJson(true, Json::array({"ethdebugs"}), Json::array({"irOptimized"}), YulCode()), {} }, { - generateStandardJson( + generateExperimentalStandardJson( true, Json::array({"ethdebug"}), { {"fileA", {{"contractA", Json::array({"evm.deployedBytecode.bin"})}}}, {"fileB", {{"contractB", Json::array({"evm.bytecode.bin"})}}} @@ -2029,11 +2036,11 @@ BOOST_AUTO_TEST_CASE(ethdebug_debug_info_ethdebug) std::nullopt, }, { - generateStandardJson(true, Json::array({"ethdebug"}), Json::array({"*"}), EvmAssemblyCode()), + generateExperimentalStandardJson(true, Json::array({"ethdebug"}), Json::array({"*"}), EvmAssemblyCode()), std::nullopt, }, { - generateStandardJson(true, Json::array({"ethdebug"}), Json::array({"*"}), SolidityAstCode()), + generateExperimentalStandardJson(true, Json::array({"ethdebug"}), Json::array({"*"}), SolidityAstCode()), std::nullopt, }, }; @@ -2050,57 +2057,57 @@ BOOST_AUTO_TEST_CASE(ethdebug_ethdebug_output) { static std::vector>>> tests{ { - generateStandardJson(false, Json::array({"ethdebug"}), Json::array({"evm.bytecode.ethdebug"})), + generateExperimentalStandardJson(false, Json::array({"ethdebug"}), Json::array({"evm.bytecode.ethdebug"})), std::nullopt }, { - generateStandardJson(false, {}, Json::array({"evm.bytecode.ethdebug"})), + generateExperimentalStandardJson(false, {}, Json::array({"evm.bytecode.ethdebug"})), std::nullopt }, { - generateStandardJson(false, Json::array({"ethdebug"}), Json::array({"evm.deployedBytecode.ethdebug"})), + generateExperimentalStandardJson(false, Json::array({"ethdebug"}), Json::array({"evm.deployedBytecode.ethdebug"})), std::nullopt }, { - generateStandardJson(false, {}, Json::array({"evm.deployedBytecode.ethdebug"})), + generateExperimentalStandardJson(false, {}, Json::array({"evm.deployedBytecode.ethdebug"})), std::nullopt }, { - generateStandardJson(false, Json::array({"ethdebug"}), Json::array({"evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"})), + generateExperimentalStandardJson(false, Json::array({"ethdebug"}), Json::array({"evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"})), std::nullopt }, { - generateStandardJson(false, {}, Json::array({"evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"})), + generateExperimentalStandardJson(false, {}, Json::array({"evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"})), std::nullopt }, { - generateStandardJson(true, Json::array({"location"}), Json::array({"evm.bytecode.ethdebug"})), + generateExperimentalStandardJson(true, Json::array({"location"}), Json::array({"evm.bytecode.ethdebug"})), std::nullopt }, { - generateStandardJson(true, Json::array({"location"}), Json::array({"evm.deployedBytecode.ethdebug"})), + generateExperimentalStandardJson(true, Json::array({"location"}), Json::array({"evm.deployedBytecode.ethdebug"})), std::nullopt }, { - generateStandardJson(true, Json::array({"location"}), Json::array({"evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"})), + generateExperimentalStandardJson(true, Json::array({"location"}), Json::array({"evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"})), std::nullopt }, { - generateStandardJson(true, Json::array({"ethdebug"}), Json::array({"evm.bytecode.ethdebug"})), + generateExperimentalStandardJson(true, Json::array({"ethdebug"}), Json::array({"evm.bytecode.ethdebug"})), [](const Json& result) { return result.contains("ethdebug") && result["contracts"]["fileA"]["C"]["evm"]["bytecode"].contains("ethdebug"); } }, { - generateStandardJson(true, Json::array({"ethdebug"}), Json::array({"evm.deployedBytecode.ethdebug"})), + generateExperimentalStandardJson(true, Json::array({"ethdebug"}), Json::array({"evm.deployedBytecode.ethdebug"})), [](const Json& result) { return result.contains("ethdebug") && result["contracts"]["fileA"]["C"]["evm"]["deployedBytecode"].contains("ethdebug"); } }, { - generateStandardJson(true, Json::array({"ethdebug"}), Json::array({"evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"})), + generateExperimentalStandardJson(true, Json::array({"ethdebug"}), Json::array({"evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"})), [](const Json& result) { return result.contains("ethdebug") && result["contracts"]["fileA"]["C"]["evm"]["deployedBytecode"].contains("ethdebug") && @@ -2108,21 +2115,21 @@ BOOST_AUTO_TEST_CASE(ethdebug_ethdebug_output) } }, { - generateStandardJson(true, {}, Json::array({"evm.bytecode.ethdebug"})), + generateExperimentalStandardJson(true, {}, Json::array({"evm.bytecode.ethdebug"})), [](const Json& result) { return result.contains("ethdebug") && result["contracts"]["fileA"]["C"]["evm"]["bytecode"].contains("ethdebug"); } }, { - generateStandardJson(true, {}, Json::array({"evm.deployedBytecode.ethdebug"})), + generateExperimentalStandardJson(true, {}, Json::array({"evm.deployedBytecode.ethdebug"})), [](const Json& result) { return result.contains("ethdebug") && result["contracts"]["fileA"]["C"]["evm"]["deployedBytecode"].contains("ethdebug"); } }, { - generateStandardJson(true, {}, Json::array({"evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"})), + generateExperimentalStandardJson(true, {}, Json::array({"evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"})), [](const Json& result) { return result.contains("ethdebug") && result["contracts"]["fileA"]["C"]["evm"]["deployedBytecode"].contains("ethdebug") && @@ -2130,29 +2137,29 @@ BOOST_AUTO_TEST_CASE(ethdebug_ethdebug_output) } }, { - generateStandardJson(true, {}, Json::array({"evm.bytecode.ethdebug", "ir"})), + generateExperimentalStandardJson(true, {}, Json::array({"evm.bytecode.ethdebug", "ir"})), [](const Json& result) { return result.dump().find("/// ethdebug: enabled") != std::string::npos && result.contains("ethdebug") && result["contracts"]["fileA"]["C"]["evm"]["bytecode"].contains("ethdebug"); } }, { - generateStandardJson(true, {}, Json::array({"evm.deployedBytecode.ethdebug", "ir"})), + generateExperimentalStandardJson(true, {}, Json::array({"evm.deployedBytecode.ethdebug", "ir"})), [](const Json& result) { return result.dump().find("/// ethdebug: enabled") != std::string::npos && result.contains("ethdebug") && result["contracts"]["fileA"]["C"]["evm"]["deployedBytecode"].contains("ethdebug"); } }, { - generateStandardJson(true, {}, Json::array({"evm.bytecode.ethdebugs"})), + generateExperimentalStandardJson(true, {}, Json::array({"evm.bytecode.ethdebugs"})), std::nullopt }, { - generateStandardJson(true, {}, Json::array({"evm.deployedBytecode.ethdebugs"})), + generateExperimentalStandardJson(true, {}, Json::array({"evm.deployedBytecode.ethdebugs"})), std::nullopt }, { - generateStandardJson(true, {}, Json::array({"evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug", "ir"})), + generateExperimentalStandardJson(true, {}, Json::array({"evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug", "ir"})), [](const Json& result) { return result.dump().find("/// ethdebug: enabled") != std::string::npos && result.contains("ethdebug") && result["contracts"]["fileA"]["C"]["evm"]["deployedBytecode"].contains("ethdebug") && @@ -2160,36 +2167,36 @@ BOOST_AUTO_TEST_CASE(ethdebug_ethdebug_output) } }, { - generateStandardJson(true, {}, Json::array({"evm.bytecode.ethdebug", "ir"}), YulCode()), + generateExperimentalStandardJson(true, {}, Json::array({"evm.bytecode.ethdebug", "ir"}), YulCode()), [](const Json& result) { return result.dump().find("/// ethdebug: enabled") != std::string::npos && result["contracts"]["fileA"]["object"]["evm"]["bytecode"].contains("ethdebug"); } }, { - generateStandardJson(true, {}, Json::array({"evm.deployedBytecode.ethdebug", "ir"}), YulCode()), + generateExperimentalStandardJson(true, {}, Json::array({"evm.deployedBytecode.ethdebug", "ir"}), YulCode()), std::nullopt }, { - generateStandardJson(true, {}, Json::array({"evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug", "ir"}), YulCode()), + generateExperimentalStandardJson(true, {}, Json::array({"evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug", "ir"}), YulCode()), std::nullopt }, { - generateStandardJson(true, {}, Json::array({"evm.bytecode"})), + generateExperimentalStandardJson(true, {}, Json::array({"evm.bytecode"})), [](const Json& result) { return result.dump().find("ethdebug") == std::string::npos; } }, { - generateStandardJson(true, {}, Json::array({"evm.deployedBytecode"})), + generateExperimentalStandardJson(true, {}, Json::array({"evm.deployedBytecode"})), [](const Json& result) { return result.dump().find("ethdebug") == std::string::npos; } }, { - generateStandardJson( + generateExperimentalStandardJson( true, {}, { {"fileA", {{"contractA", Json::array({"evm.deployedBytecode.ethdebug"})}}}, {"fileB", {{"contractB", Json::array({"evm.bytecode.ethdebug"})}}} @@ -2219,7 +2226,7 @@ BOOST_AUTO_TEST_CASE(ethdebug_ethdebug_output) BOOST_DATA_TEST_CASE(ethdebug_output_instructions_smoketest, boost::unit_test::data::make({"deployedBytecode", "bytecode"}), bytecodeType) { frontend::StandardCompiler compiler; - Json result = compiler.compile(generateStandardJson(true, {}, Json::array({std::string("evm.") + bytecodeType + ".ethdebug"}))); + Json result = compiler.compile(generateExperimentalStandardJson(true, {}, Json::array({std::string("evm.") + bytecodeType + ".ethdebug"}))); BOOST_REQUIRE(result["contracts"]["fileA"]["C"]["evm"][bytecodeType].contains("ethdebug")); bool creation = std::string(bytecodeType) == "bytecode"; Json ethdebugInstructionsToCheck = result["contracts"]["fileA"]["C"]["evm"][bytecodeType]["ethdebug"]; @@ -2259,6 +2266,222 @@ BOOST_DATA_TEST_CASE(ethdebug_output_instructions_smoketest, boost::unit_test::d } } +BOOST_AUTO_TEST_CASE(no_experimental_import_ast_solidity_evmasm) +{ + frontend::StandardCompiler compiler; + { + char const* input = R"( + { + "language": "SolidityAST", + "sources": { + "A.sol": { + "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" + } + } + } + )"; + + Json parsedInput; + BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput)); + Json result = compiler.compile(parsedInput); + BOOST_CHECK(containsError(result, "FatalError", "'SolidityAST' and 'EVMAssembly' inputs are experimental, and can only be used by toggling the 'settings.experimental' option.")); + } + { + char const* input = R"( + { + "language": "SolidityAST", + "sources": { + "A": { + "assemblyJson": { + ".code": [ + { "name": "PUSH", "value": "0" } + ] + } + } + } + } + )"; + + Json parsedInput; + BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput)); + Json result = compiler.compile(parsedInput); + BOOST_CHECK(containsError(result, "FatalError", "'SolidityAST' and 'EVMAssembly' inputs are experimental, and can only be used by toggling the 'settings.experimental' option.")); + } +} + +BOOST_AUTO_TEST_CASE(no_experimental_invalid_output_selection) +{ + frontend::StandardCompiler compiler; + { + char const* input = R"( + { + "language": "Solidity", + "sources": { + "A.sol": { + "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" + } + }, + "settings": { + "outputSelection": { + "A.sol": { + "A": ["irAst"] + } + } + } + } + )"; + + Json parsedInput; + BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput)); + Json result = compiler.compile(parsedInput); + BOOST_CHECK( + containsError( + result, + "FatalError", "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option." + ) + ); + } + { + char const* input = R"( + { + "language": "Solidity", + "sources": { + "A.sol": { + "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" + } + }, + "settings": { + "outputSelection": { + "A.sol": { + "A": ["irOptimizedAst"] + } + } + } + } + )"; + + Json parsedInput; + BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput)); + Json result = compiler.compile(parsedInput); + BOOST_CHECK( + containsError( + result, + "FatalError", "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option." + ) + ); + } + { + char const* input = R"( + { + "language": "Solidity", + "sources": { + "A.sol": { + "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" + } + }, + "settings": { + "outputSelection": { + "A.sol": { + "A": ["yulCFGJson"] + } + } + } + } + )"; + + Json parsedInput; + BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput)); + Json result = compiler.compile(parsedInput); + BOOST_CHECK( + containsError( + result, + "FatalError", "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option." + ) + ); } +} + +BOOST_AUTO_TEST_CASE(no_experimental_ethdebug) +{ + frontend::StandardCompiler compiler; + char const* input = R"( + { + "language": "Solidity", + "sources": { + "A.sol": { + "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" + } + }, + "settings": { + "viaIR": true, + "debug": { + "debugInfo": ["ethdebug"] + }, + "outputSelection": { + "A.sol": { + "A": ["evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"] + } + } + } + } + )"; + + Json parsedInput; + BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput)); + Json result = compiler.compile(parsedInput); + BOOST_CHECK( + containsError( + result, + "FatalError", "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option." + ) + );} + +BOOST_AUTO_TEST_CASE(no_experimental_eof) +{ + frontend::StandardCompiler compiler; + char const* input = R"( + { + "language": "Solidity", + "sources": { + "A.sol": { + "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" + } + }, + "settings": { + "eofVersion": 1, + "evmVersion": "osaka" + } + } + )"; + + Json parsedInput; + BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput)); + Json result = compiler.compile(parsedInput); + BOOST_CHECK(containsError(result, "FatalError", "'eofVersion' is experimental, and can only be used by toggling the 'settings.experimental' option.")); +} + +BOOST_AUTO_TEST_CASE(experimental_non_boolean) +{ + frontend::StandardCompiler compiler; + char const* input = R"( + { + "language": "Solidity", + "sources": { + "A.sol": { + "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" + } + }, + "settings": { + "experimental": 1 + } + } + )"; + + Json parsedInput; + BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput)); + Json result = compiler.compile(parsedInput); + BOOST_CHECK(containsError(result, "JSONError", "\"settings.experimental\" must be a Boolean.")); +} + BOOST_AUTO_TEST_SUITE_END() } // end namespaces From 65a1e53235ab84b82ee8a03e1c118c9e8a8506a6 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 14 Jan 2026 20:21:26 +0100 Subject: [PATCH 213/243] Documentation for experimental mode --- docs/using-the-compiler.rst | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index dbc7970794..a1ca2e0665 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -281,6 +281,8 @@ Input Description "stopAfter": "parsing", // Optional: List of remappings "remappings": [ ":g=/dir" ], + // Optional: Experimental mode toggle (Default: false) + "experimental": false, // Optional: Optimizer settings "optimizer": { // Turn on the optimizer. Optional. Default: false. @@ -696,3 +698,42 @@ Error Types 13. ``YulException``: Error during Yul code generation - this should be reported as an issue. 14. ``Warning``: A warning, which didn't stop the compilation, but should be addressed if possible. 15. ``Info``: Information that the compiler thinks the user might find useful, but is not dangerous and does not necessarily need to be addressed. + +.. index:: ! Experimental mode, ! --experimental +.. _experimental-mode: + +Experimental Mode +***************** + +Experimental mode has been introduced as of version 0.8.34 as an additional safeguard when using experimental features. +Prior to version 0.8.34, all experimental features not requiring an explicit ``pragma experimental ...`` in source file(s) +were freely available to be used without any additional safeguards. Given that experimental features are sparsely documented, +if at all, often not adequately tested, and thus not intended for production use, we decided to add the ``--experimental`` +flag to the command line interface, and the analogous ``settings.experimental`` boolean setting to the Standard JSON interface. +Going forward, usage of any experimental feature will require the experimental mode to be toggled. + +The table below details all currently available experimental features. + ++-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ +| Feature | ID | Affects bytecode | Flag/pragma | ++=======================+==========================+==================+===================================================================+ +| AST import | ``ast-import`` | yes | ``--import-ast`` | ++-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ +| LSP | ``lsp`` | yes | ``--lsp`` | ++-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ +| EVM Assembly import | ``evmasm-import`` | yes | ``--import-asm-json`` | ++-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ +| Generic Solidity | ``generic-solidity`` | yes | ``pragma experimental solidity`` | ++-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ +| IR AST | ``ir-ast`` | no | ``--ir-ast-json``, ``--ir-optimized-ast-json`` | ++-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ +| EOF | ``eof`` | yes | ``--experimental-eof-version`` | ++-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ +| Osaka EVM | ``evm-osaka`` | yes | ``--evm-version osaka`` | ++-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ +| Ethdebug | ``ethdebug`` | no | ``--ethdebug``, ``--ethdebug-runtime``, ``--debug-info ethdebug`` | ++-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ +| Yul SSA CFG exporter | ``yul-ssa-cfg-exporter`` | no | ``--yul-cfg-json`` | ++-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ +| SSA CFG codegen | ``ssa-cfg-codegen`` | yes | ``--ssa-cfg-codegen`` (not yet on ``develop``) | ++-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ From d9fc8bec0eebb73bfb0a53eff9ab6bf1973cda4e Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 14 Jan 2026 20:31:31 +0100 Subject: [PATCH 214/243] Changelog --- Changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changelog.md b/Changelog.md index ed752bf353..823d346c76 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,10 @@ Language Features: Compiler Features: +* Commandline Interface: Introduce `--experimental` flag required for enabling the experimental mode. +* General: Restrict the existing experimental features (`generic-solidity`, `lsp`, `ethdebug`, `eof`, `evm`, `ast-import`, `evmasm-import`, `ir-ast`, `ssa-cfg`) to experimental mode. +* Metadata: Store the state of the experimental mode in JSON and CBOR metadata. In CBOR this broadens the meaning of the existing `experimental` field, which used to indicate only the presence of certain experimental pragmas in the source. +* Standard JSON Interface: Introduce `settings.experimental` setting required for enabling the experimental mode. Bugfixes: From 23002f01c45f4a8d85c2e4b690a9115172d2f3c5 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 21 Jan 2026 11:53:58 +0100 Subject: [PATCH 215/243] Add experimental mode to metadata --- docs/metadata.rst | 11 +- docs/using-the-compiler.rst | 41 +- libsolidity/interface/CompilerStack.cpp | 19 +- test/cmdlineTests/metadata_experimental/args | 1 + .../metadata_experimental/input.sol | 4 + .../cmdlineTests/metadata_experimental/output | 556 ++++++++++++++++++ .../standard_metadata_experimental/args | 1 + .../standard_metadata_experimental/in.sol | 4 + .../standard_metadata_experimental/input.json | 18 + .../output.json | 77 +++ .../output.json | 4 +- test/libsolidity/Metadata.cpp | 8 +- 12 files changed, 719 insertions(+), 25 deletions(-) create mode 100644 test/cmdlineTests/metadata_experimental/args create mode 100644 test/cmdlineTests/metadata_experimental/input.sol create mode 100644 test/cmdlineTests/metadata_experimental/output create mode 100644 test/cmdlineTests/standard_metadata_experimental/args create mode 100644 test/cmdlineTests/standard_metadata_experimental/in.sol create mode 100644 test/cmdlineTests/standard_metadata_experimental/input.json create mode 100644 test/cmdlineTests/standard_metadata_experimental/output.json diff --git a/docs/metadata.rst b/docs/metadata.rst index 90513ee326..ee0bcbf1ab 100644 --- a/docs/metadata.rst +++ b/docs/metadata.rst @@ -136,6 +136,12 @@ explanatory purposes. "compilationTarget": { "myDirectory/myFile.sol": "MyContract" }, + // Optional (false if omitted): Indicates whether experimental mode has been enabled. + // Always matches the value of the `experimental` flag in CBOR metadata. + // Note that experimental mode being enabled does not necessarily mean that any + // experimental features were actually used, or if they were, that those features + // affected the bytecode. + "experimental": true, // Required for Solidity: Addresses for libraries used. // Note that metadata has a different format for "libraries" field than the standard JSON input. // metadata format = { "MyLib.sol:MyLib": "0x123123..." } @@ -198,11 +204,12 @@ Below are all the possible fields: { "ipfs": "", - // If "bytecodeHash" was "bzzr1" in compiler settings not "ipfs" but "bzzr1" + // Present if "bytecodeHash" was "bzzr1" in compiler settings "bzzr1": "", // Previous versions were using "bzzr0" instead of "bzzr1" "bzzr0": "", - // If any experimental features that affect code generation are used + // Present if experimental mode has been enabled either via "--experimental" flag or + // "settings.experimental" option in Standard JSON "experimental": true, "solc": "" } diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index a1ca2e0665..cd39a6f2d6 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -282,7 +282,9 @@ Input Description // Optional: List of remappings "remappings": [ ":g=/dir" ], // Optional: Experimental mode toggle (Default: false) - "experimental": false, + // Makes it possible to use experimental features (but does not enable any such feature by itself). + // The use of this mode is recorded in contract metadata. + "experimental": true, // Optional: Optimizer settings "optimizer": { // Turn on the optimizer. Optional. Default: false. @@ -705,12 +707,29 @@ Error Types Experimental Mode ***************** -Experimental mode has been introduced as of version 0.8.34 as an additional safeguard when using experimental features. -Prior to version 0.8.34, all experimental features not requiring an explicit ``pragma experimental ...`` in source file(s) -were freely available to be used without any additional safeguards. Given that experimental features are sparsely documented, -if at all, often not adequately tested, and thus not intended for production use, we decided to add the ``--experimental`` -flag to the command line interface, and the analogous ``settings.experimental`` boolean setting to the Standard JSON interface. -Going forward, usage of any experimental feature will require the experimental mode to be toggled. +Some language and compiler features included in stable releases are not themselves considered stable. +They are sparsely documented, if at all, often not adequately tested, and thus not yet intended for production use. +In many cases it is possible to develop a big feature incrementally, with each iteration being already stable. +Sometimes, however, it is preferable to start with a prototype and stabilize it over multiple releases, while receiving feedback from users. +To prevent accidental use, such features can be only accessed by enabling the experimental mode. + +There are no backwards compatibility guarantees for experimental features. +They are subject to change in breaking ways in non-breaking releases of the compiler. +Only major changes affecting them are recorded in the changelog. + +To enable the experimental mode, use the ``--experimental`` flag on the command line, +or the analogous ``settings.experimental`` boolean setting in the Standard JSON input. + +Note that the use of this mode is recorded in the metadata: + +- ``experimental`` flag in CBOR metadata is set to ``true``, +- ``settings.experimental`` in JSON metadata is set to ``true``, + +.. note:: + Prior to version 0.8.34, most of the experimental features were usable without any extra safeguards. + Some were gated behind ``pragma experimental``, but this was not done consistently. + The information about them was also only recorded in CBOR metadata and even then not always. + The main goal of the experimental mode is to systematize this and make users fully aware when relying on features which are unfinished or not production-ready. The table below details all currently available experimental features. @@ -719,21 +738,19 @@ The table below details all currently available experimental features. +=======================+==========================+==================+===================================================================+ | AST import | ``ast-import`` | yes | ``--import-ast`` | +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ -| LSP | ``lsp`` | yes | ``--lsp`` | +| LSP | ``lsp`` | no | ``--lsp`` | +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ | EVM Assembly import | ``evmasm-import`` | yes | ``--import-asm-json`` | +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ -| Generic Solidity | ``generic-solidity`` | yes | ``pragma experimental solidity`` | +| Core Solidity | ``core-solidity`` | yes | ``pragma experimental solidity`` | +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ | IR AST | ``ir-ast`` | no | ``--ir-ast-json``, ``--ir-optimized-ast-json`` | +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ | EOF | ``eof`` | yes | ``--experimental-eof-version`` | +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ -| Osaka EVM | ``evm-osaka`` | yes | ``--evm-version osaka`` | +| Non-mainnet EVMs | ``evm`` | yes | ``--evm-version `` | +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ | Ethdebug | ``ethdebug`` | no | ``--ethdebug``, ``--ethdebug-runtime``, ``--debug-info ethdebug`` | +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ | Yul SSA CFG exporter | ``yul-ssa-cfg-exporter`` | no | ``--yul-cfg-json`` | +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ -| SSA CFG codegen | ``ssa-cfg-codegen`` | yes | ``--ssa-cfg-codegen`` (not yet on ``develop``) | -+-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 2ed72db0ff..d7f2881c22 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -533,6 +533,10 @@ bool CompilerStack::analyze() if (!noErrors) return false; + for (Source const* source: m_sourceOrder) + if (source->ast && !m_experimental) + solAssert(source->ast->annotation().experimentalFeatures.empty()); + m_stackState = AnalysisSuccessful; return true; } @@ -767,10 +771,6 @@ bool CompilerStack::compile(State _stopAfter) std::map> otherCompilers; for (Source const* source: m_sourceOrder) - { - if (source->ast && !m_experimental) - solAssert(source->ast->annotation().experimentalFeatures.empty()); - for (ASTPointer const& node: source->ast->nodes()) if (auto contract = dynamic_cast(node.get())) if (isRequestedContract(*contract)) @@ -805,7 +805,6 @@ bool CompilerStack::compile(State _stopAfter) if (m_errorReporter.hasErrors()) return false; } - } solAssert(!m_errorReporter.hasErrors()); m_stackState = CompilationSuccessful; @@ -1827,6 +1826,8 @@ std::string CompilerStack::createMetadata(Contract const& _contract, bool _forIR if (_forIR) meta["settings"]["viaIR"] = _forIR; meta["settings"]["evmVersion"] = m_evmVersion.name(); + if (m_experimental) + meta["settings"]["experimental"] = m_experimental; if (m_eofVersion.has_value()) meta["settings"]["eofVersion"] = *m_eofVersion; meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] = @@ -1938,10 +1939,14 @@ bytes CompilerStack::createCBORMetadata(Contract const& _contract, bool _forIR) if (m_metadataFormat == MetadataFormat::NoMetadata) return bytes{}; - bool const experimentalMode = !onlySafeExperimentalFeaturesActivated( + bool const usesExperimentalSyntax = !_contract.contract->sourceUnit().annotation().experimentalFeatures.empty(); + bool const onlySafeExperimentalFeatures = !onlySafeExperimentalFeaturesActivated( _contract.contract->sourceUnit().annotation().experimentalFeatures ); + if (m_eofVersion.has_value() || (usesExperimentalSyntax && !onlySafeExperimentalFeatures)) + solAssert(m_experimental, "Experimental mode not toggled"); + std::string meta = (_forIR == m_viaIR ? metadata(_contract) : createMetadata(_contract, _forIR)); MetadataCBOREncoder encoder; @@ -1953,7 +1958,7 @@ bytes CompilerStack::createCBORMetadata(Contract const& _contract, bool _forIR) else solAssert(m_metadataHash == MetadataHash::None, "Invalid metadata hash"); - if (experimentalMode || m_eofVersion.has_value()) + if (m_experimental) encoder.pushBool("experimental", true); if (m_metadataFormat == MetadataFormat::WithReleaseVersionTag) encoder.pushBytes("solc", VersionCompactBytes); diff --git a/test/cmdlineTests/metadata_experimental/args b/test/cmdlineTests/metadata_experimental/args new file mode 100644 index 0000000000..4a834e560d --- /dev/null +++ b/test/cmdlineTests/metadata_experimental/args @@ -0,0 +1 @@ +--metadata --experimental --via-ir --ethdebug --no-cbor-metadata --pretty-json --json-indent 4 diff --git a/test/cmdlineTests/metadata_experimental/input.sol b/test/cmdlineTests/metadata_experimental/input.sol new file mode 100644 index 0000000000..a3a86cc8d3 --- /dev/null +++ b/test/cmdlineTests/metadata_experimental/input.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity *; + +contract C {} diff --git a/test/cmdlineTests/metadata_experimental/output b/test/cmdlineTests/metadata_experimental/output new file mode 100644 index 0000000000..dc9145d5e8 --- /dev/null +++ b/test/cmdlineTests/metadata_experimental/output @@ -0,0 +1,556 @@ +======= Debug Data (ethdebug/format/info/resources) ======= +{ + "compilation": { + "compiler": { + "name": "solc", + "version": "" + }, + "sources": [ + { + "id": 0, + "path": "input.sol" + } + ] + } +} + +======= input.sol:C ======= +Metadata: +{"compiler":{"version": ""},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"input.sol":"C"},"evmVersion":"osaka","experimental":true,"libraries":{},"metadata":{"appendCBOR":false,"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[],"viaIR":true},"sources":{"input.sol":{"keccak256":"0x5cf617b1707a484e3c4bd59643013dec76ab7d75900b46855214729ae3e0ceb0","license":"GPL-3.0","urls":["bzz-raw://ac418a02dfadf87234150d3568f33269e3f49460345cb39300e017a6d755eff2","dweb:/ipfs/QmQq3owBu25x2WV46HB1WyKzJpxiAPecU7eMKqtXCF7eeS"]}},"version":1} +Debug Data (ethdebug/format/program): +{ + "contract": { + "definition": { + "source": { + "id": 0 + } + }, + "name": "C" + }, + "environment": "create", + "instructions": [ + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 0, + "operation": { + "arguments": [ + "0x80" + ], + "mnemonic": "PUSH1" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 2, + "operation": { + "arguments": [ + "0x40" + ], + "mnemonic": "PUSH1" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 4, + "operation": { + "mnemonic": "MSTORE" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 5, + "operation": { + "mnemonic": "CALLVALUE" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 6, + "operation": { + "arguments": [ + "0x19" + ], + "mnemonic": "PUSH1" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 8, + "operation": { + "mnemonic": "JUMPI" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 9, + "operation": { + "arguments": [ + "0x0e" + ], + "mnemonic": "PUSH1" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 11, + "operation": { + "arguments": [ + "0x1d" + ], + "mnemonic": "PUSH1" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 13, + "operation": { + "mnemonic": "JUMP" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 14, + "operation": { + "mnemonic": "JUMPDEST" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 15, + "operation": { + "arguments": [ + "0x08" + ], + "mnemonic": "PUSH1" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 17, + "operation": { + "arguments": [ + "0x28" + ], + "mnemonic": "PUSH1" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 19, + "operation": { + "mnemonic": "DUP3" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 20, + "operation": { + "mnemonic": "CODECOPY" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 21, + "operation": { + "arguments": [ + "0x08" + ], + "mnemonic": "PUSH1" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 23, + "operation": { + "mnemonic": "SWAP1" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 24, + "operation": { + "mnemonic": "RETURN" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 25, + "operation": { + "mnemonic": "JUMPDEST" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 26, + "operation": { + "arguments": [ + "0x23" + ], + "mnemonic": "PUSH1" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 28, + "operation": { + "mnemonic": "JUMP" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 29, + "operation": { + "mnemonic": "JUMPDEST" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 30, + "operation": { + "arguments": [ + "0x40" + ], + "mnemonic": "PUSH1" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 32, + "operation": { + "mnemonic": "MLOAD" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 33, + "operation": { + "mnemonic": "SWAP1" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 34, + "operation": { + "mnemonic": "JUMP" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 35, + "operation": { + "mnemonic": "JUMPDEST" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 36, + "operation": { + "mnemonic": "PUSH0" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 37, + "operation": { + "mnemonic": "DUP1" + } + }, + { + "context": { + "code": { + "range": { + "length": 13, + "offset": 56 + }, + "source": { + "id": 0 + } + } + }, + "offset": 38, + "operation": { + "mnemonic": "REVERT" + } + } + ] +} diff --git a/test/cmdlineTests/standard_metadata_experimental/args b/test/cmdlineTests/standard_metadata_experimental/args new file mode 100644 index 0000000000..18532c5a6d --- /dev/null +++ b/test/cmdlineTests/standard_metadata_experimental/args @@ -0,0 +1 @@ +--allow-paths . diff --git a/test/cmdlineTests/standard_metadata_experimental/in.sol b/test/cmdlineTests/standard_metadata_experimental/in.sol new file mode 100644 index 0000000000..a3a86cc8d3 --- /dev/null +++ b/test/cmdlineTests/standard_metadata_experimental/in.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity *; + +contract C {} diff --git a/test/cmdlineTests/standard_metadata_experimental/input.json b/test/cmdlineTests/standard_metadata_experimental/input.json new file mode 100644 index 0000000000..fddbca92eb --- /dev/null +++ b/test/cmdlineTests/standard_metadata_experimental/input.json @@ -0,0 +1,18 @@ +{ + "language": "Solidity", + "sources": { + "C": {"urls": ["in.sol"]} + }, + "settings": { + "experimental": true, + "viaIR": true, + "debug": { + "debugInfo": [ + "ethdebug" + ] + }, + "outputSelection": { + "*": {"*": ["metadata", "ir"]} + } + } +} diff --git a/test/cmdlineTests/standard_metadata_experimental/output.json b/test/cmdlineTests/standard_metadata_experimental/output.json new file mode 100644 index 0000000000..755a594868 --- /dev/null +++ b/test/cmdlineTests/standard_metadata_experimental/output.json @@ -0,0 +1,77 @@ +{ + "contracts": { + "C": { + "C": { + "ir": "/// ethdebug: enabled +/// @use-src 0:\"C\" +object \"C_2\" { + code { + /// @src 0:56:69 + mstore(64, memoryguard(128)) + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } + + constructor_C_2() + + let _1 := allocate_unbounded() + codecopy(_1, dataoffset(\"C_2_deployed\"), datasize(\"C_2_deployed\")) + + return(_1, datasize(\"C_2_deployed\")) + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + /// @src 0:56:69 + function constructor_C_2() { + + /// @src 0:56:69 + + } + /// @src 0:56:69 + + } + /// @use-src 0:\"C\" + object \"C_2_deployed\" { + code { + /// @src 0:56:69 + mstore(64, memoryguard(128)) + + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + } + + data \".metadata\" hex\"\" + } + +} + +", + "metadata": "{\"compiler\":{\"version\":\"\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"C\":\"C\"},\"evmVersion\":\"osaka\",\"experimental\":true,\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[],\"viaIR\":true},\"sources\":{\"C\":{\"keccak256\":\"0x5cf617b1707a484e3c4bd59643013dec76ab7d75900b46855214729ae3e0ceb0\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://ac418a02dfadf87234150d3568f33269e3f49460345cb39300e017a6d755eff2\",\"dweb:/ipfs/QmQq3owBu25x2WV46HB1WyKzJpxiAPecU7eMKqtXCF7eeS\"]}},\"version\":1}" + } + } + }, + "sources": { + "C": { + "id": 0 + } + } +} diff --git a/test/cmdlineTests/standard_undeployable_contract_all_outputs/output.json b/test/cmdlineTests/standard_undeployable_contract_all_outputs/output.json index 9dec9899a7..16239faf19 100644 --- a/test/cmdlineTests/standard_undeployable_contract_all_outputs/output.json +++ b/test/cmdlineTests/standard_undeployable_contract_all_outputs/output.json @@ -35,7 +35,7 @@ "irAst": null, "irOptimized": "", "irOptimizedAst": null, - "metadata": "{\"compiler\":{\"version\":\"\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"C\":\"C\"},\"evmVersion\":\"osaka\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"C\":{\"keccak256\":\"0xa8b7bfe5eff9112e6573d2860721faef28e8920ee251acb458303a05c1ec7df2\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://a2333e25b5034de4f98729e0a737309ba8e0db4371016f312e8f991f6f01613f\",\"dweb:/ipfs/QmVLwS2grVYV6qiDvNmeEYWzb6WDne9Ze47NXERSLPM4fJ\"]}},\"version\":1}", + "metadata": "{\"compiler\":{\"version\":\"\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"C\":\"C\"},\"evmVersion\":\"osaka\",\"experimental\":true,\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"C\":{\"keccak256\":\"0xa8b7bfe5eff9112e6573d2860721faef28e8920ee251acb458303a05c1ec7df2\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://a2333e25b5034de4f98729e0a737309ba8e0db4371016f312e8f991f6f01613f\",\"dweb:/ipfs/QmVLwS2grVYV6qiDvNmeEYWzb6WDne9Ze47NXERSLPM4fJ\"]}},\"version\":1}", "storageLayout": { "storage": [], "types": null @@ -84,7 +84,7 @@ "irAst": null, "irOptimized": "", "irOptimizedAst": null, - "metadata": "{\"compiler\":{\"version\":\"\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"C\":\"I\"},\"evmVersion\":\"osaka\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"C\":{\"keccak256\":\"0xa8b7bfe5eff9112e6573d2860721faef28e8920ee251acb458303a05c1ec7df2\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://a2333e25b5034de4f98729e0a737309ba8e0db4371016f312e8f991f6f01613f\",\"dweb:/ipfs/QmVLwS2grVYV6qiDvNmeEYWzb6WDne9Ze47NXERSLPM4fJ\"]}},\"version\":1}", + "metadata": "{\"compiler\":{\"version\":\"\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"C\":\"I\"},\"evmVersion\":\"osaka\",\"experimental\":true,\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"C\":{\"keccak256\":\"0xa8b7bfe5eff9112e6573d2860721faef28e8920ee251acb458303a05c1ec7df2\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://a2333e25b5034de4f98729e0a737309ba8e0db4371016f312e8f991f6f01613f\",\"dweb:/ipfs/QmVLwS2grVYV6qiDvNmeEYWzb6WDne9Ze47NXERSLPM4fJ\"]}},\"version\":1}", "storageLayout": { "storage": [], "types": null diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index 0b6bae28db..8b54acc669 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -112,7 +112,7 @@ BOOST_AUTO_TEST_CASE(metadata_stamp) auto const cborMetadata = requireParsedCBORMetadata(bytecode, metadataFormat); if (metadataHash == CompilerStack::MetadataHash::None) - BOOST_CHECK(cborMetadata.size() == (metadataFormat == CompilerStack::MetadataFormat::NoMetadata ? 0 : 1)); + BOOST_CHECK(cborMetadata.size() == (metadataFormat == CompilerStack::MetadataFormat::NoMetadata ? 0 : 2)); else { bytes hash; @@ -132,16 +132,20 @@ BOOST_AUTO_TEST_CASE(metadata_stamp) if (metadataFormat != CompilerStack::MetadataFormat::NoMetadata) { - BOOST_CHECK(cborMetadata.size() == 2); + BOOST_CHECK(cborMetadata.size() == 3); BOOST_CHECK(cborMetadata.count(hashMethod) == 1); BOOST_CHECK(cborMetadata.at(hashMethod) == util::toHex(hash)); } } if (metadataFormat == CompilerStack::MetadataFormat::NoMetadata) + { BOOST_CHECK(cborMetadata.count("solc") == 0); + BOOST_CHECK(cborMetadata.count("experimental") == 0); + } else { + BOOST_CHECK(cborMetadata.at("experimental") == "true"); BOOST_CHECK(cborMetadata.count("solc") == 1); if (metadataFormat == CompilerStack::MetadataFormat::WithReleaseVersionTag) BOOST_CHECK(cborMetadata.at("solc") == util::toHex(VersionCompactBytes)); From cad1676fcda3e46fbde48df0c67e1320280871dc Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Fri, 6 Feb 2026 14:48:59 +0100 Subject: [PATCH 216/243] Address review comments --- docs/metadata.rst | 1 + docs/using-the-compiler.rst | 4 +- libsolidity/analysis/SyntaxChecker.cpp | 17 +- libsolidity/analysis/SyntaxChecker.h | 4 +- libsolidity/interface/CompilerStack.cpp | 30 ++-- libsolidity/interface/StandardCompiler.cpp | 24 +-- solc/CommandLineParser.cpp | 88 ++++++----- solc/CommandLineParser.h | 4 +- .../err | 2 +- .../input.json | 2 +- .../standard_eof_no_experimental/output.json | 11 ++ .../input.json | 0 .../output.json | 0 .../input.json | 0 .../output.json | 0 .../input.json | 0 .../output.json | 4 +- .../standard_no_experimental_eof/output.json | 11 -- .../output.json | 11 -- .../output.json | 11 -- .../output.json | 11 -- .../output.json | 11 -- .../output.json | 11 -- .../output.json | 4 +- .../input.json | 1 + .../input.json | 1 + .../input.json | 2 +- .../output.json | 11 ++ .../input.json | 1 + .../input.json | 1 + .../input.json | 0 .../output.json | 11 ++ .../input.json | 0 .../output.json | 11 ++ .../input.json | 1 + .../input.json | 0 .../output.json | 11 ++ .../input.json | 1 + .../input.json | 1 + .../input.json | 0 .../output.json | 11 ++ test/libsolidity/Assembly.cpp | 2 +- .../FunctionDependencyGraphTest.cpp | 2 +- test/libsolidity/Metadata.cpp | 16 +- .../SolidityExpressionCompiler.cpp | 2 +- test/libsolidity/StandardCompiler.cpp | 149 +----------------- test/libsolidity/SyntaxTest.h | 2 +- .../smtCheckerTests/options/pragma.sol | 2 - ..._constructor_fixed_bytes_from_string_1.sol | 6 +- .../conflicting_settings_reverse.sol | 2 - ...flicting_settings_reverse_experimental.sol | 2 - .../abiEncoder/same_setting_twice.sol | 2 - .../abiEncoder/selected_twice_v2.sol | 2 - .../experimental_empty_string_literal.sol | 2 - .../experimental_multiple_experimental.sol | 2 - .../experimental_multiple_same_line.sol | 2 - .../pragma/experimental_pragma_empty.sol | 2 - ...rimental_pragma_unknown_string_literal.sol | 2 - ...ental_pragma_without_experimental_mode.sol | 7 +- .../experimental_unknown_number_literal.sol | 2 - ...rimental_unknown_quoted_string_literal.sol | 2 - test/lsp.py | 8 +- test/solc/CommandLineInterface.cpp | 8 - test/solc/CommandLineParser.cpp | 2 +- 64 files changed, 202 insertions(+), 351 deletions(-) rename test/cmdlineTests/{standard_no_experimental_eof => standard_eof_no_experimental}/input.json (65%) create mode 100644 test/cmdlineTests/standard_eof_no_experimental/output.json rename test/cmdlineTests/{import_asm_json_standard_json_optimize => standard_import_asm_json_no_optimize}/input.json (100%) rename test/cmdlineTests/{import_asm_json_standard_json_optimize => standard_import_asm_json_no_optimize}/output.json (100%) rename test/cmdlineTests/{import_asm_json_standard_json_no_optimize => standard_import_asm_json_optimize}/input.json (100%) rename test/cmdlineTests/{import_asm_json_standard_json_no_optimize => standard_import_asm_json_optimize}/output.json (100%) rename test/cmdlineTests/{standard_no_experimental_ast_import => standard_import_ast_no_experimental}/input.json (100%) rename test/cmdlineTests/{standard_no_experimental_ast_import => standard_import_ast_no_experimental}/output.json (59%) delete mode 100644 test/cmdlineTests/standard_no_experimental_eof/output.json delete mode 100644 test/cmdlineTests/standard_no_experimental_ethdebug/output.json delete mode 100644 test/cmdlineTests/standard_no_experimental_ir_ast_output/output.json delete mode 100644 test/cmdlineTests/standard_no_experimental_yul_cfg_json_export/output.json delete mode 100644 test/cmdlineTests/standard_no_experimental_yul_ethdebug/output.json delete mode 100644 test/cmdlineTests/standard_no_experimental_yul_ir_ast_requested/output.json rename test/cmdlineTests/{standard_no_experimental_ethdebug => standard_output_selection_ethdebug_no_experimental}/input.json (79%) create mode 100644 test/cmdlineTests/standard_output_selection_ethdebug_no_experimental/output.json rename test/cmdlineTests/{standard_no_experimental_ir_ast_output => standard_output_selection_ir_ast_no_experimental}/input.json (100%) create mode 100644 test/cmdlineTests/standard_output_selection_ir_ast_no_experimental/output.json rename test/cmdlineTests/{standard_no_experimental_yul_cfg_json_export => standard_output_selection_yul_cfg_json_no_experimental}/input.json (100%) create mode 100644 test/cmdlineTests/standard_output_selection_yul_cfg_json_no_experimental/output.json rename test/cmdlineTests/{standard_no_experimental_yul_ethdebug => standard_yul_ethdebug_no_experimental}/input.json (100%) create mode 100644 test/cmdlineTests/standard_yul_ethdebug_no_experimental/output.json rename test/cmdlineTests/{standard_no_experimental_yul_ir_ast_requested => standard_yul_ir_ast_no_experimental}/input.json (100%) create mode 100644 test/cmdlineTests/standard_yul_ir_ast_no_experimental/output.json diff --git a/docs/metadata.rst b/docs/metadata.rst index ee0bcbf1ab..b4a8d7cfc6 100644 --- a/docs/metadata.rst +++ b/docs/metadata.rst @@ -203,6 +203,7 @@ Below are all the possible fields: .. code-block:: javascript { + // Present if "bytecodeHash" was "ipfs" in compiler settings "ipfs": "", // Present if "bytecodeHash" was "bzzr1" in compiler settings "bzzr1": "", diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index cd39a6f2d6..15f4f859a6 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -742,7 +742,7 @@ The table below details all currently available experimental features. +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ | EVM Assembly import | ``evmasm-import`` | yes | ``--import-asm-json`` | +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ -| Core Solidity | ``core-solidity`` | yes | ``pragma experimental solidity`` | +| Generic Solidity | ``generic-solidity`` | yes | ``pragma experimental solidity`` | +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ | IR AST | ``ir-ast`` | no | ``--ir-ast-json``, ``--ir-optimized-ast-json`` | +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ @@ -752,5 +752,5 @@ The table below details all currently available experimental features. +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ | Ethdebug | ``ethdebug`` | no | ``--ethdebug``, ``--ethdebug-runtime``, ``--debug-info ethdebug`` | +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ -| Yul SSA CFG exporter | ``yul-ssa-cfg-exporter`` | no | ``--yul-cfg-json`` | +| Yul SSA CFG exporter | ``ssa-cfg`` | no | ``--yul-cfg-json`` | +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 8253eec49a..e8777069f7 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -83,12 +83,6 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) else if (_pragma.literals()[0] == "experimental") { solAssert(m_sourceUnit, ""); - if (!m_experimental) - m_errorReporter.syntaxError( - 2816_error, - _pragma.location(), - "Experimental pragmas can only be used if the experimental mode has been enabled." - ); std::vector literals(_pragma.literals().begin() + 1, _pragma.literals().end()); if (literals.empty()) m_errorReporter.syntaxError( @@ -116,7 +110,16 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) auto feature = ExperimentalFeatureNames.at(literal); m_sourceUnit->annotation().experimentalFeatures.insert(feature); if (!ExperimentalFeatureWithoutWarning.count(feature)) - m_errorReporter.warning(2264_error, _pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments."); + { + if (!m_experimental) + m_errorReporter.syntaxError( + 2816_error, + _pragma.location(), + "Experimental pragmas can only be used if experimental mode is enabled. To enable experimental mode, use the --experimental flag." + ); + else + m_errorReporter.warning(2264_error, _pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments."); + } if (feature == ExperimentalFeature::ABIEncoderV2) { diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index 9babfe47e4..8a6e27cb11 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -46,7 +46,7 @@ class SyntaxChecker: private ASTConstVisitor public: /// @param _errorReporter provides the error logging functionality. /// @param _useYulOptimizer indicates whether Yul optimizer is enabled. - /// @param _experimental indicates whether the experimental toggle (option) is enabled. + /// @param _experimental indicates whether the experimental mode is enabled. SyntaxChecker(langutil::ErrorReporter& _errorReporter, bool _useYulOptimizer, bool _experimental): m_errorReporter(_errorReporter), m_useYulOptimizer(_useYulOptimizer), @@ -115,7 +115,7 @@ class SyntaxChecker: private ASTConstVisitor /// Flag that indicates whether we are inside an unchecked block. bool m_uncheckedArithmetic = false; - /// Flag that indicates whether experimental mode is toggled. + /// Flag that indicates whether the experimental mode is enabled. bool m_experimental = false; int m_inLoopDepth = 0; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index d7f2881c22..3583ae5356 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -456,6 +456,19 @@ void CompilerStack::importASTs(std::map const& _sources) storeContractDefinitions(); } +namespace +{ + +bool onlySafeExperimentalFeaturesActivated(std::set const& _features) +{ + for (auto const feature: _features) + if (!ExperimentalFeatureWithoutWarning.contains(feature)) + return false; + return true; +} + +} + bool CompilerStack::analyze() { solAssert(m_stackState == ParsedAndImported, "Must call analyze only after parsing was successful."); @@ -535,7 +548,7 @@ bool CompilerStack::analyze() for (Source const* source: m_sourceOrder) if (source->ast && !m_experimental) - solAssert(source->ast->annotation().experimentalFeatures.empty()); + solAssert(onlySafeExperimentalFeaturesActivated(source->ast->annotation().experimentalFeatures)); m_stackState = AnalysisSuccessful; return true; @@ -1456,17 +1469,6 @@ void CompilerStack::annotateInternalFunctionIDs() } } -namespace -{ -bool onlySafeExperimentalFeaturesActivated(std::set const& features) -{ - for (auto const feature: features) - if (!ExperimentalFeatureWithoutWarning.count(feature)) - return false; - return true; -} -} - void CompilerStack::assembleYul( ContractDefinition const& _contract, std::shared_ptr _assembly, @@ -1940,12 +1942,12 @@ bytes CompilerStack::createCBORMetadata(Contract const& _contract, bool _forIR) return bytes{}; bool const usesExperimentalSyntax = !_contract.contract->sourceUnit().annotation().experimentalFeatures.empty(); - bool const onlySafeExperimentalFeatures = !onlySafeExperimentalFeaturesActivated( + bool const onlySafeExperimentalFeatures = onlySafeExperimentalFeaturesActivated( _contract.contract->sourceUnit().annotation().experimentalFeatures ); if (m_eofVersion.has_value() || (usesExperimentalSyntax && !onlySafeExperimentalFeatures)) - solAssert(m_experimental, "Experimental mode not toggled"); + solAssert(m_experimental, "Experimental mode not enabled"); std::string meta = (_forIR == m_viaIR ? metadata(_contract) : createMetadata(_contract, _forIR)); diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 419c3fac5f..20e3fd45ce 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -826,7 +826,7 @@ std::variant StandardCompiler::parseI if (settings.contains("experimental")) { if (!settings["experimental"].is_boolean()) - return formatFatalError(Error::Type::JSONError, "\"settings.experimental\" must be a Boolean."); + return formatFatalError(Error::Type::JSONError, "'settings.experimental' must be a Boolean."); ret.experimental = settings["experimental"].get(); } @@ -1230,11 +1230,13 @@ std::variant StandardCompiler::parseI } } - if ( - ret.debugInfoSelection.has_value() && ret.debugInfoSelection->ethdebug && (ret.language == "Solidity" || ret.language == "Yul") && - !pipelineConfig(ret.outputSelection)[""][""].irCodegen && !isEthdebugRequested(ret.outputSelection) - ) - return formatFatalError(Error::Type::FatalError, "'settings.debug.debugInfo' can only include 'ethdebug', if output 'ir', 'irOptimized', 'evm.bytecode.ethdebug', or 'evm.deployedBytecode.ethdebug' was selected."); + if (ret.debugInfoSelection.has_value() && ret.debugInfoSelection->ethdebug) + { + if (!ret.experimental) + return formatFatalError(Error::Type::FatalError, "Ethdebug annotations are experimental and can only be included in ‘settings.debug.debugInfo’ by enabling the ‘settings.experimental’ option."); + if (!pipelineConfig(ret.outputSelection)[""][""].irCodegen && !isEthdebugRequested(ret.outputSelection)) + return formatFatalError(Error::Type::FatalError, "'settings.debug.debugInfo' can only include 'ethdebug', if output 'ir', 'irOptimized', 'evm.bytecode.ethdebug', or 'evm.deployedBytecode.ethdebug' was selected."); + } if (isEthdebugRequested(ret.outputSelection)) if (ret.optimiserSettings.runYulOptimiser) @@ -1243,17 +1245,17 @@ std::variant StandardCompiler::parseI if (!ret.experimental) { if (ret.language == "SolidityAST" || ret.language == "EVMAssembly") - return formatFatalError(Error::Type::FatalError, "'SolidityAST' and 'EVMAssembly' inputs are experimental, and can only be used by toggling the 'settings.experimental' option."); + return formatFatalError(Error::Type::FatalError, "'SolidityAST' and 'EVMAssembly' inputs are experimental and can only be used with the 'settings.experimental' option enabled."); if (ret.evmVersion.isExperimental()) // TODO: Cover with test when the Amsterdam version is introduced - return formatFatalError(Error::Type::FatalError, fmt::format("EVM version {} is experimental, and can only be used by toggling the 'settings.experimental' option.", ret.evmVersion.name())); + return formatFatalError(Error::Type::FatalError, fmt::format("EVM version '{}' is experimental and can only be used with the 'settings.experimental' option enabled.", ret.evmVersion.name())); if (isExperimentalArtifactRequested(ret.outputSelection)) - return formatFatalError(Error::Type::FatalError, "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option."); + return formatFatalError(Error::Type::FatalError, "'irAst', 'irOptimizedAst', 'yulCFGJson', and 'ethdebug' outputs are experimental and can only be used with the 'settings.experimental' option enabled."); - if (ret.eofVersion) - return formatFatalError(Error::Type::FatalError, "'eofVersion' is experimental, and can only be used by toggling the 'settings.experimental' option."); + if (ret.eofVersion.has_value()) + return formatFatalError(Error::Type::FatalError, "'eofVersion' setting is experimental and can only be used with the 'settings.experimental' option enabled."); } return {std::move(ret)}; diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 6720b9fe8d..b11adf0373 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -164,21 +164,29 @@ void CommandLineParser::checkExperimental(std::vector const& _optio { solThrow( CommandLineValidationError, - "The following options are only available in experimental mode: " + joinOptionNames(_optionNames) + ". " + - "To enable experimental mode, use the --experimental flag." + fmt::format( + "The following options are only available in experimental mode: {}. " + "To enable experimental mode, use the --{} flag.", + joinOptionNames(_optionNames), + g_strExperimental + ) ); } - if (m_args.count(g_strEVMVersion)) + if (m_args.contains(g_strEVMVersion)) { - std::optional versionOption = EVMVersion::fromString(m_args[g_strEVMVersion].as()); - if (versionOption && versionOption->isExperimental()) + std::string versionOptionStr = m_args[g_strEVMVersion].as(); + std::optional versionOption = langutil::EVMVersion::fromString(versionOptionStr); + + if (versionOption.has_value() && versionOption->isExperimental()) // TODO: Cover with test when the Amsterdam version is introduced solThrow( CommandLineValidationError, fmt::format( - "EVM version '{}' is experimental and can only be selected in experimental mode." - "To enable experimental mode, use the --experimental flag", versionOption->name() + "EVM version '{}' is experimental and can only be selected in experimental mode. " + "To enable experimental mode, use the --{} flag", + m_options.output.evmVersion.name(), + g_strExperimental ) ); } @@ -502,7 +510,7 @@ void CommandLineParser::parseOutputSelection() // TODO: restrict EOF version to correct EVM version. } -po::options_description CommandLineParser::optionsDescription(bool _forHelp) +po::options_description CommandLineParser::optionsDescription() { // Declare the supported options. po::options_description desc((R"(solc, the Solidity commandline compiler. @@ -590,19 +598,18 @@ General Information)").c_str(), ("Select desired EVM version: " + annotatedEVMVersions + ".").c_str() ) ; - if (!_forHelp) // Note: We intentionally keep this undocumented for now. - outputOptions.add_options() - ( - g_strEOFVersion.c_str(), - // Declared as uint64_t, since uint8_t will be parsed as character by boost. - po::value()->value_name("version")->implicit_value(1), - "Select desired EOF version. Currently the only valid value is 1. " - "If not specified, legacy non-EOF bytecode will be generated." - ) - ( - g_strYul.c_str(), "The typed Yul dialect is no longer supported. For regular Yul compilation use --strict-assembly instead." - ) - ; + outputOptions.add_options() + ( + g_strEOFVersion.c_str(), + // Declared as uint64_t, since uint8_t will be parsed as character by boost. + po::value()->value_name("version")->implicit_value(1), + "Select desired EOF version. Currently the only valid value is 1. " + "If not specified, legacy non-EOF bytecode will be generated." + ) + ( + g_strYul.c_str(), "The typed Yul dialect is no longer supported. For regular Yul compilation use --strict-assembly instead." + ) + ; outputOptions.add_options() ( g_strExperimentalViaIR.c_str(), @@ -746,24 +753,21 @@ General Information)").c_str(), (CompilerOutputs::componentName(&CompilerOutputs::storageLayout).c_str(), "Slots, offsets and types of the contract's state variables located in storage.") (CompilerOutputs::componentName(&CompilerOutputs::transientStorageLayout).c_str(), "Slots, offsets and types of the contract's state variables located in transient storage.") ; - if (!_forHelp) // Note: We intentionally keep this undocumented for now. - { - outputComponents.add_options() - ( - CompilerOutputs::componentName(&CompilerOutputs::yulCFGJson).c_str(), - "Control Flow Graph (CFG) of Yul code in JSON format." - ); - outputComponents.add_options() - ( - CompilerOutputs::componentName(&CompilerOutputs::ethdebug).c_str(), - "Ethdebug output of all contracts." - ); - outputComponents.add_options() - ( - CompilerOutputs::componentName(&CompilerOutputs::ethdebugRuntime).c_str(), - "Ethdebug output of the runtime part of all contracts." - ); - } + outputComponents.add_options() + ( + CompilerOutputs::componentName(&CompilerOutputs::yulCFGJson).c_str(), + "Control Flow Graph (CFG) of Yul code in JSON format." + ); + outputComponents.add_options() + ( + CompilerOutputs::componentName(&CompilerOutputs::ethdebug).c_str(), + "Ethdebug output of all contracts." + ); + outputComponents.add_options() + ( + CompilerOutputs::componentName(&CompilerOutputs::ethdebugRuntime).c_str(), + "Ethdebug output of the runtime part of all contracts." + ); desc.add(outputComponents); po::options_description extraOutput("Extra Output"); @@ -1167,7 +1171,11 @@ void CommandLineParser::processArgs() if (!m_options.experimental && m_options.output.debugInfoSelection->ethdebug) solThrow( CommandLineValidationError, - "--" + g_strDebugInfo + " ethdebug can only be used by toggling the --" + g_strExperimental + " flag." + fmt::format( + "Ethdebug annotations are experimental and can only be included in --{} in experimental mode. To enable experimental mode, use the --{} flag.", + g_strDebugInfo, + g_strExperimental + ) ); if (m_options.output.debugInfoSelection->snippet && !m_options.output.debugInfoSelection->location) diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index 6c32b5ee07..bdb5fef2fd 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -288,12 +288,12 @@ class CommandLineParser CommandLineOptions const& options() const { return m_options; } - static void printHelp(std::ostream& _out) { _out << optionsDescription(true /* _forHelp */); } + static void printHelp(std::ostream& _out) { _out << optionsDescription(); } private: /// @returns a specification of all named command-line options accepted by the compiler. /// The object can be used to parse command-line arguments or to generate the help screen. - static boost::program_options::options_description optionsDescription(bool _forHelp = false); + static boost::program_options::options_description optionsDescription(); /// @returns a specification of all positional command-line arguments accepted by the compiler. /// The object can be used to parse command-line arguments or to generate the help screen. diff --git a/test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/err b/test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/err index d8327f90bf..a7b9ca900b 100644 --- a/test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/err +++ b/test/cmdlineTests/ethdebug_debuginfo_without_experimental_flag/err @@ -1 +1 @@ -Error: --debug-info ethdebug can only be used by toggling the --experimental flag. +Error: Ethdebug annotations are experimental and can only be included in --debug-info in experimental mode. To enable experimental mode, use the --experimental flag. diff --git a/test/cmdlineTests/standard_no_experimental_eof/input.json b/test/cmdlineTests/standard_eof_no_experimental/input.json similarity index 65% rename from test/cmdlineTests/standard_no_experimental_eof/input.json rename to test/cmdlineTests/standard_eof_no_experimental/input.json index 8892caf272..6ba6f727b9 100644 --- a/test/cmdlineTests/standard_no_experimental_eof/input.json +++ b/test/cmdlineTests/standard_eof_no_experimental/input.json @@ -2,7 +2,7 @@ "language": "Solidity", "sources": { "A.sol": { - "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" + "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" } }, "settings": { diff --git a/test/cmdlineTests/standard_eof_no_experimental/output.json b/test/cmdlineTests/standard_eof_no_experimental/output.json new file mode 100644 index 0000000000..22be8bde12 --- /dev/null +++ b/test/cmdlineTests/standard_eof_no_experimental/output.json @@ -0,0 +1,11 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "'eofVersion' setting is experimental and can only be used with the 'settings.experimental' option enabled.", + "message": "'eofVersion' setting is experimental and can only be used with the 'settings.experimental' option enabled.", + "severity": "error", + "type": "FatalError" + } + ] +} diff --git a/test/cmdlineTests/import_asm_json_standard_json_optimize/input.json b/test/cmdlineTests/standard_import_asm_json_no_optimize/input.json similarity index 100% rename from test/cmdlineTests/import_asm_json_standard_json_optimize/input.json rename to test/cmdlineTests/standard_import_asm_json_no_optimize/input.json diff --git a/test/cmdlineTests/import_asm_json_standard_json_optimize/output.json b/test/cmdlineTests/standard_import_asm_json_no_optimize/output.json similarity index 100% rename from test/cmdlineTests/import_asm_json_standard_json_optimize/output.json rename to test/cmdlineTests/standard_import_asm_json_no_optimize/output.json diff --git a/test/cmdlineTests/import_asm_json_standard_json_no_optimize/input.json b/test/cmdlineTests/standard_import_asm_json_optimize/input.json similarity index 100% rename from test/cmdlineTests/import_asm_json_standard_json_no_optimize/input.json rename to test/cmdlineTests/standard_import_asm_json_optimize/input.json diff --git a/test/cmdlineTests/import_asm_json_standard_json_no_optimize/output.json b/test/cmdlineTests/standard_import_asm_json_optimize/output.json similarity index 100% rename from test/cmdlineTests/import_asm_json_standard_json_no_optimize/output.json rename to test/cmdlineTests/standard_import_asm_json_optimize/output.json diff --git a/test/cmdlineTests/standard_no_experimental_ast_import/input.json b/test/cmdlineTests/standard_import_ast_no_experimental/input.json similarity index 100% rename from test/cmdlineTests/standard_no_experimental_ast_import/input.json rename to test/cmdlineTests/standard_import_ast_no_experimental/input.json diff --git a/test/cmdlineTests/standard_no_experimental_ast_import/output.json b/test/cmdlineTests/standard_import_ast_no_experimental/output.json similarity index 59% rename from test/cmdlineTests/standard_no_experimental_ast_import/output.json rename to test/cmdlineTests/standard_import_ast_no_experimental/output.json index 792d6b4c19..d9e6d8cac0 100644 --- a/test/cmdlineTests/standard_no_experimental_ast_import/output.json +++ b/test/cmdlineTests/standard_import_ast_no_experimental/output.json @@ -2,8 +2,8 @@ "errors": [ { "component": "general", - "formattedMessage": "'SolidityAST' and 'EVMAssembly' inputs are experimental, and can only be used by toggling the 'settings.experimental' option.", - "message": "'SolidityAST' and 'EVMAssembly' inputs are experimental, and can only be used by toggling the 'settings.experimental' option.", + "formattedMessage": "'SolidityAST' and 'EVMAssembly' inputs are experimental and can only be used with the 'settings.experimental' option enabled.", + "message": "'SolidityAST' and 'EVMAssembly' inputs are experimental and can only be used with the 'settings.experimental' option enabled.", "severity": "error", "type": "FatalError" } diff --git a/test/cmdlineTests/standard_no_experimental_eof/output.json b/test/cmdlineTests/standard_no_experimental_eof/output.json deleted file mode 100644 index 653fbc85a8..0000000000 --- a/test/cmdlineTests/standard_no_experimental_eof/output.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "errors": [ - { - "component": "general", - "formattedMessage": "'eofVersion' is experimental, and can only be used by toggling the 'settings.experimental' option.", - "message": "'eofVersion' is experimental, and can only be used by toggling the 'settings.experimental' option.", - "severity": "error", - "type": "FatalError" - } - ] -} diff --git a/test/cmdlineTests/standard_no_experimental_ethdebug/output.json b/test/cmdlineTests/standard_no_experimental_ethdebug/output.json deleted file mode 100644 index 6f132c1cd9..0000000000 --- a/test/cmdlineTests/standard_no_experimental_ethdebug/output.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "errors": [ - { - "component": "general", - "formattedMessage": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", - "message": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", - "severity": "error", - "type": "FatalError" - } - ] -} diff --git a/test/cmdlineTests/standard_no_experimental_ir_ast_output/output.json b/test/cmdlineTests/standard_no_experimental_ir_ast_output/output.json deleted file mode 100644 index 6f132c1cd9..0000000000 --- a/test/cmdlineTests/standard_no_experimental_ir_ast_output/output.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "errors": [ - { - "component": "general", - "formattedMessage": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", - "message": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", - "severity": "error", - "type": "FatalError" - } - ] -} diff --git a/test/cmdlineTests/standard_no_experimental_yul_cfg_json_export/output.json b/test/cmdlineTests/standard_no_experimental_yul_cfg_json_export/output.json deleted file mode 100644 index 6f132c1cd9..0000000000 --- a/test/cmdlineTests/standard_no_experimental_yul_cfg_json_export/output.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "errors": [ - { - "component": "general", - "formattedMessage": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", - "message": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", - "severity": "error", - "type": "FatalError" - } - ] -} diff --git a/test/cmdlineTests/standard_no_experimental_yul_ethdebug/output.json b/test/cmdlineTests/standard_no_experimental_yul_ethdebug/output.json deleted file mode 100644 index 6f132c1cd9..0000000000 --- a/test/cmdlineTests/standard_no_experimental_yul_ethdebug/output.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "errors": [ - { - "component": "general", - "formattedMessage": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", - "message": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", - "severity": "error", - "type": "FatalError" - } - ] -} diff --git a/test/cmdlineTests/standard_no_experimental_yul_ir_ast_requested/output.json b/test/cmdlineTests/standard_no_experimental_yul_ir_ast_requested/output.json deleted file mode 100644 index 6f132c1cd9..0000000000 --- a/test/cmdlineTests/standard_no_experimental_yul_ir_ast_requested/output.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "errors": [ - { - "component": "general", - "formattedMessage": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", - "message": "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option.", - "severity": "error", - "type": "FatalError" - } - ] -} diff --git a/test/cmdlineTests/standard_output_debuginfo_ethdebug_incompatible/output.json b/test/cmdlineTests/standard_output_debuginfo_ethdebug_incompatible/output.json index 963bcd8d7d..9342b91f51 100644 --- a/test/cmdlineTests/standard_output_debuginfo_ethdebug_incompatible/output.json +++ b/test/cmdlineTests/standard_output_debuginfo_ethdebug_incompatible/output.json @@ -2,8 +2,8 @@ "errors": [ { "component": "general", - "formattedMessage": "'settings.debug.debugInfo' can only include 'ethdebug', if output 'ir', 'irOptimized', 'evm.bytecode.ethdebug', or 'evm.deployedBytecode.ethdebug' was selected.", - "message": "'settings.debug.debugInfo' can only include 'ethdebug', if output 'ir', 'irOptimized', 'evm.bytecode.ethdebug', or 'evm.deployedBytecode.ethdebug' was selected.", + "formattedMessage": "Ethdebug annotations are experimental and can only be included in \u2018settings.debug.debugInfo\u2019 by enabling the \u2018settings.experimental\u2019 option.", + "message": "Ethdebug annotations are experimental and can only be included in \u2018settings.debug.debugInfo\u2019 by enabling the \u2018settings.experimental\u2019 option.", "severity": "error", "type": "FatalError" } diff --git a/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_and_deployedbytecode_optimizer_ir/input.json b/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_and_deployedbytecode_optimizer_ir/input.json index ad74649f5d..514012ee7c 100644 --- a/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_and_deployedbytecode_optimizer_ir/input.json +++ b/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_and_deployedbytecode_optimizer_ir/input.json @@ -9,6 +9,7 @@ } }, "settings": { + "experimental": true, "viaIR": true, "optimizer": { "enabled": true diff --git a/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_and_deployedbytecode_optimizer_iroptimized/input.json b/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_and_deployedbytecode_optimizer_iroptimized/input.json index ad74649f5d..514012ee7c 100644 --- a/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_and_deployedbytecode_optimizer_iroptimized/input.json +++ b/test/cmdlineTests/standard_output_selection_ethdebug_bytecode_and_deployedbytecode_optimizer_iroptimized/input.json @@ -9,6 +9,7 @@ } }, "settings": { + "experimental": true, "viaIR": true, "optimizer": { "enabled": true diff --git a/test/cmdlineTests/standard_no_experimental_ethdebug/input.json b/test/cmdlineTests/standard_output_selection_ethdebug_no_experimental/input.json similarity index 79% rename from test/cmdlineTests/standard_no_experimental_ethdebug/input.json rename to test/cmdlineTests/standard_output_selection_ethdebug_no_experimental/input.json index b11fdac34b..5fca11155a 100644 --- a/test/cmdlineTests/standard_no_experimental_ethdebug/input.json +++ b/test/cmdlineTests/standard_output_selection_ethdebug_no_experimental/input.json @@ -12,7 +12,7 @@ }, "outputSelection": { "A.sol": { - "A": ["ethdebug","evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"] + "A": ["evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"] } } } diff --git a/test/cmdlineTests/standard_output_selection_ethdebug_no_experimental/output.json b/test/cmdlineTests/standard_output_selection_ethdebug_no_experimental/output.json new file mode 100644 index 0000000000..9342b91f51 --- /dev/null +++ b/test/cmdlineTests/standard_output_selection_ethdebug_no_experimental/output.json @@ -0,0 +1,11 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "Ethdebug annotations are experimental and can only be included in \u2018settings.debug.debugInfo\u2019 by enabling the \u2018settings.experimental\u2019 option.", + "message": "Ethdebug annotations are experimental and can only be included in \u2018settings.debug.debugInfo\u2019 by enabling the \u2018settings.experimental\u2019 option.", + "severity": "error", + "type": "FatalError" + } + ] +} diff --git a/test/cmdlineTests/standard_output_selection_ethdebug_optimize_ir/input.json b/test/cmdlineTests/standard_output_selection_ethdebug_optimize_ir/input.json index a684e0dc07..e1084799b9 100644 --- a/test/cmdlineTests/standard_output_selection_ethdebug_optimize_ir/input.json +++ b/test/cmdlineTests/standard_output_selection_ethdebug_optimize_ir/input.json @@ -9,6 +9,7 @@ } }, "settings": { + "experimental": true, "viaIR": true, "optimizer": { "enabled": true diff --git a/test/cmdlineTests/standard_output_selection_ethdebug_optimize_iroptimized/input.json b/test/cmdlineTests/standard_output_selection_ethdebug_optimize_iroptimized/input.json index 1f09e3a702..235e3c855f 100644 --- a/test/cmdlineTests/standard_output_selection_ethdebug_optimize_iroptimized/input.json +++ b/test/cmdlineTests/standard_output_selection_ethdebug_optimize_iroptimized/input.json @@ -9,6 +9,7 @@ } }, "settings": { + "experimental": true, "viaIR": true, "optimizer": { "enabled": true diff --git a/test/cmdlineTests/standard_no_experimental_ir_ast_output/input.json b/test/cmdlineTests/standard_output_selection_ir_ast_no_experimental/input.json similarity index 100% rename from test/cmdlineTests/standard_no_experimental_ir_ast_output/input.json rename to test/cmdlineTests/standard_output_selection_ir_ast_no_experimental/input.json diff --git a/test/cmdlineTests/standard_output_selection_ir_ast_no_experimental/output.json b/test/cmdlineTests/standard_output_selection_ir_ast_no_experimental/output.json new file mode 100644 index 0000000000..c85e0af018 --- /dev/null +++ b/test/cmdlineTests/standard_output_selection_ir_ast_no_experimental/output.json @@ -0,0 +1,11 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "'irAst', 'irOptimizedAst', 'yulCFGJson', and 'ethdebug' outputs are experimental and can only be used with the 'settings.experimental' option enabled.", + "message": "'irAst', 'irOptimizedAst', 'yulCFGJson', and 'ethdebug' outputs are experimental and can only be used with the 'settings.experimental' option enabled.", + "severity": "error", + "type": "FatalError" + } + ] +} diff --git a/test/cmdlineTests/standard_no_experimental_yul_cfg_json_export/input.json b/test/cmdlineTests/standard_output_selection_yul_cfg_json_no_experimental/input.json similarity index 100% rename from test/cmdlineTests/standard_no_experimental_yul_cfg_json_export/input.json rename to test/cmdlineTests/standard_output_selection_yul_cfg_json_no_experimental/input.json diff --git a/test/cmdlineTests/standard_output_selection_yul_cfg_json_no_experimental/output.json b/test/cmdlineTests/standard_output_selection_yul_cfg_json_no_experimental/output.json new file mode 100644 index 0000000000..c85e0af018 --- /dev/null +++ b/test/cmdlineTests/standard_output_selection_yul_cfg_json_no_experimental/output.json @@ -0,0 +1,11 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "'irAst', 'irOptimizedAst', 'yulCFGJson', and 'ethdebug' outputs are experimental and can only be used with the 'settings.experimental' option enabled.", + "message": "'irAst', 'irOptimizedAst', 'yulCFGJson', and 'ethdebug' outputs are experimental and can only be used with the 'settings.experimental' option enabled.", + "severity": "error", + "type": "FatalError" + } + ] +} diff --git a/test/cmdlineTests/standard_yul_debug_info_ethdebug_incompatible_output/input.json b/test/cmdlineTests/standard_yul_debug_info_ethdebug_incompatible_output/input.json index c2c501c966..e4c9d17e5a 100644 --- a/test/cmdlineTests/standard_yul_debug_info_ethdebug_incompatible_output/input.json +++ b/test/cmdlineTests/standard_yul_debug_info_ethdebug_incompatible_output/input.json @@ -8,6 +8,7 @@ } }, "settings": { + "experimental": true, "debug": {"debugInfo": ["ethdebug"]}, "outputSelection": { "*": {"*": ["evm.assembly"]} diff --git a/test/cmdlineTests/standard_no_experimental_yul_ethdebug/input.json b/test/cmdlineTests/standard_yul_ethdebug_no_experimental/input.json similarity index 100% rename from test/cmdlineTests/standard_no_experimental_yul_ethdebug/input.json rename to test/cmdlineTests/standard_yul_ethdebug_no_experimental/input.json diff --git a/test/cmdlineTests/standard_yul_ethdebug_no_experimental/output.json b/test/cmdlineTests/standard_yul_ethdebug_no_experimental/output.json new file mode 100644 index 0000000000..9342b91f51 --- /dev/null +++ b/test/cmdlineTests/standard_yul_ethdebug_no_experimental/output.json @@ -0,0 +1,11 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "Ethdebug annotations are experimental and can only be included in \u2018settings.debug.debugInfo\u2019 by enabling the \u2018settings.experimental\u2019 option.", + "message": "Ethdebug annotations are experimental and can only be included in \u2018settings.debug.debugInfo\u2019 by enabling the \u2018settings.experimental\u2019 option.", + "severity": "error", + "type": "FatalError" + } + ] +} diff --git a/test/cmdlineTests/standard_yul_ethdebug_optimize_ir/input.json b/test/cmdlineTests/standard_yul_ethdebug_optimize_ir/input.json index d7aec1f088..8074135b4f 100644 --- a/test/cmdlineTests/standard_yul_ethdebug_optimize_ir/input.json +++ b/test/cmdlineTests/standard_yul_ethdebug_optimize_ir/input.json @@ -4,6 +4,7 @@ "C": {"urls": ["in.yul"]} }, "settings": { + "experimental": true, "optimizer": { "enabled": true }, diff --git a/test/cmdlineTests/standard_yul_ethdebug_optimize_iroptimized/input.json b/test/cmdlineTests/standard_yul_ethdebug_optimize_iroptimized/input.json index eeabdbcb98..417a42a311 100644 --- a/test/cmdlineTests/standard_yul_ethdebug_optimize_iroptimized/input.json +++ b/test/cmdlineTests/standard_yul_ethdebug_optimize_iroptimized/input.json @@ -4,6 +4,7 @@ "C": {"urls": ["in.yul"]} }, "settings": { + "experimental": true, "optimizer": { "enabled": true }, diff --git a/test/cmdlineTests/standard_no_experimental_yul_ir_ast_requested/input.json b/test/cmdlineTests/standard_yul_ir_ast_no_experimental/input.json similarity index 100% rename from test/cmdlineTests/standard_no_experimental_yul_ir_ast_requested/input.json rename to test/cmdlineTests/standard_yul_ir_ast_no_experimental/input.json diff --git a/test/cmdlineTests/standard_yul_ir_ast_no_experimental/output.json b/test/cmdlineTests/standard_yul_ir_ast_no_experimental/output.json new file mode 100644 index 0000000000..c85e0af018 --- /dev/null +++ b/test/cmdlineTests/standard_yul_ir_ast_no_experimental/output.json @@ -0,0 +1,11 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "'irAst', 'irOptimizedAst', 'yulCFGJson', and 'ethdebug' outputs are experimental and can only be used with the 'settings.experimental' option enabled.", + "message": "'irAst', 'irOptimizedAst', 'yulCFGJson', and 'ethdebug' outputs are experimental and can only be used with the 'settings.experimental' option enabled.", + "severity": "error", + "type": "FatalError" + } + ] +} diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 85d6a39820..1078650541 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -66,7 +66,7 @@ evmasm::AssemblyItems compileContract(std::shared_ptr _sourceCode) BOOST_CHECK(!!sourceUnit); Scoper::assignScopes(*sourceUnit); - BOOST_REQUIRE(SyntaxChecker(errorReporter, false, false).checkSyntax(*sourceUnit)); + BOOST_REQUIRE(SyntaxChecker(errorReporter, false /* _useYulOptimizer */, false /* _experimental */).checkSyntax(*sourceUnit)); GlobalContext globalContext(solidity::test::CommonOptions::get().evmVersion()); NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter, false); DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion()); diff --git a/test/libsolidity/FunctionDependencyGraphTest.cpp b/test/libsolidity/FunctionDependencyGraphTest.cpp index ded643e682..ba03d6f11b 100644 --- a/test/libsolidity/FunctionDependencyGraphTest.cpp +++ b/test/libsolidity/FunctionDependencyGraphTest.cpp @@ -42,7 +42,7 @@ TestCase::TestResult FunctionDependencyGraphTest::run(std::ostream& _stream, std compiler().setSources(StringMap{{"", m_source}}); compiler().setViaIR(true); compiler().setOptimiserSettings(OptimiserSettings::none()); - // Experimental on by default as this is an extension of the initial experimental Solidity + // Experimental on by default as function dependency analysis is a part of Generic Solidity implementation compiler().setExperimental(true); if (!compiler().compile(CompilerStack::AnalysisSuccessful)) { diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index 8b54acc669..683522693c 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -104,7 +104,6 @@ BOOST_AUTO_TEST_CASE(metadata_stamp) compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize); compilerStack.setMetadataHash(metadataHash); - compilerStack.setExperimental(true); // Experimental pragma requires it BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); bytes const& bytecode = compilerStack.runtimeObject("test").bytecode; std::string const& metadata = compilerStack.metadata("test"); @@ -112,7 +111,7 @@ BOOST_AUTO_TEST_CASE(metadata_stamp) auto const cborMetadata = requireParsedCBORMetadata(bytecode, metadataFormat); if (metadataHash == CompilerStack::MetadataHash::None) - BOOST_CHECK(cborMetadata.size() == (metadataFormat == CompilerStack::MetadataFormat::NoMetadata ? 0 : 2)); + BOOST_CHECK(cborMetadata.size() == (metadataFormat == CompilerStack::MetadataFormat::NoMetadata ? 0 : 1)); else { bytes hash; @@ -132,25 +131,22 @@ BOOST_AUTO_TEST_CASE(metadata_stamp) if (metadataFormat != CompilerStack::MetadataFormat::NoMetadata) { - BOOST_CHECK(cborMetadata.size() == 3); + BOOST_CHECK(cborMetadata.size() == 2); BOOST_CHECK(cborMetadata.count(hashMethod) == 1); BOOST_CHECK(cborMetadata.at(hashMethod) == util::toHex(hash)); } } if (metadataFormat == CompilerStack::MetadataFormat::NoMetadata) - { - BOOST_CHECK(cborMetadata.count("solc") == 0); - BOOST_CHECK(cborMetadata.count("experimental") == 0); - } + BOOST_CHECK(cborMetadata.empty()); else { - BOOST_CHECK(cborMetadata.at("experimental") == "true"); BOOST_CHECK(cborMetadata.count("solc") == 1); if (metadataFormat == CompilerStack::MetadataFormat::WithReleaseVersionTag) BOOST_CHECK(cborMetadata.at("solc") == util::toHex(VersionCompactBytes)); else BOOST_CHECK(cborMetadata.at("solc") == VersionStringStrict); + BOOST_CHECK(!cborMetadata.contains("experimental")); } } } @@ -217,7 +213,7 @@ BOOST_AUTO_TEST_CASE(metadata_stamp_experimental) } if (metadataFormat == CompilerStack::MetadataFormat::NoMetadata) - BOOST_CHECK(cborMetadata.count("solc") == 0); + BOOST_CHECK(cborMetadata.empty()); else { BOOST_CHECK(cborMetadata.count("solc") == 1); @@ -225,7 +221,7 @@ BOOST_AUTO_TEST_CASE(metadata_stamp_experimental) BOOST_CHECK(cborMetadata.at("solc") == util::toHex(VersionCompactBytes)); else BOOST_CHECK(cborMetadata.at("solc") == VersionStringStrict); - BOOST_CHECK(cborMetadata.count("experimental") == 1); + BOOST_CHECK(cborMetadata.contains("experimental")); BOOST_CHECK(cborMetadata.at("experimental") == "true"); } } diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index d64ffac5b7..103732b987 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -126,7 +126,7 @@ bytes compileFirstExpression( ErrorReporter errorReporter(errors); GlobalContext globalContext(solidity::test::CommonOptions::get().evmVersion()); Scoper::assignScopes(*sourceUnit); - BOOST_REQUIRE(SyntaxChecker(errorReporter, false, false).checkSyntax(*sourceUnit)); + BOOST_REQUIRE(SyntaxChecker(errorReporter, false /* _useYulOptimizer */, false /* _experimental */).checkSyntax(*sourceUnit)); NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter, false); resolver.registerDeclarations(*sourceUnit); BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*sourceUnit), "Resolving names failed"); diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index cffddcfd0e..43b68223be 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -297,9 +297,9 @@ Json generateStandardJson(bool _viaIr, Json const& _debugInfoSelection, Json con return result; } -Json generateExperimentalStandardJson(bool _viaIr, Json const& _debugInfoSelection, Json const& _outputSelection, Code const& _code = SolidityCode(), bool _advancedOutputSelection = false) +Json generateExperimentalStandardJson(bool _viaIR, Json const& _debugInfoSelection, Json const& _outputSelection, Code const& _code = SolidityCode(), bool _advancedOutputSelection = false) { - Json result = generateStandardJson(_viaIr, _debugInfoSelection, _outputSelection, _code, _advancedOutputSelection); + Json result = generateStandardJson(_viaIR, _debugInfoSelection, _outputSelection, _code, _advancedOutputSelection); result["settings"]["experimental"] = true; return result; } @@ -2269,23 +2269,6 @@ BOOST_DATA_TEST_CASE(ethdebug_output_instructions_smoketest, boost::unit_test::d BOOST_AUTO_TEST_CASE(no_experimental_import_ast_solidity_evmasm) { frontend::StandardCompiler compiler; - { - char const* input = R"( - { - "language": "SolidityAST", - "sources": { - "A.sol": { - "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" - } - } - } - )"; - - Json parsedInput; - BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput)); - Json result = compiler.compile(parsedInput); - BOOST_CHECK(containsError(result, "FatalError", "'SolidityAST' and 'EVMAssembly' inputs are experimental, and can only be used by toggling the 'settings.experimental' option.")); - } { char const* input = R"( { @@ -2305,102 +2288,11 @@ BOOST_AUTO_TEST_CASE(no_experimental_import_ast_solidity_evmasm) Json parsedInput; BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput)); Json result = compiler.compile(parsedInput); - BOOST_CHECK(containsError(result, "FatalError", "'SolidityAST' and 'EVMAssembly' inputs are experimental, and can only be used by toggling the 'settings.experimental' option.")); + BOOST_CHECK(containsError(result, "FatalError", "'SolidityAST' and 'EVMAssembly' inputs are experimental and can only be used with the 'settings.experimental' option enabled.")); } } BOOST_AUTO_TEST_CASE(no_experimental_invalid_output_selection) -{ - frontend::StandardCompiler compiler; - { - char const* input = R"( - { - "language": "Solidity", - "sources": { - "A.sol": { - "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" - } - }, - "settings": { - "outputSelection": { - "A.sol": { - "A": ["irAst"] - } - } - } - } - )"; - - Json parsedInput; - BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput)); - Json result = compiler.compile(parsedInput); - BOOST_CHECK( - containsError( - result, - "FatalError", "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option." - ) - ); - } - { - char const* input = R"( - { - "language": "Solidity", - "sources": { - "A.sol": { - "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" - } - }, - "settings": { - "outputSelection": { - "A.sol": { - "A": ["irOptimizedAst"] - } - } - } - } - )"; - - Json parsedInput; - BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput)); - Json result = compiler.compile(parsedInput); - BOOST_CHECK( - containsError( - result, - "FatalError", "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option." - ) - ); - } - { - char const* input = R"( - { - "language": "Solidity", - "sources": { - "A.sol": { - "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" - } - }, - "settings": { - "outputSelection": { - "A.sol": { - "A": ["yulCFGJson"] - } - } - } - } - )"; - - Json parsedInput; - BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput)); - Json result = compiler.compile(parsedInput); - BOOST_CHECK( - containsError( - result, - "FatalError", "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option." - ) - ); } -} - -BOOST_AUTO_TEST_CASE(no_experimental_ethdebug) { frontend::StandardCompiler compiler; char const* input = R"( @@ -2412,13 +2304,9 @@ BOOST_AUTO_TEST_CASE(no_experimental_ethdebug) } }, "settings": { - "viaIR": true, - "debug": { - "debugInfo": ["ethdebug"] - }, "outputSelection": { "A.sol": { - "A": ["evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"] + "A": ["irOptimizedAst"] } } } @@ -2431,32 +2319,9 @@ BOOST_AUTO_TEST_CASE(no_experimental_ethdebug) BOOST_CHECK( containsError( result, - "FatalError", "'irAst', 'irOptimizedAst', 'yulCFGJson' and 'debugInfo.ethdebug' outputs are experimental, and can only be used by toggling the 'settings.experimental' option." + "FatalError", "'irAst', 'irOptimizedAst', 'yulCFGJson', and 'ethdebug' outputs are experimental and can only be used with the 'settings.experimental' option enabled." ) - );} - -BOOST_AUTO_TEST_CASE(no_experimental_eof) -{ - frontend::StandardCompiler compiler; - char const* input = R"( - { - "language": "Solidity", - "sources": { - "A.sol": { - "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" - } - }, - "settings": { - "eofVersion": 1, - "evmVersion": "osaka" - } - } - )"; - - Json parsedInput; - BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput)); - Json result = compiler.compile(parsedInput); - BOOST_CHECK(containsError(result, "FatalError", "'eofVersion' is experimental, and can only be used by toggling the 'settings.experimental' option.")); + ); } BOOST_AUTO_TEST_CASE(experimental_non_boolean) @@ -2479,7 +2344,7 @@ BOOST_AUTO_TEST_CASE(experimental_non_boolean) Json parsedInput; BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput)); Json result = compiler.compile(parsedInput); - BOOST_CHECK(containsError(result, "JSONError", "\"settings.experimental\" must be a Boolean.")); + BOOST_CHECK(containsError(result, "JSONError", "'settings.experimental' must be a Boolean.")); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/SyntaxTest.h b/test/libsolidity/SyntaxTest.h index 9c52acd9bc..946ada94c2 100644 --- a/test/libsolidity/SyntaxTest.h +++ b/test/libsolidity/SyntaxTest.h @@ -53,7 +53,7 @@ class SyntaxTest: public AnalysisFramework, public solidity::test::CommonSyntaxT virtual void filterObtainedErrors(); bool m_optimiseYul{}; - bool m_experimental{false}; + bool m_experimental{}; std::string m_compileViaYul{}; langutil::Error::Severity m_minSeverity{}; PipelineStage m_stopAfter; diff --git a/test/libsolidity/smtCheckerTests/options/pragma.sol b/test/libsolidity/smtCheckerTests/options/pragma.sol index af2f644ad5..b3e35ee022 100644 --- a/test/libsolidity/smtCheckerTests/options/pragma.sol +++ b/test/libsolidity/smtCheckerTests/options/pragma.sol @@ -4,8 +4,6 @@ contract C { assert(x > 0); } } -// ==== -// experimental: true // ---- // Warning 5523: (0-31): The SMTChecker pragma has been deprecated and will be removed in the future. Please use the "model checker engine" compiler setting to activate the SMTChecker instead. If the pragma is enabled, all engines will be used. // Warning 6328: (90-103): CHC: Assertion violation happens here.\nCounterexample:\n\nx = 0\n\nTransaction trace:\nC.constructor()\nC.f(0) diff --git a/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_fixed_bytes_from_string_1.sol b/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_fixed_bytes_from_string_1.sol index f31b9ca88e..aa03179599 100644 --- a/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_fixed_bytes_from_string_1.sol +++ b/test/libsolidity/smtCheckerTests/types/struct/struct_constructor_fixed_bytes_from_string_1.sol @@ -1,4 +1,3 @@ -pragma experimental SMTChecker; contract C { struct S { int a; @@ -9,9 +8,6 @@ contract C { assert(S({a:2, b:""}).a == 0); // should fail } } -// ==== -// experimental: true // ---- -// Warning 5523: (0-31): The SMTChecker pragma has been deprecated and will be removed in the future. Please use the "model checker engine" compiler setting to activate the SMTChecker instead. If the pragma is enabled, all engines will be used. -// Warning 6328: (178-207): CHC: Assertion violation happens here. +// Warning 6328: (146-175): CHC: Assertion violation happens here. // Info 1391: CHC: 1 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse.sol b/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse.sol index 19a29b524f..feacbbeb31 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse.sol @@ -1,7 +1,5 @@ pragma abicoder v1; pragma abicoder v2; -// ==== -// experimental: true // ---- // Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // SyntaxError 3845: (20-39): ABI coder has already been selected for this source unit. diff --git a/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse_experimental.sol b/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse_experimental.sol index 022aa76a22..884d03132e 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse_experimental.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/conflicting_settings_reverse_experimental.sol @@ -1,7 +1,5 @@ pragma abicoder v1; pragma experimental ABIEncoderV2; -// ==== -// experimental: true // ---- // Warning 9511: (0-19): ABI coder v1 is deprecated and scheduled for removal. Use ABI coder v2 instead. // SyntaxError 8273: (20-53): ABI coder v1 has already been selected through "pragma abicoder v1". diff --git a/test/libsolidity/syntaxTests/abiEncoder/same_setting_twice.sol b/test/libsolidity/syntaxTests/abiEncoder/same_setting_twice.sol index fcdc2d5bbc..0303bcb149 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/same_setting_twice.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/same_setting_twice.sol @@ -1,6 +1,4 @@ pragma experimental ABIEncoderV2; pragma abicoder v2; -// ==== -// experimental: true // ---- // SyntaxError 3845: (34-53): ABI coder has already been selected for this source unit. diff --git a/test/libsolidity/syntaxTests/abiEncoder/selected_twice_v2.sol b/test/libsolidity/syntaxTests/abiEncoder/selected_twice_v2.sol index 848efa83e9..0b296da3a8 100644 --- a/test/libsolidity/syntaxTests/abiEncoder/selected_twice_v2.sol +++ b/test/libsolidity/syntaxTests/abiEncoder/selected_twice_v2.sol @@ -1,5 +1,3 @@ pragma abicoder v2; pragma experimental ABIEncoderV2; -// ==== -// experimental: true // ---- diff --git a/test/libsolidity/syntaxTests/pragma/experimental_empty_string_literal.sol b/test/libsolidity/syntaxTests/pragma/experimental_empty_string_literal.sol index c97f464952..963ef5884b 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_empty_string_literal.sol +++ b/test/libsolidity/syntaxTests/pragma/experimental_empty_string_literal.sol @@ -1,5 +1,3 @@ pragma experimental ""; -// ==== -// experimental: true // ---- // SyntaxError 3250: (0-23): Empty experimental feature name is invalid. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_multiple_experimental.sol b/test/libsolidity/syntaxTests/pragma/experimental_multiple_experimental.sol index 204c4eada0..49fe597ec3 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_multiple_experimental.sol +++ b/test/libsolidity/syntaxTests/pragma/experimental_multiple_experimental.sol @@ -1,6 +1,4 @@ pragma experimental experimental __test; -// ==== -// experimental: true // ---- // SyntaxError 6022: (0-40): Stray arguments. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_multiple_same_line.sol b/test/libsolidity/syntaxTests/pragma/experimental_multiple_same_line.sol index f6210a871c..f88c0e0d84 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_multiple_same_line.sol +++ b/test/libsolidity/syntaxTests/pragma/experimental_multiple_same_line.sol @@ -1,5 +1,3 @@ pragma experimental unsupportedName unsupportedName; -// ==== -// experimental: true // ---- // SyntaxError 6022: (0-52): Stray arguments. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_pragma_empty.sol b/test/libsolidity/syntaxTests/pragma/experimental_pragma_empty.sol index 2360d4e4db..d45e7e6afe 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_pragma_empty.sol +++ b/test/libsolidity/syntaxTests/pragma/experimental_pragma_empty.sol @@ -1,5 +1,3 @@ pragma experimental; -// ==== -// experimental: true // ---- // SyntaxError 9679: (0-20): Experimental feature name is missing. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_pragma_unknown_string_literal.sol b/test/libsolidity/syntaxTests/pragma/experimental_pragma_unknown_string_literal.sol index 4a52ec831d..76be60ce8a 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_pragma_unknown_string_literal.sol +++ b/test/libsolidity/syntaxTests/pragma/experimental_pragma_unknown_string_literal.sol @@ -1,5 +1,3 @@ pragma experimental unsupportedName; -// ==== -// experimental: true // ---- // SyntaxError 8491: (0-36): Unsupported experimental feature name. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_pragma_without_experimental_mode.sol b/test/libsolidity/syntaxTests/pragma/experimental_pragma_without_experimental_mode.sol index d5ba45b701..1d5e0595e7 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_pragma_without_experimental_mode.sol +++ b/test/libsolidity/syntaxTests/pragma/experimental_pragma_without_experimental_mode.sol @@ -1,4 +1,7 @@ pragma experimental __test; +pragma experimental solidity; +// ==== +// EVMVersion: >=constantinople // ---- -// SyntaxError 2816: (0-27): Experimental pragmas can only be used if the experimental mode has been enabled. -// Warning 2264: (0-27): Experimental features are turned on. Do not use experimental features on live deployments. +// SyntaxError 2816: (0-27): Experimental pragmas can only be used if experimental mode is enabled. To enable experimental mode, use the --experimental flag. +// SyntaxError 2816: (28-57): Experimental pragmas can only be used if experimental mode is enabled. To enable experimental mode, use the --experimental flag. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_unknown_number_literal.sol b/test/libsolidity/syntaxTests/pragma/experimental_unknown_number_literal.sol index 4329b387b9..f8e8267a0d 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_unknown_number_literal.sol +++ b/test/libsolidity/syntaxTests/pragma/experimental_unknown_number_literal.sol @@ -1,5 +1,3 @@ pragma experimental 123; -// ==== -// experimental: true // ---- // SyntaxError 8491: (0-24): Unsupported experimental feature name. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_unknown_quoted_string_literal.sol b/test/libsolidity/syntaxTests/pragma/experimental_unknown_quoted_string_literal.sol index 9c69275107..a08a0489b2 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_unknown_quoted_string_literal.sol +++ b/test/libsolidity/syntaxTests/pragma/experimental_unknown_quoted_string_literal.sol @@ -1,5 +1,3 @@ pragma experimental "unsupportedName"; -// ==== -// experimental: true // ---- // SyntaxError 8491: (0-38): Unsupported experimental feature name. diff --git a/test/lsp.py b/test/lsp.py index 266a29fcc8..d5bff2132f 100755 --- a/test/lsp.py +++ b/test/lsp.py @@ -883,10 +883,10 @@ def main(self) -> int: print(f"{SGR_TEST_BEGIN}Testing {title} ...{SGR_RESET}") try: with JsonRpcProcess( - self.solc_path, - ["--experimental", "--lsp"], - trace_io=self.trace_io, - print_pid=self.print_solc_pid + self.solc_path, + ["--experimental", "--lsp"], + trace_io=self.trace_io, + print_pid=self.print_solc_pid ) as solc: test_fn(solc) self.test_counter.passed += 1 diff --git a/test/solc/CommandLineInterface.cpp b/test/solc/CommandLineInterface.cpp index d1c43be5bd..2141b9e634 100644 --- a/test/solc/CommandLineInterface.cpp +++ b/test/solc/CommandLineInterface.cpp @@ -1412,14 +1412,6 @@ BOOST_AUTO_TEST_CASE(cli_include_paths_ambiguous_import) BOOST_REQUIRE(!result.success); } -BOOST_AUTO_TEST_CASE(cli_ethdebug_no_ethdebug_in_help) -{ - OptionsReaderAndMessages result = runCLI({"solc", "--help"}); - BOOST_REQUIRE(result.stdoutContent.find("ethdebug") == std::string::npos); - // just in case - BOOST_REQUIRE(result.stderrContent.find("ethdebug") == std::string::npos); -} - BOOST_AUTO_TEST_CASE(cli_ethdebug_incompatible_outputs) { TemporaryDirectory tempDir(TEST_CASE_NAME); diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index 705ae3f73c..4a32b00446 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -702,7 +702,7 @@ BOOST_AUTO_TEST_CASE(experimental_features_without_experimental_flag) BOOST_AUTO_TEST_CASE(debug_info_ethdebug_without_experimental_flag) { - std::string const expectedErrorMessage { "--debug-info ethdebug can only be used by toggling the --experimental flag." }; + std::string const expectedErrorMessage {"Ethdebug annotations are experimental and can only be included in --debug-info in experimental mode. To enable experimental mode, use the --experimental flag."}; auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) { return _exception.what() == expectedErrorMessage; }; std::vector const commandLineOptions{"solc", "--debug-info", "ethdebug", "contract.sol"}; From c61a2aea2434c8ccdb9c574c03bd0b054bc9665c Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Tue, 3 Mar 2026 09:43:43 +0100 Subject: [PATCH 217/243] Help output changes --- docs/using-the-compiler.rst | 30 +++++--- libsolidity/interface/StandardCompiler.cpp | 4 +- solc/CommandLineParser.cpp | 71 +++++++++++-------- solc/CommandLineParser.h | 3 + .../err | 2 +- .../output.json | 4 +- .../output.json | 4 +- .../output.json | 4 +- test/solc/CommandLineParser.cpp | 26 ++++--- 9 files changed, 90 insertions(+), 58 deletions(-) diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 15f4f859a6..ce82aa0778 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -387,8 +387,11 @@ Input Description // - `` is the index of the first byte after that location. // - `snippet`: A single-line code snippet from the location indicated by `@src`. // The snippet is quoted and follows the corresponding `@src` annotation. - // - `*`: Wildcard value that can be used to request everything. - "debugInfo": ["location", "snippet"] + // - `ast-id`: Annotations of the form `@ast-id ` over elements that can be mapped back to a definition in the original Solidity file. + // `` is a node ID in the Solidity AST ('ast' output). + // - `ethdebug`: Ethdebug annotations (experimental). + // - `*`: Wildcard value that can be used to request all non-experimental components. + "debugInfo": ["location", "snippet", "ast-id", "ethdebug"] }, // Metadata settings (optional) "metadata": { @@ -439,13 +442,15 @@ Input Description // userdoc - User documentation (natspec) // metadata - Metadata // ir - Yul intermediate representation of the code before optimization - // irAst - AST of Yul intermediate representation of the code before optimization + // irAst - AST of Yul intermediate representation of the code before optimization (experimental) // irOptimized - Intermediate representation after optimization - // irOptimizedAst - AST of intermediate representation after optimization - // storageLayout - Slots, offsets and types of the contract's state variables in storage. - // transientStorageLayout - Slots, offsets and types of the contract's state variables in transient storage. + // irOptimizedAst - AST of intermediate representation after optimization (experimental) + // storageLayout - Slots, offsets and types of the contract's state variables in storage + // transientStorageLayout - Slots, offsets and types of the contract's state variables in transient storage // evm.assembly - New assembly format // evm.legacyAssembly - Old-style assembly format in JSON + // evm.bytecode.ethdebug - Debug information in ethdebug format (ethdebug/format/program schema). Can only be requested when compiling via IR. (experimental) + // evm.deployedBytecode.ethdebug - Like evm.bytecode.ethdebug, but for the runtime part of the contract (experimental) // evm.bytecode.functionDebugData - Debugging information at function level // evm.bytecode.object - Bytecode object // evm.bytecode.opcodes - Opcodes list @@ -456,6 +461,7 @@ Input Description // evm.deployedBytecode.immutableReferences - Map from AST ids to bytecode ranges that reference immutables // evm.methodIdentifiers - The list of function hashes // evm.gasEstimates - Function gas estimates + // yulCFGJson - Control Flow Graph (CFG) of the Single Static Assignment (SSA) form of the contract (experimental) // // Note that using `evm`, `evm.bytecode`, etc. will select every // target part of that output. Additionally, `*` can be used as a wildcard to request everything. @@ -608,6 +614,8 @@ Output Description "legacyAssembly": {}, // Bytecode and related details. "bytecode": { + // Ethdebug output (experimental) + "ethdebug": {/* ... */}, // Debugging data at the level of functions. "functionDebugData": { // Now follows a set of functions including compiler-internal and @@ -650,6 +658,8 @@ Output Description } }, "deployedBytecode": { + // Ethdebug output (experimental) + "ethdebug": {/* ... */}, /* ..., */ // The same layout as above. "immutableReferences": { // There are two references to the immutable with AST ID 3, both 32 bytes long. One is @@ -674,11 +684,15 @@ Output Description "internal": { "heavyLifting()": "infinite" } - } + }, + // Yul CFG representation of the SSA form (experimental) + "yulCFGJson": {/* ... */} } } } - } + }, + // Global Ethdebug output (experimental) + "ethdebug": {/* ... */ } } diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 20e3fd45ce..74307bbc05 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -300,7 +300,7 @@ bool isEthdebugRequested(Json const& _outputSelection) if (!_outputSelection.is_object()) return false; - static std::array const ethdebugArtifacts{"ethdebug", "evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"}; + static std::array const ethdebugArtifacts{"evm.bytecode.ethdebug", "evm.deployedBytecode.ethdebug"}; for (auto const& fileRequests: _outputSelection) for (auto const& requests: fileRequests) @@ -1233,7 +1233,7 @@ std::variant StandardCompiler::parseI if (ret.debugInfoSelection.has_value() && ret.debugInfoSelection->ethdebug) { if (!ret.experimental) - return formatFatalError(Error::Type::FatalError, "Ethdebug annotations are experimental and can only be included in ‘settings.debug.debugInfo’ by enabling the ‘settings.experimental’ option."); + return formatFatalError(Error::Type::FatalError, "Ethdebug annotations are experimental and can only be included in 'settings.debug.debugInfo' by enabling the 'settings.experimental' option."); if (!pipelineConfig(ret.outputSelection)[""][""].irCodegen && !isEthdebugRequested(ret.outputSelection)) return formatFatalError(Error::Type::FatalError, "'settings.debug.debugInfo' can only include 'ethdebug', if output 'ir', 'irOptimized', 'evm.bytecode.ethdebug', or 'evm.deployedBytecode.ethdebug' was selected."); } diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index b11adf0373..7f143695d5 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -167,7 +167,7 @@ void CommandLineParser::checkExperimental(std::vector const& _optio fmt::format( "The following options are only available in experimental mode: {}. " "To enable experimental mode, use the --{} flag.", - joinOptionNames(_optionNames), + joinOptionNames(enabledOptions(_optionNames)), g_strExperimental ) ); @@ -436,6 +436,12 @@ void CommandLineParser::parseLibraryOption(std::string const& _input) } } +std::vector CommandLineParser::enabledOptions(std::vector const& _optionList) const +{ + auto optionEnabled = [&](std::string const& _option) { return m_args.contains(_option); }; + return _optionList | ranges::views::filter(optionEnabled) | ranges::to_vector; +} + void CommandLineParser::parseOutputSelection() { static auto outputSupported = [](InputMode _mode, std::string_view _outputName) @@ -578,7 +584,7 @@ General Information)").c_str(), std::string annotatedEVMVersions = util::joinHumanReadable( allEVMVersions | ranges::views::transform(annotateEVMVersion), ", ", - " or " + ", or " ); po::options_description outputOptions("Output Options"); @@ -603,14 +609,14 @@ General Information)").c_str(), g_strEOFVersion.c_str(), // Declared as uint64_t, since uint8_t will be parsed as character by boost. po::value()->value_name("version")->implicit_value(1), - "Select desired EOF version. Currently the only valid value is 1. " - "If not specified, legacy non-EOF bytecode will be generated." + "(experimental) Select desired EOF version. Currently the only valid value is 1. " + "If not specified, non-EOF bytecode will be generated." ) ( - g_strYul.c_str(), "The typed Yul dialect is no longer supported. For regular Yul compilation use --strict-assembly instead." + g_strYul.c_str(), + "(disabled) Switch to typed Yul mode. The typed Yul dialect is no longer supported. " + "For regular Yul compilation use --strict-assembly instead." ) - ; - outputOptions.add_options() ( g_strExperimentalViaIR.c_str(), "Deprecated synonym of --via-ir." @@ -628,8 +634,10 @@ General Information)").c_str(), g_strDebugInfo.c_str(), po::value()->default_value(util::toString(DebugInfoSelection::Default())), ("Debug info components to be included in the produced EVM assembly and Yul code. " - "Value can be all, none or a comma-separated list containing one or more of the " - "following components: " + util::joinHumanReadable(DebugInfoSelection::Default().selectedNames()) + ".").c_str() + "Value can be 'all', 'none' or a comma-separated list containing one or more of the " + "following components: " + util::joinHumanReadable(DebugInfoSelection::AllExceptExperimental().selectedNames()) + + ", or ethdebug (experimental). " + "Note that 'all' does not include experimental components.").c_str() ) ( g_strStopAfter.c_str(), @@ -661,21 +669,25 @@ General Information)").c_str(), ) ( g_strImportAst.c_str(), - ("Import ASTs to be compiled, assumes input holds the AST in compact JSON format. " - "Supported Inputs is the output of the --" + g_strStandardJSON + " or the one produced by " - "--" + g_strCombinedJson + " " + CombinedJsonRequests::componentName(&CombinedJsonRequests::ast)).c_str() + ("(experimental) Resume compilation from an abstract syntax tree (AST) representing already parsed and analyzed Solidity code. " + "In this mode the compiler expects exactly one input file, containing an AST in compact JSON format, as produced by" + " --" + CompilerOutputs::componentName(&CompilerOutputs::astCompactJson) + ", --" + + CombinedJsonRequests::componentName(&CombinedJsonRequests::ast) + " ast or the 'ast' output in Standard JSON.").c_str() ) ( g_strImportEvmAssemblerJson.c_str(), - ("Import EVM assembly in JSON format produced by --asm-json. " - "WARNING: --asm-json output is already optimized according to settings stored in metadata. " + ("(experimental) Resume compilation from EVM assembly. " + "In this mode the compiler expects exactly one input file, containing assembly in JSON format, " + "as produced by --" + g_strImportEvmAssemblerJson + "or the 'evm.legacyAssembly' output in Standard JSON. " + "WARNING: --" + g_strImportEvmAssemblerJson + " output is already optimized according to settings stored in metadata. " "Using --" + g_strOptimize + " in this mode is allowed, but not necessary under normal circumstances. " - "It forces the optimizer to run again and can produce bytecode that is not reproducible from metadata.").c_str() + "--" + g_strOptimize + " forces the optimizer to run again and can produce bytecode that is not reproducible from metadata.").c_str() ) ( g_strLSP.c_str(), - "Switch to language server mode (\"LSP\"). Allows the compiler to be used as an analysis backend " - "for your favourite IDE." + "(experimental) Switch to the language server mode. " + "Allows the compiler to be used as an analysis backend for your favourite IDE. " + "In this mode no input files are accepted and the compiler expects language server protocol (LSP) messages on standard input." ) ; desc.add(alternativeInputModes); @@ -743,30 +755,27 @@ General Information)").c_str(), (CompilerOutputs::componentName(&CompilerOutputs::binaryRuntime).c_str(), "Binary of the runtime part of the contracts in hex.") (CompilerOutputs::componentName(&CompilerOutputs::abi).c_str(), "ABI specification of the contracts.") (CompilerOutputs::componentName(&CompilerOutputs::ir).c_str(), "Intermediate Representation (IR) of all contracts.") - (CompilerOutputs::componentName(&CompilerOutputs::irAstJson).c_str(), "AST of Intermediate Representation (IR) of all contracts in a compact JSON format.") + (CompilerOutputs::componentName(&CompilerOutputs::irAstJson).c_str(), "(experimental) AST of Intermediate Representation (IR) of all contracts in a compact JSON format.") (CompilerOutputs::componentName(&CompilerOutputs::irOptimized).c_str(), "Optimized Intermediate Representation (IR) of all contracts.") - (CompilerOutputs::componentName(&CompilerOutputs::irOptimizedAstJson).c_str(), "AST of optimized Intermediate Representation (IR) of all contracts in a compact JSON format.") + (CompilerOutputs::componentName(&CompilerOutputs::irOptimizedAstJson).c_str(), "(experimental) AST of optimized Intermediate Representation (IR) of all contracts in a compact JSON format.") (CompilerOutputs::componentName(&CompilerOutputs::signatureHashes).c_str(), "Function signature hashes of the contracts.") (CompilerOutputs::componentName(&CompilerOutputs::natspecUser).c_str(), "Natspec user documentation of all contracts.") (CompilerOutputs::componentName(&CompilerOutputs::natspecDev).c_str(), "Natspec developer documentation of all contracts.") (CompilerOutputs::componentName(&CompilerOutputs::metadata).c_str(), "Combined Metadata JSON whose IPFS hash is stored on-chain.") (CompilerOutputs::componentName(&CompilerOutputs::storageLayout).c_str(), "Slots, offsets and types of the contract's state variables located in storage.") (CompilerOutputs::componentName(&CompilerOutputs::transientStorageLayout).c_str(), "Slots, offsets and types of the contract's state variables located in transient storage.") - ; - outputComponents.add_options() ( CompilerOutputs::componentName(&CompilerOutputs::yulCFGJson).c_str(), - "Control Flow Graph (CFG) of Yul code in JSON format." - ); - outputComponents.add_options() + "(experimental) Control Flow Graph (CFG) of Yul code in Static Single Assignment (SSA) form in JSON format." + ) ( CompilerOutputs::componentName(&CompilerOutputs::ethdebug).c_str(), - "Ethdebug output of all contracts." - ); - outputComponents.add_options() + "(experimental) Debug information in ethdebug format for all contracts (ethdebug/format/program schema). " + "When enabled, an extra global output containing the ethdebug/format/info/resources schema with information shared by all contracts is automatically enabled as well." + ) ( CompilerOutputs::componentName(&CompilerOutputs::ethdebugRuntime).c_str(), - "Ethdebug output of the runtime part of all contracts." + ("(experimental) Like --" + CompilerOutputs::componentName(&CompilerOutputs::ethdebug) + ", but for the runtime part of the contracts.").c_str() ); desc.add(outputComponents); @@ -914,9 +923,9 @@ General Information)").c_str(), experimentalOptions.add_options() ( g_strExperimental.c_str(), - "Enable experimental mode. Note that enabling experimental mode by itself will not " - "enable any experimental features, but will simply allow for such features to be enabled in the " - "usual manner, whether by using specific flags or pragmas." + "Enable experimental mode. " + "This flag does not activate any experimental features on its own - " + "it unlocks the ability to use them through the usual flags and pragmas." ) ; desc.add(experimentalOptions); diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index bdb5fef2fd..9d9dc28b6a 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -324,6 +324,9 @@ class CommandLineParser void parseOutputSelection(); + /// Returns a list of enabled options from @_optionList + std::vector enabledOptions(std::vector const& _optionList) const; + void checkMutuallyExclusive(std::vector const& _optionNames); void checkExperimental(std::vector const& _optionNames) const; size_t countEnabledOptions(std::vector const& _optionNames) const; diff --git a/test/cmdlineTests/experimental_feature_without_experimental_flag/err b/test/cmdlineTests/experimental_feature_without_experimental_flag/err index 1b73066ce6..b21b7318e1 100644 --- a/test/cmdlineTests/experimental_feature_without_experimental_flag/err +++ b/test/cmdlineTests/experimental_feature_without_experimental_flag/err @@ -1 +1 @@ -Error: The following options are only available in experimental mode: --lsp, --import-ast, --import-asm-json, --ir-ast-json, --ir-optimized-ast-json, --yul-cfg-json, --ethdebug, --ethdebug-runtime, --experimental-eof-version. To enable experimental mode, use the --experimental flag. +Error: The following options are only available in experimental mode: --ir-ast-json. To enable experimental mode, use the --experimental flag. diff --git a/test/cmdlineTests/standard_output_debuginfo_ethdebug_incompatible/output.json b/test/cmdlineTests/standard_output_debuginfo_ethdebug_incompatible/output.json index 9342b91f51..ab744acc1d 100644 --- a/test/cmdlineTests/standard_output_debuginfo_ethdebug_incompatible/output.json +++ b/test/cmdlineTests/standard_output_debuginfo_ethdebug_incompatible/output.json @@ -2,8 +2,8 @@ "errors": [ { "component": "general", - "formattedMessage": "Ethdebug annotations are experimental and can only be included in \u2018settings.debug.debugInfo\u2019 by enabling the \u2018settings.experimental\u2019 option.", - "message": "Ethdebug annotations are experimental and can only be included in \u2018settings.debug.debugInfo\u2019 by enabling the \u2018settings.experimental\u2019 option.", + "formattedMessage": "Ethdebug annotations are experimental and can only be included in 'settings.debug.debugInfo' by enabling the 'settings.experimental' option.", + "message": "Ethdebug annotations are experimental and can only be included in 'settings.debug.debugInfo' by enabling the 'settings.experimental' option.", "severity": "error", "type": "FatalError" } diff --git a/test/cmdlineTests/standard_output_selection_ethdebug_no_experimental/output.json b/test/cmdlineTests/standard_output_selection_ethdebug_no_experimental/output.json index 9342b91f51..ab744acc1d 100644 --- a/test/cmdlineTests/standard_output_selection_ethdebug_no_experimental/output.json +++ b/test/cmdlineTests/standard_output_selection_ethdebug_no_experimental/output.json @@ -2,8 +2,8 @@ "errors": [ { "component": "general", - "formattedMessage": "Ethdebug annotations are experimental and can only be included in \u2018settings.debug.debugInfo\u2019 by enabling the \u2018settings.experimental\u2019 option.", - "message": "Ethdebug annotations are experimental and can only be included in \u2018settings.debug.debugInfo\u2019 by enabling the \u2018settings.experimental\u2019 option.", + "formattedMessage": "Ethdebug annotations are experimental and can only be included in 'settings.debug.debugInfo' by enabling the 'settings.experimental' option.", + "message": "Ethdebug annotations are experimental and can only be included in 'settings.debug.debugInfo' by enabling the 'settings.experimental' option.", "severity": "error", "type": "FatalError" } diff --git a/test/cmdlineTests/standard_yul_ethdebug_no_experimental/output.json b/test/cmdlineTests/standard_yul_ethdebug_no_experimental/output.json index 9342b91f51..ab744acc1d 100644 --- a/test/cmdlineTests/standard_yul_ethdebug_no_experimental/output.json +++ b/test/cmdlineTests/standard_yul_ethdebug_no_experimental/output.json @@ -2,8 +2,8 @@ "errors": [ { "component": "general", - "formattedMessage": "Ethdebug annotations are experimental and can only be included in \u2018settings.debug.debugInfo\u2019 by enabling the \u2018settings.experimental\u2019 option.", - "message": "Ethdebug annotations are experimental and can only be included in \u2018settings.debug.debugInfo\u2019 by enabling the \u2018settings.experimental\u2019 option.", + "formattedMessage": "Ethdebug annotations are experimental and can only be included in 'settings.debug.debugInfo' by enabling the 'settings.experimental' option.", + "message": "Ethdebug annotations are experimental and can only be included in 'settings.debug.debugInfo' by enabling the 'settings.experimental' option.", "severity": "error", "type": "FatalError" } diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index 4a32b00446..6d91533816 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -682,27 +682,33 @@ BOOST_AUTO_TEST_CASE(experimental_features_without_experimental_flag) "--ethdebug-runtime" }; - std::string const expectedErrorMessage { - "The following options are only available in experimental mode: --lsp, --import-ast, --import-asm-json, " - "--ir-ast-json, --ir-optimized-ast-json, --yul-cfg-json, --ethdebug, --ethdebug-runtime, --experimental-eof-version. " - "To enable experimental mode, use the --experimental flag." - }; - + std::string expectedErrorMessage; auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) { return _exception.what() == expectedErrorMessage; }; for (auto const& experimentalFeature: experimentalFeatures) { + expectedErrorMessage = + fmt::format( + "The following options are only available in experimental mode: {}. " + "To enable experimental mode, use the --experimental flag.", + experimentalFeature + ); + std::vector const commandLineOptions{"solc", experimentalFeature, "contract.sol"}; BOOST_CHECK_EXCEPTION(parseCommandLine(commandLineOptions), CommandLineValidationError, hasCorrectMessage); } std::vector const commandLineOptions{"solc", "--experimental-eof-version", "1", "contract.sol"}; + expectedErrorMessage = "The following options are only available in experimental mode: --experimental-eof-version. To enable experimental mode, use the --experimental flag."; BOOST_CHECK_EXCEPTION(parseCommandLine(commandLineOptions), CommandLineValidationError, hasCorrectMessage); } BOOST_AUTO_TEST_CASE(debug_info_ethdebug_without_experimental_flag) { - std::string const expectedErrorMessage {"Ethdebug annotations are experimental and can only be included in --debug-info in experimental mode. To enable experimental mode, use the --experimental flag."}; + std::string const expectedErrorMessage = + "Ethdebug annotations are experimental and can only be included in --debug-info in experimental mode. " + "To enable experimental mode, use the --experimental flag."; + auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) { return _exception.what() == expectedErrorMessage; }; std::vector const commandLineOptions{"solc", "--debug-info", "ethdebug", "contract.sol"}; @@ -711,10 +717,10 @@ BOOST_AUTO_TEST_CASE(debug_info_ethdebug_without_experimental_flag) BOOST_AUTO_TEST_CASE(experimental_flag_with_standard_json) { - std::string const expectedErrorMessage { + std::string const expectedErrorMessage = "Standard JSON input mode is incompatible with the --experimental flag. " - "Instead, please use the 'settings.experimental' setting in your Standard JSON input file to enable experimental mode." - }; + "Instead, please use the 'settings.experimental' setting in your Standard JSON input file to enable experimental mode."; + auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) { return _exception.what() == expectedErrorMessage; }; std::vector const commandLineOptions{"solc", "--experimental", "--standard-json", "input.json"}; From 5bd7117aa02a3fec90ecc55bc97cff9e1b50c668 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Wed, 11 Feb 2026 13:55:17 +0100 Subject: [PATCH 218/243] Add SSA CFG code transform --- libyul/CMakeLists.txt | 2 + libyul/backends/evm/ssa/CodeTransform.cpp | 385 ++++++++++++++++++ libyul/backends/evm/ssa/CodeTransform.h | 144 +++++++ .../backends/evm/ssa/StackLayoutGenerator.cpp | 65 ++- .../backends/evm/ssa/StackLayoutGenerator.h | 12 +- test/libyul/ssa/StackLayoutGeneratorTest.cpp | 3 +- 6 files changed, 575 insertions(+), 36 deletions(-) create mode 100644 libyul/backends/evm/ssa/CodeTransform.cpp create mode 100644 libyul/backends/evm/ssa/CodeTransform.h diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index d52dbba0db..87cf8aaa48 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -74,6 +74,8 @@ add_library(yul backends/evm/VariableReferenceCounter.h backends/evm/VariableReferenceCounter.cpp backends/evm/ssa/BridgeFinder.h + backends/evm/ssa/CodeTransform.cpp + backends/evm/ssa/CodeTransform.h backends/evm/ssa/ControlFlow.cpp backends/evm/ssa/ControlFlow.h backends/evm/ssa/JunkAdmittingBlocksFinder.cpp diff --git a/libyul/backends/evm/ssa/CodeTransform.cpp b/libyul/backends/evm/ssa/CodeTransform.cpp new file mode 100644 index 0000000000..6dc33ad7b3 --- /dev/null +++ b/libyul/backends/evm/ssa/CodeTransform.cpp @@ -0,0 +1,385 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +using namespace solidity::yul; +using namespace solidity::yul::ssa; + +namespace +{ +void assertLayoutCompatibility(StackData const& _layout1, StackData const& _layout2) +{ + auto const compatibility = checkLayoutCompatibility(_layout1, _layout2); + yulAssert(compatibility.ok(), compatibility.formatErrors()); +} +} + +void CodeTransform::run +( + AbstractAssembly& _assembly, + ControlFlowLiveness const& _controlFlowLiveness, + BuiltinContext& _builtinContext +) +{ + yulAssert(!_controlFlowLiveness.cfgLiveness.empty()); + ControlFlow const& controlFlow = _controlFlowLiveness.controlFlow.get(); + yulAssert(controlFlow.functionGraphs.size() == _controlFlowLiveness.cfgLiveness.size()); + FunctionLabels const functionLabels = registerFunctionLabels(_assembly, controlFlow); + + for (std::size_t functionIndex = 0; functionIndex < controlFlow.functionGraphMapping.size(); ++functionIndex) + { + auto const& [function, cfg] = controlFlow.functionGraphMapping[functionIndex]; + yulAssert(cfg); + auto const callSites = gatherCallSites(*cfg); + auto const& liveness = _controlFlowLiveness.cfgLiveness[functionIndex]; + yulAssert(liveness); + auto const graphID = static_cast(functionIndex); + auto const& stackLayout = StackLayoutGenerator::generate(*liveness, callSites, graphID); + CodeTransform transform( + _assembly, + _builtinContext, + functionLabels, + callSites, + *cfg, + stackLayout, + function, + graphID + ); + transform(cfg->entry); + } +} + +CodeTransform::FunctionLabels CodeTransform::registerFunctionLabels( + AbstractAssembly& _assembly, ControlFlow const& _controlFlow) +{ + FunctionLabels functionLabels; + std::set assignedFunctionNames; + + for (auto const& [_function, _functionGraph]: _controlFlow.functionGraphMapping) + { + if (!_function) + continue; + bool nameAlreadySeen = !assignedFunctionNames.insert(_function->name).second; + functionLabels[_function] = !nameAlreadySeen ? + _assembly.namedLabel( + _function->name.str(), + _functionGraph->arguments.size(), + _functionGraph->returns.size(), + _functionGraph->debugData ? _functionGraph->debugData->astID : std::nullopt + ) : + _assembly.newLabelId(); + } + return functionLabels; +} + +CodeTransform::CodeTransform( + AbstractAssembly& _assembly, + BuiltinContext& _builtinContext, + FunctionLabels const& _functionLabels, + CallSites const& _callSites, + SSACFG const& _cfg, + SSACFGStackLayout const& _stackLayout, + Scope::Function const* _function, + ControlFlow::FunctionGraphID +): + m_assembly(_assembly), + m_builtinContext(_builtinContext), + m_functionLabels(_functionLabels), + m_callSites(_callSites), + m_cfg(_cfg), + m_stackLayout(_stackLayout), + m_blockIsTransformed(_cfg.numBlocks(), false), + m_blockLabels([this] { + std::vector blockLabels; + blockLabels.reserve(m_cfg.numBlocks()); + for (std::size_t i = 0; i < m_cfg.numBlocks(); ++i) + blockLabels.push_back(m_assembly.newLabelId()); + return blockLabels; + }()), + m_assemblyCallbacks{ + .cfg = &_cfg, + .assembly = &_assembly, + .callSites = &_callSites, + .returnLabels = &m_returnLabels + }, + m_stackData([&] + { + auto const& entryLayout = m_stackLayout[m_cfg.entry]; + yulAssert(entryLayout); + return entryLayout->stackIn; + }()), + m_stack(m_stackData, m_assemblyCallbacks) +{ + if (_function) + { + auto const findIt = m_functionLabels.find(_function); + yulAssert(findIt != m_functionLabels.end()); + m_assembly.appendLabel(findIt->second); + m_assembly.setStackHeight(static_cast(_function->numArguments)); + } + StackData expectedStackTop; + expectedStackTop.reserve(m_cfg.arguments.size()); + for (auto const& [_, valueID]: m_cfg.arguments | ranges::views::reverse) + expectedStackTop.push_back(StackSlot::makeValueID(valueID)); + assertLayoutCompatibility(m_stack.data(), expectedStackTop); +} + +void CodeTransform::operator()(SSACFG::BlockId const _blockId) +{ + yulAssert(!m_blockIsTransformed[_blockId.value], "Each block is transformed exactly once."); + m_blockIsTransformed[_blockId.value] = true; + + m_assembly.appendLabel(m_blockLabels[_blockId.value]); + + auto const& blockLayout = m_stackLayout[_blockId]; + yulAssert(blockLayout); + assertLayoutCompatibility(m_stack.data(), blockLayout->stackIn); + yulAssert(static_cast(m_stack.size()) == m_assembly.stackHeight()); + + auto const& block = m_cfg.block(_blockId); + yulAssert(block.operations.size() == blockLayout->operationIn.size(), "We need as many operation stack layouts as we have operations"); + + for (std::size_t operationIndex = 0; operationIndex < block.operations.size(); ++operationIndex) + { + SSACFG::Operation const& operation = block.operations[operationIndex]; + auto const& operationInLayout = blockLayout->operationIn[operationIndex]; + + // perform the operation + (*this)(operation, operationInLayout); + } + + // Shuffle to the block's exit layout before dispatching the exit. + // This ensures the condition is on top for ConditionalJump, phi pre-images are + // in the right positions for jumps, and return values are accessible for FunctionReturn. + StackShuffler::shuffle(m_stack, blockLayout->stackOut); + + // handle the block exit + std::visit(util::GenericVisitor{ [this, &_blockId](auto const& exit) { (*this)(_blockId, exit); } }, block.exit); +} + +void CodeTransform::operator()(SSACFG::Operation const& _operation, StackData const& _operationInputLayout) +{ + bool const hasReturnLabel = + std::holds_alternative(_operation.kind) && + std::get(_operation.kind).canContinue; + + if (hasReturnLabel) + { + auto const [it, inserted] = m_returnLabels.try_emplace(&std::get(_operation.kind).call.get(), 0); + yulAssert(inserted, "Call sites should be unique."); + it->second = m_assembly.newLabelId(); + } + + // check that the assembly stack height corresponds to the stack size before shuffling + yulAssert(static_cast(m_stack.size()) == m_assembly.stackHeight()); + + // prepare stack for operation + StackShuffler::shuffle(m_stack, _operationInputLayout); + + // check that the assembly stack height corresponds to the stack size after shuffling + yulAssert(static_cast(m_stack.size()) == m_assembly.stackHeight()); + + // check that the stack is compatible with the operation input layout + assertLayoutCompatibility(m_stack.data(), _operationInputLayout); + + // Assert that we have the inputs of the operation on stack top. + yulAssert(m_stack.size() >= _operation.inputs.size()); + for (auto const& [stackEntry, input]: ranges::views::zip( + m_stack | ranges::views::take_last(_operation.inputs.size()), + _operation.inputs + )) + yulAssert(stackEntry.isValueID() && stackEntry.valueID() == input); + + // if the function can continue (doesn't always abort), make sure we have the correct return label slot in place + if (hasReturnLabel) + { + yulAssert(m_stack.size() > _operation.inputs.size()); + auto const returnLabelSlot = m_stack.slot(StackDepth{_operation.inputs.size()}); + yulAssert(std::holds_alternative(_operation.kind)); + yulAssert( + returnLabelSlot.isFunctionCallReturnLabel() && + &m_callSites.functionCall(returnLabelSlot.functionCallReturnLabel()) == &std::get(_operation.kind).call.get() + ); + } + + // height of the stack sans function return label and operation inputs + std::size_t const baseHeight = m_stack.size() - _operation.inputs.size() - (hasReturnLabel ? 1 : 0); + + // generate code for the operation + std::visit(util::GenericVisitor{ + [&](SSACFG::BuiltinCall const& _builtin) { + m_assembly.setSourceLocation(originLocationOf(_builtin)); + static_cast(_builtin.builtin.get()).generateCode( + _builtin.call, + m_assembly, + m_builtinContext + ); + }, + [&](SSACFG::Call const& _call) { + auto const* returnLabel = util::valueOrNullptr(m_returnLabels, &_call.call.get()); + // check that if we have a return label, the call can continue + yulAssert(!!returnLabel == _call.canContinue); + m_assembly.setSourceLocation(originLocationOf(_call)); + m_assembly.appendJumpTo( + m_functionLabels.at(&_call.function.get()), + static_cast(_call.function.get().numReturns - _call.function.get().numArguments) - (_call.canContinue ? 1 : 0), + AbstractAssembly::JumpType::IntoFunction + ); + // if we have a return label, append it to assembly and pop the label from the stack + // it might also be one of the inputs that is popped here but then the label will be popped below with + // the other inputs + if (returnLabel) + { + m_assembly.appendLabel(*returnLabel); + m_stack.pop(); + } + }, + [&](SSACFG::LiteralAssignment const&){} + }, _operation.kind); + // simulate that the inputs are consumed + for (size_t i = 0; i < _operation.inputs.size(); ++i) + m_stack.pop(); + // simulate that the outputs are produced + for (auto value: _operation.outputs) + m_stack.push(StackSlot::makeValueID(value)); + + // Assert that the operation produced its proclaimed output. + yulAssert(m_stack.size() == baseHeight + _operation.outputs.size()); + for (auto const& [stackEntry, output]: ranges::views::zip( + m_stack.data() | ranges::views::take_last(_operation.outputs.size()), + _operation.outputs + )) + yulAssert(stackEntry.isValueID() && stackEntry.valueID() == output); + yulAssert( + static_cast(m_stack.size()) == m_assembly.stackHeight(), + fmt::format("symbolic stack size = {} =/= {} = assembly stack height", m_stack.size(), m_assembly.stackHeight()) + ); +} + +void CodeTransform::operator()(SSACFG::BlockId const&, SSACFG::BasicBlock::MainExit const&) +{ + yulAssert(static_cast(m_stack.size()) == m_assembly.stackHeight()); + m_assembly.appendInstruction(evmasm::Instruction::STOP); +} + +void CodeTransform::operator()(SSACFG::BlockId const& _currentBlock, SSACFG::BasicBlock::ConditionalJump const& _conditionalJump) +{ + yulAssert(static_cast(m_stack.size()) == m_assembly.stackHeight()); + // condition must be at the top of the stack + yulAssert(m_stack.top().isValueID() && m_stack.top().valueID() == _conditionalJump.condition); + // emit JUMPI to nonZero block + m_assembly.appendJumpToIf(m_blockLabels[_conditionalJump.nonZero.value]); + // update symbolic stack by popping the condition as it'll be consumed by JUMPI + m_stack.pop(); + + { + // restore stack to previous state once zero-path is handled + ScopedSaveAndRestore restoreStack(m_stackData, StackData(m_stackData)); + yulAssert(m_stackLayout[_conditionalJump.zero]); + + // transform stack to a state in which we can jump to the zero branch + prepareBlockExitStack( + m_stackLayout[_conditionalJump.zero]->stackIn, + PhiInverse(m_cfg, _currentBlock, _conditionalJump.zero) + ); + assertLayoutCompatibility(m_stack.data(), m_stackLayout[_conditionalJump.zero]->stackIn); + m_assembly.appendJumpTo(m_blockLabels[_conditionalJump.zero.value]); + + if (!m_blockIsTransformed[_conditionalJump.zero.value]) + (*this)(_conditionalJump.zero); + } + { + yulAssert(m_stackLayout[_conditionalJump.nonZero]); + assertLayoutCompatibility(m_stack.data(), m_stackLayout[_conditionalJump.nonZero]->stackIn); + + m_assembly.setStackHeight(static_cast(m_stack.size())); + if (!m_blockIsTransformed[_conditionalJump.nonZero.value]) + (*this)(_conditionalJump.nonZero); + } +} + +void CodeTransform::operator()(SSACFG::BlockId const& _currentBlock, SSACFG::BasicBlock::Jump const& _jump) +{ + yulAssert(static_cast(m_stack.size()) == m_assembly.stackHeight()); + yulAssert(m_stackLayout[_jump.target]); + prepareBlockExitStack(m_stackLayout[_jump.target]->stackIn, PhiInverse(m_cfg, _currentBlock, _jump.target)); + assertLayoutCompatibility(m_stack.data(), m_stackLayout[_jump.target]->stackIn); + m_assembly.appendJumpTo(m_blockLabels[_jump.target.value]); + if (!m_blockIsTransformed[_jump.target.value]) + (*this)(_jump.target); +} + +void CodeTransform::operator()(SSACFG::BlockId const&, SSACFG::BasicBlock::FunctionReturn const& _functionReturn) +{ + yulAssert(static_cast(m_stack.size()) == m_assembly.stackHeight()); + yulAssert(m_cfg.function); + // The return label sits physically below the tracked virtual stack. + // Inform the assembler so SWAP can reach it. + m_assembly.setStackHeight(m_assembly.stackHeight() + 1); + // Build target: [rv1, rv2, ..., rvN-1, rv0] (rv0 at top). + // After shuffle: EVM = [label, rv1, ..., rvN-1, rv0]. + // After SWAP(N): EVM = [rv0, rv1, ..., rvN-1, label]. + // After JUMP: EVM = [rv0, rv1, ..., rvN-1]. + // TODO: it would be better to account for function return labels from the entry of the function to the end symbolically + StackData returnTarget; + if (!_functionReturn.returnValues.empty()) + { + returnTarget.reserve(_functionReturn.returnValues.size()); + for (std::size_t i = 1; i < _functionReturn.returnValues.size(); ++i) + returnTarget.push_back(StackSlot::makeValueID(_functionReturn.returnValues[i])); + returnTarget.push_back(StackSlot::makeValueID(_functionReturn.returnValues.front())); + StackShuffler::shuffle(m_stack, returnTarget, {}, returnTarget.size()); + m_assembly.appendInstruction(evmasm::swapInstruction( + static_cast(_functionReturn.returnValues.size()) + )); + } + else + StackShuffler::shuffle(m_stack, returnTarget, {}, 0); + m_assembly.appendJump(0, AbstractAssembly::JumpType::OutOfFunction); +} + +void CodeTransform::operator()(SSACFG::BlockId const&, SSACFG::BasicBlock::Terminated const&) +{ + yulAssert(static_cast(m_stack.size()) == m_assembly.stackHeight()); + // To be sure just emit another INVALID - should be removed by optimizer. + m_assembly.appendInstruction(evmasm::Instruction::INVALID); +} + +void CodeTransform::prepareBlockExitStack(StackData const& _target, PhiInverse const& _phiInverse) +{ + // pull back target to live in current variable space + auto const pulledBackTarget = stackPreImage(_target, _phiInverse); + // shuffle to target + StackShuffler::shuffle(m_stack, pulledBackTarget); + // check that shuffling was successful + assertLayoutCompatibility(m_stack.data(), pulledBackTarget); + // now we can simply set the target to the actual one which will take care of the application of phi functions + m_stackData = _target; +} diff --git a/libyul/backends/evm/ssa/CodeTransform.h b/libyul/backends/evm/ssa/CodeTransform.h new file mode 100644 index 0000000000..03c50cbd7d --- /dev/null +++ b/libyul/backends/evm/ssa/CodeTransform.h @@ -0,0 +1,144 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include +#include + +#include + +#include + +namespace solidity::yul +{ +struct BuiltinContext; +} +namespace solidity::yul::ssa +{ + +struct AssemblyCallbacks +{ + void swap(std::size_t const _depth) + { + assembly->appendInstruction(evmasm::swapInstruction(static_cast(_depth))); + } + + void pop() + { + assembly->appendInstruction(evmasm::Instruction::POP); + } + + void push(StackSlot const& _slot) + { + switch (_slot.kind()) + { + case StackSlot::Kind::ValueID: + { + auto const id = _slot.valueID(); + yulAssert(id.isLiteral(), fmt::format("Tried bringing up v{}", id.value())); + assembly->appendConstant(cfg->literalInfo(id).value); + return; + } + case StackSlot::Kind::Junk: + { + if (assembly->evmVersion().hasPush0()) + assembly->appendConstant(0); + else + assembly->appendInstruction(evmasm::Instruction::CODESIZE); + return; + } + case StackSlot::Kind::FunctionCallReturnLabel: + { + auto const& call = callSites->functionCall(_slot.functionCallReturnLabel()); + assembly->appendLabelReference(returnLabels->at(&call)); + return; + } + case StackSlot::Kind::FunctionReturnLabel: + { + yulAssert(false, "Cannot produce function return label."); + } + } + } + + void dup(size_t const _depth) + { + assembly->appendInstruction(evmasm::dupInstruction(static_cast(_depth))); + } + + SSACFG const* cfg{}; + AbstractAssembly* assembly{}; + CallSites const* callSites{}; + std::map const* returnLabels{}; +}; +static_assert(StackManipulationCallbackConcept); + +class CodeTransform +{ +public: + static void run( + AbstractAssembly& _assembly, + ControlFlowLiveness const& _liveness, + BuiltinContext& _builtinContext + ); + +private: + using FunctionLabels = std::map; + + static FunctionLabels registerFunctionLabels( + AbstractAssembly& _assembly, + ControlFlow const& _controlFlow + ); + + CodeTransform( + AbstractAssembly& _assembly, + BuiltinContext& _builtinContext, + FunctionLabels const& _functionLabels, + CallSites const& _callSites, + SSACFG const& _cfg, + SSACFGStackLayout const& _stackLayout, + Scope::Function const* _function, + ControlFlow::FunctionGraphID _graphID); + + void operator()(SSACFG::BlockId _blockId); + void operator()(SSACFG::Operation const& _operation, StackData const& _operationInputLayout); + void operator()(SSACFG::BlockId const& _currentBlock, SSACFG::BasicBlock::MainExit const& _mainExit); + void operator()(SSACFG::BlockId const& _currentBlock, SSACFG::BasicBlock::ConditionalJump const& _conditionalJump); + void operator()(SSACFG::BlockId const& _currentBlock, SSACFG::BasicBlock::Jump const& _jump); + void operator()(SSACFG::BlockId const& _currentBlock, SSACFG::BasicBlock::FunctionReturn const& _functionReturn); + void operator()(SSACFG::BlockId const& _currentBlock, SSACFG::BasicBlock::Terminated const& _terminated); + + void prepareBlockExitStack(StackData const& _target, PhiInverse const& _phiInverse); + + AbstractAssembly& m_assembly; + BuiltinContext& m_builtinContext; + FunctionLabels const& m_functionLabels; + CallSites const& m_callSites; + SSACFG const& m_cfg; + SSACFGStackLayout const& m_stackLayout; + + std::vector m_blockIsTransformed; + std::vector m_blockLabels; + AssemblyCallbacks m_assemblyCallbacks; + StackData m_stackData; + Stack m_stack; + std::map m_returnLabels; +}; + +} diff --git a/libyul/backends/evm/ssa/StackLayoutGenerator.cpp b/libyul/backends/evm/ssa/StackLayoutGenerator.cpp index 04abd8b644..8e5f9b07a8 100644 --- a/libyul/backends/evm/ssa/StackLayoutGenerator.cpp +++ b/libyul/backends/evm/ssa/StackLayoutGenerator.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include @@ -77,12 +77,12 @@ void declareJunk(StackType& _stack, LivenessAnalysis::LivenessData const& _live) } -SSACFGStackLayout StackLayoutGenerator::generate(LivenessAnalysis const& _liveness, CallSites const& _callSites) +SSACFGStackLayout StackLayoutGenerator::generate(LivenessAnalysis const& _liveness, CallSites const& _callSites, ControlFlow::FunctionGraphID _graphID) { - return StackLayoutGenerator(_liveness, _callSites).m_resultLayout; + return StackLayoutGenerator(_liveness, _callSites, _graphID).m_resultLayout; } -StackLayoutGenerator::StackLayoutGenerator(LivenessAnalysis const& _liveness, CallSites const& _callSites): +StackLayoutGenerator::StackLayoutGenerator(LivenessAnalysis const& _liveness, CallSites const& _callSites, ControlFlow::FunctionGraphID): m_cfg(_liveness.cfg()), m_liveness(_liveness), m_callSites(_callSites), @@ -132,11 +132,10 @@ void StackLayoutGenerator::defineStackIn(SSACFG::BlockId const& _blockId) if (_blockId == m_cfg.entry) { if (m_cfg.function) - blockLayout.stackIn = - m_cfg.arguments | - ranges::views::reverse | - ranges::views::transform([](auto&& _variableAndValueId) -> Slot { return Slot::makeValueID(std::get<1>(_variableAndValueId)); }) | - ranges::to; + { + for (auto const& [_, valueID]: m_cfg.arguments | ranges::views::reverse) + blockLayout.stackIn.push_back(Slot::makeValueID(valueID)); + } m_resultLayout[_blockId] = blockLayout; return; } @@ -163,35 +162,38 @@ void StackLayoutGenerator::defineStackIn(SSACFG::BlockId const& _blockId) { // we have more than one entry and need to unify or at the very least apply phi fct. auto const& liveIn = m_liveness.liveIn(_blockId); - std::vector cumulativeCosts(parentExits.size(), std::numeric_limits::max()); + // Pre-compute each parent's phi-term proposal (declareJunk + handlePhiFunctions) once + std::vector proposals(parentExits.size()); for (std::size_t i = 0; i < parentExits.size(); ++i) { if (!parentExits[i].second) continue; - std::size_t cumulativeCost = 0; - auto referenceStackIn = *parentExits[i].second; + proposals[i] = *parentExits[i].second; { - StackType stack(referenceStackIn, {}); + StackType stack(proposals[i], {}); declareJunk(stack, liveIn); } - handlePhiFunctions(referenceStackIn, PhiInverse(m_cfg, parentExits[i].first, _blockId), liveIn); + handlePhiFunctions(proposals[i], PhiInverse(m_cfg, parentExits[i].first, _blockId), liveIn); + } + std::vector cumulativeCosts(parentExits.size(), std::numeric_limits::max()); + for (std::size_t i = 0; i < parentExits.size(); ++i) + { + if (!parentExits[i].second) + continue; + std::size_t cumulativeCost = 0; for (std::size_t j = 0; j < parentExits.size(); ++j) { - if (j != i) - { - if (parentExits[j].second != nullptr) - { - auto otherStackIn = *parentExits[j].second; - StackType stack(otherStackIn, {}); - StackShuffler::shuffle( - stack, - stackPreImage(referenceStackIn, PhiInverse(m_cfg, parentExits[j].first, _blockId)), - {}, - referenceStackIn.size() - ); - cumulativeCost += stack.callbacks().numOps; - } - } + if (j == i || !parentExits[j].second) + continue; + auto proposalCopy = proposals[j]; + StackType stack(proposalCopy, {}); + StackShuffler::shuffle( + stack, + proposals[i], + {}, + proposals[i].size() + ); + cumulativeCost += stack.callbacks().numOps; } cumulativeCosts[i] = cumulativeCost; } @@ -199,10 +201,7 @@ void StackLayoutGenerator::defineStackIn(SSACFG::BlockId const& _blockId) std::distance(cumulativeCosts.begin(), ranges::min_element(cumulativeCosts)) ); yulAssert(parentExits[argMin].second); - blockLayout.stackIn = *parentExits[argMin].second; - StackType stack(blockLayout.stackIn, {}); - declareJunk(stack, liveIn); - handlePhiFunctions(blockLayout.stackIn, PhiInverse(m_cfg, parentExits[argMin].first, _blockId), liveIn); + blockLayout.stackIn = std::move(proposals[argMin]); } m_resultLayout[_blockId] = blockLayout; } diff --git a/libyul/backends/evm/ssa/StackLayoutGenerator.h b/libyul/backends/evm/ssa/StackLayoutGenerator.h index c9d45c72a2..a1acfa57de 100644 --- a/libyul/backends/evm/ssa/StackLayoutGenerator.h +++ b/libyul/backends/evm/ssa/StackLayoutGenerator.h @@ -32,10 +32,18 @@ class StackLayoutGenerator { public: using Slot = StackSlot; - static SSACFGStackLayout generate(LivenessAnalysis const& _liveness, CallSites const& _callSites); + static SSACFGStackLayout generate( + LivenessAnalysis const& _liveness, + CallSites const& _callSites, + ControlFlow::FunctionGraphID _graphID + ); private: - explicit StackLayoutGenerator(LivenessAnalysis const& _liveness, CallSites const& _callSites); + explicit StackLayoutGenerator( + LivenessAnalysis const& _liveness, + CallSites const& _callSites, + ControlFlow::FunctionGraphID _graphID + ); void defineStackIn(SSACFG::BlockId const& _blockId); void visitBlock(SSACFG::BlockId const& _blockId); diff --git a/test/libyul/ssa/StackLayoutGeneratorTest.cpp b/test/libyul/ssa/StackLayoutGeneratorTest.cpp index a7ddad529c..0066e8fa2b 100644 --- a/test/libyul/ssa/StackLayoutGeneratorTest.cpp +++ b/test/libyul/ssa/StackLayoutGeneratorTest.cpp @@ -162,7 +162,8 @@ frontend::test::TestCase::TestResult StackLayoutGeneratorTest::run(std::ostream& auto const& cfg = *controlFlow->functionGraphs[index]; SSACFGStackLayout const layout = StackLayoutGenerator::generate( LivenessAnalysis(cfg), - gatherCallSites(cfg) + gatherCallSites(cfg), + static_cast(index) ); StackLayoutDotExporter exporter(cfg, index, layout); if (cfg.function) From 0f5f9ed64b1932d70400804d3c82d072248d6f76 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 23 Feb 2026 16:57:50 +0100 Subject: [PATCH 219/243] Add ssa stack target validator --- libyul/backends/evm/ssa/StackUtils.cpp | 31 ++++++++++++++++++++++++++ libyul/backends/evm/ssa/StackUtils.h | 18 +++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/libyul/backends/evm/ssa/StackUtils.cpp b/libyul/backends/evm/ssa/StackUtils.cpp index 3f2d284238..d15b45a161 100644 --- a/libyul/backends/evm/ssa/StackUtils.cpp +++ b/libyul/backends/evm/ssa/StackUtils.cpp @@ -21,9 +21,13 @@ #include #include +#include +#include #include +#include + using namespace solidity::yul::ssa; StackData solidity::yul::ssa::stackPreImage(StackData _stack, PhiInverse const& _phiInverse) @@ -141,3 +145,30 @@ CallSites solidity::yul::ssa::gatherCallSites(SSACFG const& _cfg) } return result; } + +std::string ValidationResult::formatErrors() const +{ + return fmt::format("{}", fmt::join(m_errors, "\n")); +} + +ValidationResult solidity::yul::ssa::checkLayoutCompatibility(StackData const& _current, StackData const& _desired) +{ + ValidationResult result; + if (_current.size() != _desired.size()) + return result.addError(fmt::format( + "size mismatch: {} = len({}) =/= len({}) = {}", + _current.size(), stackToString(_current), stackToString(_desired), _desired.size() + )); + for (auto&& [index, currentSlot, desiredSlot]: ranges::zip_view(ranges::views::iota(0), _current, _desired)) + if (!desiredSlot.isJunk() && currentSlot != desiredSlot) + result.addError(fmt::format( + "stack element mismatch: {} = {}[{}] =/= {}[{}] = {}", + slotToString(currentSlot), + stackToString(_current), + index, + stackToString(_desired), + index, + slotToString(desiredSlot) + )); + return result; +} diff --git a/libyul/backends/evm/ssa/StackUtils.h b/libyul/backends/evm/ssa/StackUtils.h index 6f5e991a09..eaaf8ad1bd 100644 --- a/libyul/backends/evm/ssa/StackUtils.h +++ b/libyul/backends/evm/ssa/StackUtils.h @@ -21,9 +21,23 @@ #include #include +#include +#include + namespace solidity::yul::ssa { +class ValidationResult +{ +public: + bool ok() const { return m_errors.empty(); } + std::string formatErrors() const; + ValidationResult& addError(std::string _msg) { m_errors.push_back(std::move(_msg)); return *this; } + std::vector const& errors() const { return m_errors; } +private: + std::vector m_errors; +}; + struct CountingInstructionsCallbacks { std::size_t numOps = 0; @@ -40,4 +54,8 @@ std::size_t findOptimalTargetSize(StackData const& _stackData, StackData const& CallSites gatherCallSites(SSACFG const& _cfg); +/// Checks that _current and _desired have the same size and that each slot matches, +/// treating junk slots in _desired as wildcards. +ValidationResult checkLayoutCompatibility(StackData const& _current, StackData const& _desired); + } From fc1f03204e3990d5093fe34e15fc3d3fc9b096a7 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 23 Feb 2026 16:58:17 +0100 Subject: [PATCH 220/243] SSA stack shuffler: convenience method when no liveness is required --- libyul/backends/evm/ssa/StackShuffler.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libyul/backends/evm/ssa/StackShuffler.h b/libyul/backends/evm/ssa/StackShuffler.h index 983505bb2c..5dddf5e5a6 100644 --- a/libyul/backends/evm/ssa/StackShuffler.h +++ b/libyul/backends/evm/ssa/StackShuffler.h @@ -169,6 +169,14 @@ class StackShuffler yulAssert(i < maxIterations, fmt::format("Maximum iterations reached on {}", stackToString(_stack.data()))); } + static void shuffle( + Stack& _stack, + StackData const& _target + ) + { + shuffle(_stack, _target, {}, _target.size()); + } + private: /// Make a local step in stack space that should bring us closer to the target. Returns true if more shuffling /// is required, returns false if finished. From 2e557869d282b84a2fab83e3be837b16ac55ab5c Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Tue, 24 Feb 2026 10:43:25 +0100 Subject: [PATCH 221/243] SSA Stack callbacks use StackDepth --- libyul/backends/evm/ssa/CodeTransform.h | 8 ++-- libyul/backends/evm/ssa/Stack.h | 53 +++++++++++++------------ libyul/backends/evm/ssa/StackUtils.h | 4 +- test/libyul/ssa/StackShufflerTest.cpp | 8 ++-- 4 files changed, 38 insertions(+), 35 deletions(-) diff --git a/libyul/backends/evm/ssa/CodeTransform.h b/libyul/backends/evm/ssa/CodeTransform.h index 03c50cbd7d..0764731c93 100644 --- a/libyul/backends/evm/ssa/CodeTransform.h +++ b/libyul/backends/evm/ssa/CodeTransform.h @@ -35,9 +35,9 @@ namespace solidity::yul::ssa struct AssemblyCallbacks { - void swap(std::size_t const _depth) + void swap(StackDepth const _depth) { - assembly->appendInstruction(evmasm::swapInstruction(static_cast(_depth))); + assembly->appendInstruction(evmasm::swapInstruction(static_cast(_depth.value))); } void pop() @@ -77,9 +77,9 @@ struct AssemblyCallbacks } } - void dup(size_t const _depth) + void dup(StackDepth const _depth) { - assembly->appendInstruction(evmasm::dupInstruction(static_cast(_depth))); + assembly->appendInstruction(evmasm::dupInstruction(static_cast(_depth.value))); } SSACFG const* cfg{}; diff --git a/libyul/backends/evm/ssa/Stack.h b/libyul/backends/evm/ssa/Stack.h index 834e21078d..ebeefe2259 100644 --- a/libyul/backends/evm/ssa/Stack.h +++ b/libyul/backends/evm/ssa/Stack.h @@ -132,28 +132,6 @@ using StackData = std::vector; std::string slotToString(StackSlot const& _slot); std::string stackToString(StackData const& _stackData); -template -concept StackManipulationCallbackConcept = requires( - StackManipulationCallback& _callback, - StackSlot _slot, - size_t _depth -) -{ - { _callback.swap(_depth) } -> std::same_as; - { _callback.dup(_depth) } -> std::same_as; - { _callback.push(_slot) } -> std::same_as; - { _callback.pop() } -> std::same_as; -}; - -struct NoOpStackManipulationCallbacks -{ - static void swap(size_t) {} - static void dup(size_t) {} - static void push(StackSlot const&) {} - static void pop() {} -}; -static_assert(StackManipulationCallbackConcept); - /// Array index into stack from the bottom (offset 0 = bottom). /// Natural for array-like access and iteration; used when treating the stack as a data structure. struct StackOffset @@ -179,6 +157,28 @@ struct StackDepth constexpr auto operator<=>(StackDepth const lhs, size_t const rhs) noexcept { return lhs.value <=> rhs; } constexpr auto operator<=>(size_t const lhs, StackDepth const rhs) noexcept { return lhs <=> rhs.value; } +template +concept StackManipulationCallbackConcept = requires( + StackManipulationCallback& _callback, + StackSlot _slot, + StackDepth _depth +) +{ + { _callback.swap(_depth) } -> std::same_as; + { _callback.dup(_depth) } -> std::same_as; + { _callback.push(_slot) } -> std::same_as; + { _callback.pop() } -> std::same_as; +}; + +struct NoOpStackManipulationCallbacks +{ + static void swap(StackDepth) {} + static void dup(StackDepth) {} + static void push(StackSlot const&) {} + static void pop() {} +}; +static_assert(StackManipulationCallbackConcept); + template< StackManipulationCallbackConcept CallbacksType = NoOpStackManipulationCallbacks > @@ -213,7 +213,7 @@ class Stack yulAssert(swapReachable(_offset), "Stack too deep"); std::swap((*m_data)[_offset.value], m_data->back()); if constexpr (!std::is_same_v) - m_callbacks.swap(offsetToDepth(_offset).value); + m_callbacks.swap(offsetToDepth(_offset)); } /// if the stack state needs to be updated without notifying the callback, the template parameter can be set to false @@ -230,6 +230,7 @@ class Stack template void push(Slot const& _slot) { + yulAssert(!_slot.isFunctionReturnLabel(), "Cannot push function return label"); m_data->emplace_back(_slot); if constexpr (callback && !std::is_same_v) m_callbacks.push(_slot); @@ -240,9 +241,11 @@ class Stack { auto const depth = offsetToDepth(_offset); yulAssert(dupReachable(depth), "Stack too deep"); - m_data->push_back((*m_data)[_offset.value]); + auto const slot = (*m_data)[_offset.value]; + yulAssert(!slot.isFunctionReturnLabel(), "Cannot dup function return label"); + m_data->push_back(slot); if constexpr (!std::is_same_v) - m_callbacks.dup(depth.value + 1); + m_callbacks.dup(StackDepth{depth.value + 1}); } bool dupReachable(Offset const& _offset) const noexcept { return dupReachable(offsetToDepth(_offset)); } diff --git a/libyul/backends/evm/ssa/StackUtils.h b/libyul/backends/evm/ssa/StackUtils.h index eaaf8ad1bd..7d30888739 100644 --- a/libyul/backends/evm/ssa/StackUtils.h +++ b/libyul/backends/evm/ssa/StackUtils.h @@ -41,8 +41,8 @@ class ValidationResult struct CountingInstructionsCallbacks { std::size_t numOps = 0; - void swap(std::size_t) { ++numOps; } - void dup(std::size_t) { ++numOps; } + void swap(StackDepth) { ++numOps; } + void dup(StackDepth) { ++numOps; } void push(StackSlot const&) { ++numOps; } void pop() { ++numOps; } }; diff --git a/test/libyul/ssa/StackShufflerTest.cpp b/test/libyul/ssa/StackShufflerTest.cpp index cd24b3b5ec..62e0955bb4 100644 --- a/test/libyul/ssa/StackShufflerTest.cpp +++ b/test/libyul/ssa/StackShufflerTest.cpp @@ -56,15 +56,15 @@ using Slot = StackSlot; using ValueId = SSACFG::ValueId; struct StackManipulationCallbacks { - void swap(size_t _depth) const + void swap(StackDepth _depth) const { if (hook) - (*hook)(fmt::format("SWAP{}", _depth)); + (*hook)(fmt::format("SWAP{}", _depth.value)); } - void dup(size_t const _depth) const + void dup(StackDepth const _depth) const { if (hook) - (*hook)(fmt::format("DUP{}", _depth)); + (*hook)(fmt::format("DUP{}", _depth.value)); } void push(Slot const& _slot) const { From ad675cbf76761df31e35f604dc8f71d69e675d62 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Tue, 3 Mar 2026 17:50:35 +0100 Subject: [PATCH 222/243] Thread SSA CFG Codegen through compiler stack --- libsolidity/interface/CompilerStack.cpp | 9 ++- libsolidity/interface/CompilerStack.h | 5 ++ libyul/YulStack.cpp | 16 ++--- libyul/YulStack.h | 10 +-- libyul/backends/evm/EVMObjectCompiler.cpp | 79 +++++++++++++++-------- libyul/backends/evm/EVMObjectCompiler.h | 5 +- 6 files changed, 81 insertions(+), 43 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 3583ae5356..7d25fcf0d3 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -235,6 +235,12 @@ void CompilerStack::setExperimental(bool _experimental) m_experimental = _experimental; } +void CompilerStack::setViaSSACFG(bool _viaSSACFG) +{ + solAssert(m_stackState < CompilationSuccessful, "Must set SSA CFG codegen before compilation."); + m_viaSSACFG = _viaSSACFG; +} + void CompilerStack::setEVMVersion(langutil::EVMVersion _version) { solAssert(m_stackState < ParsedAndImported, "Must set EVM version before parsing."); @@ -325,6 +331,7 @@ void CompilerStack::reset(bool _keepSettings) m_importRemapper.clear(); m_libraries.clear(); m_viaIR = false; + m_viaSSACFG = false; m_evmVersion = langutil::EVMVersion(); m_eofVersion.reset(); m_modelCheckerSettings = ModelCheckerSettings{}; @@ -1672,7 +1679,7 @@ void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract) std::string deployedName = IRNames::deployedObject(_contract); solAssert(!deployedName.empty(), ""); - tie(compiledContract.evmAssembly, compiledContract.evmRuntimeAssembly) = stack.assembleEVMWithDeployed(deployedName); + tie(compiledContract.evmAssembly, compiledContract.evmRuntimeAssembly) = stack.assembleEVMWithDeployed(deployedName, m_viaSSACFG); if (stack.hasErrors()) { diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index eb765d3c66..41f61ae4a2 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -223,6 +223,10 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac /// Must be set before parsing. void setViaIR(bool _viaIR); + /// Sets the pipeline to use the SSA CFG code generator instead of OptimizedEVMCodeTransform. + /// Must be set before compilation. + void setViaSSACFG(bool _viaSSACFG); + /// Sets the experimental toggle to allow usage of experimental features. void setExperimental(bool _experimental); @@ -610,6 +614,7 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac RevertStrings m_revertStrings = RevertStrings::Default; State m_stopAfter = State::CompilationSuccessful; bool m_viaIR = false; + bool m_viaSSACFG = false; bool m_experimental = false; langutil::EVMVersion m_evmVersion; std::optional m_eofVersion; diff --git a/libyul/YulStack.cpp b/libyul/YulStack.cpp index ae32c0f25b..496f249a34 100644 --- a/libyul/YulStack.cpp +++ b/libyul/YulStack.cpp @@ -192,9 +192,9 @@ bool YulStack::analyzeParsed(Object& _object) return success; } -void YulStack::compileEVM(AbstractAssembly& _assembly, bool _optimize) const +void YulStack::compileEVM(AbstractAssembly& _assembly, bool _optimize, bool _viaSSACFG) const { - EVMObjectCompiler::compile(*m_parserResult, _assembly, _optimize); + EVMObjectCompiler::compile(*m_parserResult, _assembly, _optimize, _viaSSACFG); } void YulStack::reparse() @@ -232,7 +232,7 @@ void YulStack::reparse() // locations and fewer warnings. } -MachineAssemblyObject YulStack::assemble(Machine _machine) +MachineAssemblyObject YulStack::assemble(Machine _machine, bool _viaSSACFG) { yulAssert(m_stackState >= AnalysisSuccessful); yulAssert(m_parserResult, ""); @@ -242,17 +242,17 @@ MachineAssemblyObject YulStack::assemble(Machine _machine) switch (_machine) { case Machine::EVM: - return assembleWithDeployed().first; + return assembleWithDeployed({}, _viaSSACFG).first; } unreachable(); } std::pair -YulStack::assembleWithDeployed(std::optional _deployName) +YulStack::assembleWithDeployed(std::optional _deployName, bool _viaSSACFG) { yulAssert(m_charStream); - auto [creationAssembly, deployedAssembly] = assembleEVMWithDeployed(_deployName); + auto [creationAssembly, deployedAssembly] = assembleEVMWithDeployed(_deployName, _viaSSACFG); if (!creationAssembly) { yulAssert(!deployedAssembly); @@ -307,7 +307,7 @@ YulStack::assembleWithDeployed(std::optional _deployName) } std::pair, std::shared_ptr> -YulStack::assembleEVMWithDeployed(std::optional _deployName) +YulStack::assembleEVMWithDeployed(std::optional _deployName, bool _viaSSACFG) { yulAssert(m_stackState >= AnalysisSuccessful); yulAssert(m_parserResult, ""); @@ -326,7 +326,7 @@ YulStack::assembleEVMWithDeployed(std::optional _deployName) ); try { - compileEVM(adapter, optimize); + compileEVM(adapter, optimize, _viaSSACFG); assembly.optimise(evmasm::Assembly::OptimiserSettings::translateSettings(m_optimiserSettings)); diff --git a/libyul/YulStack.h b/libyul/YulStack.h index e9d75b6ae9..694c14e36a 100644 --- a/libyul/YulStack.h +++ b/libyul/YulStack.h @@ -118,7 +118,7 @@ class YulStack: public langutil::CharStreamProvider void optimize(); /// Run the assembly step (should only be called after parseAndAnalyze). - MachineAssemblyObject assemble(Machine _machine); + MachineAssemblyObject assemble(Machine _machine, bool _viaSSACFG = false); /// Run the assembly step (should only be called after parseAndAnalyze). /// In addition to the value returned by @a assemble, returns @@ -126,7 +126,8 @@ class YulStack: public langutil::CharStreamProvider /// Only available for EVM. std::pair assembleWithDeployed( - std::optional _deployName = {} + std::optional _deployName = {}, + bool _viaSSACFG = false ); /// Run the assembly step (should only be called after parseAndAnalyze). @@ -134,7 +135,8 @@ class YulStack: public langutil::CharStreamProvider /// Only available for EVM. std::pair, std::shared_ptr> assembleEVMWithDeployed( - std::optional _deployName = {} + std::optional _deployName = {}, + bool _viaSSACFG = false ); /// @returns the errors generated during parsing, analysis (and potentially assembly). @@ -161,7 +163,7 @@ class YulStack: public langutil::CharStreamProvider bool analyzeParsed(); bool analyzeParsed(yul::Object& _object); - void compileEVM(yul::AbstractAssembly& _assembly, bool _optimize) const; + void compileEVM(yul::AbstractAssembly& _assembly, bool _optimize, bool _viaSSACFG) const; /// Prints the Yul object stored internally and parses it again. /// This ensures that the debug info in the AST matches the source that printing would produce diff --git a/libyul/backends/evm/EVMObjectCompiler.cpp b/libyul/backends/evm/EVMObjectCompiler.cpp index 830c32752d..b476f2e096 100644 --- a/libyul/backends/evm/EVMObjectCompiler.cpp +++ b/libyul/backends/evm/EVMObjectCompiler.cpp @@ -21,6 +21,10 @@ #include +#include +#include +#include + #include #include #include @@ -37,14 +41,15 @@ using namespace solidity::yul; void EVMObjectCompiler::compile( Object const& _object, AbstractAssembly& _assembly, - bool _optimize + bool _optimize, + bool _viaSSACFG ) { EVMObjectCompiler compiler(_assembly); - compiler.run(_object, _optimize); + compiler.run(_object, _optimize, _viaSSACFG); } -void EVMObjectCompiler::run(Object const& _object, bool _optimize) +void EVMObjectCompiler::run(Object const& _object, bool _optimize, bool _viaSSACFG) { yulAssert(_object.dialect()); auto const* evmDialect = dynamic_cast(_object.dialect()); @@ -61,7 +66,7 @@ void EVMObjectCompiler::run(Object const& _object, bool _optimize) auto subAssemblyAndID = m_assembly.createSubAssembly(isCreation, subObject->name); context.subIDs[subObject->name] = subAssemblyAndID.second; subObject->subId = subAssemblyAndID.second; - compile(*subObject, *subAssemblyAndID.first, _optimize); + compile(*subObject, *subAssemblyAndID.first, _optimize, _viaSSACFG); } else { @@ -82,33 +87,51 @@ void EVMObjectCompiler::run(Object const& _object, bool _optimize) } if (_optimize && evmDialect->evmVersion().canOverchargeGasForCall()) { - auto stackErrors = OptimizedEVMCodeTransform::run( - m_assembly, - *_object.analysisInfo, - _object.code()->root(), - *evmDialect, - context, - OptimizedEVMCodeTransform::UseNamedLabels::ForFirstFunctionOfEachName - ); - if (!stackErrors.empty()) + if (_viaSSACFG) { - yulAssert(evmDialect->providesObjectAccess()); - auto const memoryGuardHandle = evmDialect->findBuiltin("memoryguard"); - yulAssert(memoryGuardHandle, "Compiling with object access, memoryguard should be available as builtin."); - std::vector memoryGuardCalls = findFunctionCalls( + std::unique_ptr controlFlow = ssa::SSACFGBuilder::build( + *_object.analysisInfo, + *_object.dialect(), _object.code()->root(), - *memoryGuardHandle + false ); - auto stackError = stackErrors.front(); - std::string msg = stackError.comment() ? *stackError.comment() : ""; - if (memoryGuardCalls.empty()) - msg += "\nNo memoryguard was present. " - "Consider using memory-safe assembly only and annotating it via " - "'assembly (\"memory-safe\") { ... }'."; - else - msg += "\nmemoryguard was present."; - stackError << util::errinfo_comment(msg); - BOOST_THROW_EXCEPTION(stackError); + ssa::ControlFlowLiveness const liveness(*controlFlow); + ssa::CodeTransform::run( + m_assembly, + liveness, + context + ); + } + else + { + auto stackErrors = OptimizedEVMCodeTransform::run( + m_assembly, + *_object.analysisInfo, + _object.code()->root(), + *evmDialect, + context, + OptimizedEVMCodeTransform::UseNamedLabels::ForFirstFunctionOfEachName + ); + if (!stackErrors.empty()) + { + yulAssert(evmDialect->providesObjectAccess()); + auto const memoryGuardHandle = evmDialect->findBuiltin("memoryguard"); + yulAssert(memoryGuardHandle, "Compiling with object access, memoryguard should be available as builtin."); + std::vector memoryGuardCalls = findFunctionCalls( + _object.code()->root(), + *memoryGuardHandle + ); + auto stackError = stackErrors.front(); + std::string msg = stackError.comment() ? *stackError.comment() : ""; + if (memoryGuardCalls.empty()) + msg += "\nNo memoryguard was present. " + "Consider using memory-safe assembly only and annotating it via " + "'assembly (\"memory-safe\") { ... }'."; + else + msg += "\nmemoryguard was present."; + stackError << util::errinfo_comment(msg); + BOOST_THROW_EXCEPTION(stackError); + } } } else diff --git a/libyul/backends/evm/EVMObjectCompiler.h b/libyul/backends/evm/EVMObjectCompiler.h index cca346ae78..d7f983a318 100644 --- a/libyul/backends/evm/EVMObjectCompiler.h +++ b/libyul/backends/evm/EVMObjectCompiler.h @@ -36,12 +36,13 @@ class EVMObjectCompiler static void compile( Object const& _object, AbstractAssembly& _assembly, - bool _optimize + bool _optimize, + bool _viaSSACFG = false ); private: EVMObjectCompiler(AbstractAssembly& _assembly): m_assembly(_assembly) {} - void run(Object const& _object, bool _optimize); + void run(Object const& _object, bool _optimize, bool _viaSSACFG); AbstractAssembly& m_assembly; }; From 4687eeced72f44c4d2ead29a0f22471abe861fe9 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Tue, 3 Mar 2026 17:51:04 +0100 Subject: [PATCH 223/243] Add compileViaSSAYul option to semantic test suite --- test/libsolidity/SemanticTest.cpp | 31 ++++++++++++++----- test/libsolidity/SemanticTest.h | 8 +++-- .../SolidityExecutionFramework.cpp | 1 + test/libsolidity/SolidityExecutionFramework.h | 1 + test/libsolidity/util/TestFileParser.cpp | 4 +-- 5 files changed, 34 insertions(+), 11 deletions(-) diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index 296467ae20..d9d44e6289 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -112,6 +112,7 @@ SemanticTest::SemanticTest( m_revertStrings = revertStrings.value(); m_allowNonExistingFunctions = m_reader.boolSetting("allowNonExistingFunctions", false); + m_testCaseWantsSSACFGRun = m_reader.boolSetting("compileViaSSACFG", false); m_compiler.setExperimental(m_reader.boolSetting("experimental", false)); parseExpectations(m_reader.stream()); @@ -333,6 +334,14 @@ TestCase::TestResult SemanticTest::run(std::ostream& _stream, std::string const& result = tryRunTestWithYulOptimizer(_stream, _linePrefix, _formatted); } + if (m_testCaseWantsSSACFGRun && m_testCaseWantsYulRun && result == TestResult::Success) + { + if (solidity::test::CommonOptions::get().optimize) + result = runTest(_stream, _linePrefix, _formatted, true /* _isYulRun */, true /* _isSSACFGRun */); + else + result = tryRunTestWithYulOptimizer(_stream, _linePrefix, _formatted, true /* _isSSACFGRun */); + } + if (result != TestResult::Success) solidity::test::CommonOptions::get().printSelectedOptions( _stream, @@ -347,19 +356,24 @@ TestCase::TestResult SemanticTest::runTest( std::ostream& _stream, std::string const& _linePrefix, bool _formatted, - bool _isYulRun + bool _isYulRun, + bool _isSSACFGRun ) { bool success = true; m_gasCostFailure = false; + m_isSSACFGRun = _isSSACFGRun; selectVM(evmc_capabilities::EVMC_CAPABILITY_EVM1); reset(); m_compileViaYul = _isYulRun; + m_compileViaSSACFG = _isSSACFGRun; - if (_isYulRun) + if (_isSSACFGRun) + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via SSA Yul: " << std::endl; + else if (_isYulRun) AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul: " << std::endl; for (TestFunctionCall& test: m_tests) @@ -525,7 +539,8 @@ TestCase::TestResult SemanticTest::runTest( TestCase::TestResult SemanticTest::tryRunTestWithYulOptimizer( std::ostream& _stream, std::string const& _linePrefix, - bool _formatted + bool _formatted, + bool _isSSACFGRun ) { TestResult result{}; @@ -542,7 +557,7 @@ TestCase::TestResult SemanticTest::tryRunTestWithYulOptimizer( try { - result = runTest(_stream, _linePrefix, _formatted, true /* _isYulRun */); + result = runTest(_stream, _linePrefix, _formatted, true /* _isYulRun */, _isSSACFGRun); } catch (yul::StackTooDeepError const&) { @@ -571,9 +586,10 @@ TestCase::TestResult SemanticTest::tryRunTestWithYulOptimizer( bool SemanticTest::checkGasCostExpectation(TestFunctionCall& io_test, bool _compileViaYul) const { - std::string setting = - (_compileViaYul ? "ir"s : "legacy"s) + - (m_optimiserSettings == OptimiserSettings::full() ? "Optimized" : ""); + std::string setting = m_isSSACFGRun + ? (m_optimiserSettings == OptimiserSettings::full() ? "ssaCFGOptimized"s : "ssaCFG"s) + : (_compileViaYul ? "ir"s : "legacy"s) + + (m_optimiserSettings == OptimiserSettings::full() ? "Optimized" : ""); soltestAssert( io_test.call().expectations.gasUsedExcludingCode.count(setting) == @@ -584,6 +600,7 @@ bool SemanticTest::checkGasCostExpectation(TestFunctionCall& io_test, bool _comp m_gasUsed < m_enforceGasCostMinValue || // gas used less than threshold for enforcing feature m_gasUsed >= InitialGas || // test has used up all available gas (test will fail anyway) setting == "ir" || + setting == "ssaCFG" || io_test.call().kind == FunctionCall::Kind::Builtin; // isoltest builtin e.g. `smokeTest` or `storageEmpty` bool gasValueMissing = !io_test.call().expectations.gasUsedExcludingCode.contains(setting); if (!m_enforceGasCost || (gasValueMissing && uninteresting)) diff --git a/test/libsolidity/SemanticTest.h b/test/libsolidity/SemanticTest.h index efc2b59030..2421fdf29a 100644 --- a/test/libsolidity/SemanticTest.h +++ b/test/libsolidity/SemanticTest.h @@ -96,12 +96,14 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict std::ostream& _stream, std::string const& _linePrefix, bool _formatted, - bool _isYulRun + bool _isYulRun, + bool _isSSACFGRun = false ); TestResult tryRunTestWithYulOptimizer( std::ostream& _stream, std::string const& _linePrefix, - bool _formatted + bool _formatted, + bool _isSSACFGRun = false ); bool checkGasCostExpectation(TestFunctionCall& io_test, bool _compileViaYul) const; std::map makeBuiltins(); @@ -119,6 +121,8 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict std::vector const m_sideEffectHooks; bool m_testCaseWantsYulRun = true; bool m_testCaseWantsLegacyRun = true; + bool m_testCaseWantsSSACFGRun = false; + bool m_isSSACFGRun = false; bool m_runWithABIEncoderV1Only = false; bool m_allowNonExistingFunctions = false; bool m_gasCostFailure = false; diff --git a/test/libsolidity/SolidityExecutionFramework.cpp b/test/libsolidity/SolidityExecutionFramework.cpp index 57d4d9d9b7..fc9087e926 100644 --- a/test/libsolidity/SolidityExecutionFramework.cpp +++ b/test/libsolidity/SolidityExecutionFramework.cpp @@ -61,6 +61,7 @@ bytes SolidityExecutionFramework::multiSourceCompileContract( m_compiler.setEOFVersion(m_eofVersion); m_compiler.setOptimiserSettings(m_optimiserSettings); m_compiler.setViaIR(m_compileViaYul); + m_compiler.setViaSSACFG(m_compileViaSSACFG); m_compiler.setRevertStringBehaviour(m_revertStrings); if (!m_appendCBORMetadata) { m_compiler.setMetadataFormat(CompilerStack::MetadataFormat::NoMetadata); diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index 4caa4aa1e1..306ac80ade 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -84,6 +84,7 @@ class SolidityExecutionFramework: public solidity::test::ExecutionFramework std::optional m_eofVersion; CompilerStack m_compiler; bool m_compileViaYul = false; + bool m_compileViaSSACFG = false; bool m_showMetadata = false; bool m_appendCBORMetadata = true; CompilerStack::MetadataHash m_metadataHash = CompilerStack::MetadataHash::IPFS; diff --git a/test/libsolidity/util/TestFileParser.cpp b/test/libsolidity/util/TestFileParser.cpp index 5a32a1591a..247bd4d544 100644 --- a/test/libsolidity/util/TestFileParser.cpp +++ b/test/libsolidity/util/TestFileParser.cpp @@ -84,9 +84,9 @@ std::vector TestFileParser::parseFunctio BOOST_THROW_EXCEPTION(TestParserError("Expected function call before gas usage filter.")); std::string runType = m_scanner.currentLiteral(); - if (std::set{"ir", "irOptimized", "legacy", "legacyOptimized"}.count(runType) == 0) + if (std::set{"ir", "irOptimized", "legacy", "legacyOptimized", "ssaCFG", "ssaCFGOptimized"}.count(runType) == 0) BOOST_THROW_EXCEPTION(TestParserError( - "Expected \"ir\", \"irOptimized\", \"legacy\", or \"legacyOptimized\"." + "Expected \"ir\", \"irOptimized\", \"legacy\", \"legacyOptimized\", \"ssaCFG\", or \"ssaCFGOptimized\"." )); m_scanner.scanNextToken(); From 99a57b2dc6b78b70d26ab3575756b6946e6e3ddd Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Tue, 3 Mar 2026 17:51:22 +0100 Subject: [PATCH 224/243] Enable compileViaSSAYul for deposit and snark contract --- .../semanticTests/externalContracts/deposit_contract.sol | 8 ++++++++ .../libsolidity/semanticTests/externalContracts/snark.sol | 3 +++ 2 files changed, 11 insertions(+) diff --git a/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol b/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol index c53e49f1f0..b6b5a09243 100644 --- a/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol +++ b/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol @@ -174,6 +174,8 @@ contract DepositContract is IDepositContract, ERC165 { ret[7] = bytesValue[0]; } } +// ==== +// compileViaSSACFG: true // ---- // constructor() // gas irOptimized: 809570 @@ -182,6 +184,8 @@ contract DepositContract is IDepositContract, ERC165 { // gas legacy code: 1438800 // gas legacyOptimized: 848699 // gas legacyOptimized code: 878200 +// gas ssaCFGOptimized: 810764 +// gas ssaCFGOptimized code: 582000 // supportsInterface(bytes4): 0x0 -> 0 // supportsInterface(bytes4): 0xffffffff00000000000000000000000000000000000000000000000000000000 -> false # defined to be false by ERC-165 # // supportsInterface(bytes4): 0x01ffc9a700000000000000000000000000000000000000000000000000000000 -> true # ERC-165 id # @@ -190,12 +194,14 @@ contract DepositContract is IDepositContract, ERC165 { // gas irOptimized: 109178 // gas legacy: 142741 // gas legacyOptimized: 117558 +// gas ssaCFGOptimized: 110211 // get_deposit_count() -> 0x20, 8, 0 # TODO: check balance and logs after each deposit # // deposit(bytes,bytes,bytes,bytes32), 32 ether: 0 -> FAILURE # Empty input # // get_deposit_root() -> 0xd70a234731285c6804c2a4f56711ddb8c82c99740f207854891028af34e27e5e // gas irOptimized: 109178 // gas legacy: 142741 // gas legacyOptimized: 117558 +// gas ssaCFGOptimized: 110211 // get_deposit_count() -> 0x20, 8, 0 // deposit(bytes,bytes,bytes,bytes32), 1 ether: 0x80, 0xe0, 0x120, 0xaa4a8d0b7d9077248630f1a4701ae9764e42271d7f22b7838778411857fd349e, 0x30, 0x933ad9491b62059dd065b560d256d8957a8c402cc6e8d8ee7290ae11e8f73292, 0x67a8811c397529dac52ae1342ba58c9500000000000000000000000000000000, 0x20, 0x00f50428677c60f997aadeab24aabf7fceaef491c96a52b463ae91f95611cf71, 0x60, 0xa29d01cc8c6296a8150e515b5995390ef841dc18948aa3e79be6d7c1851b4cbb, 0x5d6ff49fa70b9c782399506a22a85193151b9b691245cebafd2063012443c132, 0x4b6c36debaedefb7b2d71b0503ffdc00150aaffd42e63358238ec888901738b8 -> # txhash: 0x7085c586686d666e8bb6e9477a0f0b09565b2060a11f1c4209d3a52295033832 # // ~ emit DepositEvent(bytes,bytes,bytes,bytes,bytes): 0xa0, 0x0100, 0x0140, 0x0180, 0x0200, 0x30, 0x933ad9491b62059dd065b560d256d8957a8c402cc6e8d8ee7290ae11e8f73292, 0x67a8811c397529dac52ae1342ba58c9500000000000000000000000000000000, 0x20, 0xf50428677c60f997aadeab24aabf7fceaef491c96a52b463ae91f95611cf71, 0x08, 0xca9a3b00000000000000000000000000000000000000000000000000000000, 0x60, 0xa29d01cc8c6296a8150e515b5995390ef841dc18948aa3e79be6d7c1851b4cbb, 0x5d6ff49fa70b9c782399506a22a85193151b9b691245cebafd2063012443c132, 0x4b6c36debaedefb7b2d71b0503ffdc00150aaffd42e63358238ec888901738b8, 0x08, 0x00 @@ -203,6 +209,7 @@ contract DepositContract is IDepositContract, ERC165 { // gas irOptimized: 109174 // gas legacy: 142750 // gas legacyOptimized: 117570 +// gas ssaCFGOptimized: 110201 // get_deposit_count() -> 0x20, 8, 0x0100000000000000000000000000000000000000000000000000000000000000 // deposit(bytes,bytes,bytes,bytes32), 32 ether: 0x80, 0xe0, 0x120, 0xdbd986dc85ceb382708cf90a3500f500f0a393c5ece76963ac3ed72eccd2c301, 0x30, 0xb2ce0f79f90e7b3a113ca5783c65756f96c4b4673c2b5c1eb4efc22280259441, 0x06d601211e8866dc5b50dc48a244dd7c00000000000000000000000000000000, 0x20, 0x00344b6c73f71b11c56aba0d01b7d8ad83559f209d0a4101a515f6ad54c89771, 0x60, 0x945caaf82d18e78c033927d51f452ebcd76524497b91d7a11219cb3db6a1d369, 0x7595fc095ce489e46b2ef129591f2f6d079be4faaf345a02c5eb133c072e7c56, 0x0c6c3617eee66b4b878165c502357d49485326bc6b31bc96873f308c8f19c09d -> # txhash: 0x404d8e109822ce448e68f45216c12cb051b784d068fbe98317ab8e50c58304ac # // ~ emit DepositEvent(bytes,bytes,bytes,bytes,bytes): 0xa0, 0x0100, 0x0140, 0x0180, 0x0200, 0x30, 0xb2ce0f79f90e7b3a113ca5783c65756f96c4b4673c2b5c1eb4efc22280259441, 0x06d601211e8866dc5b50dc48a244dd7c00000000000000000000000000000000, 0x20, 0x344b6c73f71b11c56aba0d01b7d8ad83559f209d0a4101a515f6ad54c89771, 0x08, 0x40597307000000000000000000000000000000000000000000000000000000, 0x60, 0x945caaf82d18e78c033927d51f452ebcd76524497b91d7a11219cb3db6a1d369, 0x7595fc095ce489e46b2ef129591f2f6d079be4faaf345a02c5eb133c072e7c56, 0x0c6c3617eee66b4b878165c502357d49485326bc6b31bc96873f308c8f19c09d, 0x08, 0x0100000000000000000000000000000000000000000000000000000000000000 @@ -210,4 +217,5 @@ contract DepositContract is IDepositContract, ERC165 { // gas irOptimized: 109174 // gas legacy: 142750 // gas legacyOptimized: 117570 +// gas ssaCFGOptimized: 110201 // get_deposit_count() -> 0x20, 8, 0x0200000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/externalContracts/snark.sol b/test/libsolidity/semanticTests/externalContracts/snark.sol index c073993e1d..4d303926bd 100644 --- a/test/libsolidity/semanticTests/externalContracts/snark.sol +++ b/test/libsolidity/semanticTests/externalContracts/snark.sol @@ -287,6 +287,7 @@ contract Test { // // ==== // EVMVersion: >=constantinople +// compileViaSSACFG: true // ---- // library: Pairing // f() -> true @@ -295,8 +296,10 @@ contract Test { // gas irOptimized: 275229 // gas legacy: 293579 // gas legacyOptimized: 276313 +// gas ssaCFGOptimized: 275439 // verifyTx() -> true // ~ emit Verified(string): 0x20, 0x16, "Successfully verified." // gas irOptimized: 818076 // gas legacy: 904397 // gas legacyOptimized: 816770 +// gas ssaCFGOptimized: 819542 From 08051cf43243e3a3d17435c4209ba232a90baf4a Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Tue, 3 Mar 2026 18:22:52 +0100 Subject: [PATCH 225/243] Add ssa yul ir parsing capabilities to gas_diff_stats.py --- scripts/gas_diff_stats.py | 34 ++++++++++++++++------------- test/scripts/test_gas_diff_stats.py | 14 +++++++++--- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/scripts/gas_diff_stats.py b/scripts/gas_diff_stats.py index ecb4cc912a..cb90e7b38b 100755 --- a/scripts/gas_diff_stats.py +++ b/scripts/gas_diff_stats.py @@ -25,6 +25,7 @@ class Kind(Enum): IrOptimized = 2 Legacy = 3 LegacyOptimized = 4 + SsaCFGOptimized = 5 class Diff(Enum): Minus = 1 @@ -43,6 +44,7 @@ class Diff(Enum): gas_ir_optimized = string("gas irOptimized").result(Kind.IrOptimized) gas_legacy_optimized = string("gas legacyOptimized").result(Kind.LegacyOptimized) gas_legacy = string("gas legacy").result(Kind.Legacy) +gas_ssa_cfg_optimized = string("gas ssaCFGOptimized").result(Kind.SsaCFGOptimized) code_suffix = string("code") def number() -> int: @@ -61,7 +63,7 @@ def diff_string() -> (Kind, Diff, int): diff_kind = yield minus | plus yield comment yield space - codegen_kind = yield gas_ir_optimized ^ gas_ir ^ gas_legacy_optimized ^ gas_legacy + codegen_kind = yield gas_ssa_cfg_optimized ^ gas_ir_optimized ^ gas_ir ^ gas_legacy_optimized ^ gas_legacy yield optional(space) yield optional(code_suffix) yield colon @@ -69,11 +71,11 @@ def diff_string() -> (Kind, Diff, int): val = yield number() return (diff_kind, codegen_kind, val) -def collect_statistics(lines) -> (int, int, int, int, int, int): +def collect_statistics(lines) -> (int, int, int, int, int, int, int, int): """Returns - (old_ir_optimized, old_legacy_optimized, old_legacy, new_ir_optimized, - new_legacy_optimized, new_legacy) + (old_ir_optimized, old_legacy_optimized, old_legacy, old_ssa_cfg_optimized, + new_ir_optimized, new_legacy_optimized, new_legacy, new_ssa_cfg_optimized) All the values in the same file (in the diff) are summed up. @@ -88,7 +90,7 @@ def collect_statistics(lines) -> (int, int, int, int, int, int): if (parsed := diff_string.parse(line)) is not None ] diff_kinds = [Diff.Minus, Diff.Plus] - codegen_kinds = [Kind.IrOptimized, Kind.LegacyOptimized, Kind.Legacy] + codegen_kinds = [Kind.IrOptimized, Kind.LegacyOptimized, Kind.Legacy, Kind.SsaCFGOptimized] return tuple( sum( val @@ -141,23 +143,25 @@ def stat(old, new): parsed = parse_git_diff(fname) if parsed is None: continue - assert len(parsed) == 6 - ir_optimized = stat(parsed[0], parsed[3]) - legacy_optimized = stat(parsed[1], parsed[4]) - legacy = stat(parsed[2], parsed[5]) + assert len(parsed) == 8 + ir_optimized = stat(parsed[0], parsed[4]) + legacy_optimized = stat(parsed[1], parsed[5]) + legacy = stat(parsed[2], parsed[6]) + ssa_cfg_optimized = stat(parsed[3], parsed[7]) fname = f"`{fname.split('/', 3)[-1]}`" average = (( - percent_or_zero(parsed[0], parsed[3]) + - percent_or_zero(parsed[1], parsed[4]) + - percent_or_zero(parsed[2], parsed[5]) - ) / 3) - table += [[average, fname, ir_optimized, legacy_optimized, legacy]] + percent_or_zero(parsed[0], parsed[4]) + + percent_or_zero(parsed[1], parsed[5]) + + percent_or_zero(parsed[2], parsed[6]) + + percent_or_zero(parsed[3], parsed[7]) + ) / 4) + table += [[average, fname, ir_optimized, legacy_optimized, legacy, ssa_cfg_optimized]] sorted_table = [row[1:] for row in sorted(table, reverse=True)] if table: print("
Click for a table of gas differences\n") - table_header = ["File name", "IR optimized", "Legacy optimized", "Legacy"] + table_header = ["File name", "IR optimized", "Legacy optimized", "Legacy", "SSA CFG optimized"] print(tabulate(sorted_table, headers=table_header, tablefmt="github")) print("
") else: diff --git a/test/scripts/test_gas_diff_stats.py b/test/scripts/test_gas_diff_stats.py index e67bd0be89..4fe1576bff 100644 --- a/test/scripts/test_gas_diff_stats.py +++ b/test/scripts/test_gas_diff_stats.py @@ -11,13 +11,13 @@ class TestGasDiffStats(unittest.TestCase): def test_collect_statistics_should_fail_on_empty_diff(self): with self.assertRaises(RuntimeError): - self.assertEqual(collect_statistics(""), (0, 0, 0, 0, 0, 0)) + self.assertEqual(collect_statistics(""), (0, 0, 0, 0, 0, 0, 0, 0)) def test_collect_statistics_should_accept_whitespace_only_diff(self): # TODO: Should it really work this way? # If we're rejecting empty diff, not sure why whitespace is accepted. - self.assertEqual(collect_statistics("\n"), (0, 0, 0, 0, 0, 0)) - self.assertEqual(collect_statistics("\n \n\t\n\n"), (0, 0, 0, 0, 0, 0)) + self.assertEqual(collect_statistics("\n"), (0, 0, 0, 0, 0, 0, 0, 0)) + self.assertEqual(collect_statistics("\n \n\t\n\n"), (0, 0, 0, 0, 0, 0, 0, 0)) def test_collect_statistics_should_report_sum_of_gas_costs(self): diff_output = dedent(""" @@ -48,9 +48,11 @@ def test_collect_statistics_should_report_sum_of_gas_costs(self): 98438588 + 98438589, # -irOptimized 98438580 + 98438580, # -legacyOptimized 102095 + 98438774 + 98438774, # -legacy + 0, # -ssaCFGOptimized 25388 + 25389, # +irOptimized 25380 + 25380, # +legacyOptimized 76495 + 98413174 + 98413174, # +legacy + 0, # +ssaCFGOptimized )) def test_collect_statistics_should_ignore_ir_costs(self): @@ -65,9 +67,11 @@ def test_collect_statistics_should_ignore_ir_costs(self): 0, # -irOptimized 0, # -legacyOptimized 1, # -legacy + 0, # -ssaCFGOptimized 0, # +irOptimized 0, # +legacyOptimized 3, # +legacy + 0, # +ssaCFGOptimized )) def test_collect_statistics_should_ignore_unchanged_costs(self): @@ -81,9 +85,11 @@ def test_collect_statistics_should_ignore_unchanged_costs(self): 0, # -irOptimized 0, # -legacyOptimized 1, # -legacy + 0, # -ssaCFGOptimized 0, # +irOptimized 0, # +legacyOptimized 3, # +legacy + 0, # +ssaCFGOptimized )) def test_collect_statistics_should_include_code_deposit_in_total_cost(self): @@ -103,7 +109,9 @@ def test_collect_statistics_should_include_code_deposit_in_total_cost(self): 1, # -irOptimized 300, # -legacyOptimized 900000020, # -legacy + 0, # -ssaCFGOptimized 54000, # +irOptimized 87000000, # +legacyOptimized 600000, # +legacy + 0, # +ssaCFGOptimized )) From 436ccd5a09c6829d54f382378a8b72d781e942a6 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Thu, 5 Mar 2026 16:09:05 +0100 Subject: [PATCH 226/243] Make SSA CFG Codegen an experimental feature --- libsolidity/interface/CompilerStack.cpp | 3 +++ .../semanticTests/externalContracts/deposit_contract.sol | 1 + test/libsolidity/semanticTests/externalContracts/snark.sol | 1 + 3 files changed, 5 insertions(+) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 7d25fcf0d3..a55b907199 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -780,6 +780,9 @@ CompilerStack::PipelineConfig CompilerStack::requestedPipelineConfig(ContractDef bool CompilerStack::compile(State _stopAfter) { m_stopAfter = _stopAfter; + + solAssert(!m_viaSSACFG || m_experimental, "SSA CFG code generation is an experimental feature. It requires experimental mode to be enabled."); + if (m_stackState < AnalysisSuccessful) if (!parseAndAnalyze(_stopAfter)) return false; diff --git a/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol b/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol index b6b5a09243..82d307cd5e 100644 --- a/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol +++ b/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol @@ -176,6 +176,7 @@ contract DepositContract is IDepositContract, ERC165 { } // ==== // compileViaSSACFG: true +// experimental: true // ---- // constructor() // gas irOptimized: 809570 diff --git a/test/libsolidity/semanticTests/externalContracts/snark.sol b/test/libsolidity/semanticTests/externalContracts/snark.sol index 4d303926bd..0e6108c0f6 100644 --- a/test/libsolidity/semanticTests/externalContracts/snark.sol +++ b/test/libsolidity/semanticTests/externalContracts/snark.sol @@ -288,6 +288,7 @@ contract Test { // ==== // EVMVersion: >=constantinople // compileViaSSACFG: true +// experimental: true // ---- // library: Pairing // f() -> true From d153bfe7d4694beceb7469cf4b6769fc7b2886ae Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Wed, 4 Mar 2026 16:04:34 +0100 Subject: [PATCH 227/243] Stack shuffler: use mark-on-enqueue for bring up target slot stack traversal --- Changelog.md | 1 + libyul/backends/evm/StackHelpers.h | 7 +- ...huffler_bring_up_target_slot_bfs_dedup.yul | 83 +++++++++++++++++++ 3 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 test/libyul/yulOptimizerTests/fullSuite/stack_shuffler_bring_up_target_slot_bfs_dedup.yul diff --git a/Changelog.md b/Changelog.md index 823d346c76..aa3e2951f0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Compiler Features: * General: Restrict the existing experimental features (`generic-solidity`, `lsp`, `ethdebug`, `eof`, `evm`, `ast-import`, `evmasm-import`, `ir-ast`, `ssa-cfg`) to experimental mode. * Metadata: Store the state of the experimental mode in JSON and CBOR metadata. In CBOR this broadens the meaning of the existing `experimental` field, which used to indicate only the presence of certain experimental pragmas in the source. * Standard JSON Interface: Introduce `settings.experimental` setting required for enabling the experimental mode. +* Yul EVM Code Transform: Improve stack shuffler performance by fixing a BFS deduplication issue. Bugfixes: diff --git a/libyul/backends/evm/StackHelpers.h b/libyul/backends/evm/StackHelpers.h index c5d6384715..119642d12c 100644 --- a/libyul/backends/evm/StackHelpers.h +++ b/libyul/backends/evm/StackHelpers.h @@ -186,13 +186,13 @@ class Shuffler static bool bringUpTargetSlot(ShuffleOperations& _ops, size_t _targetOffset) { std::list toVisit{_targetOffset}; - std::set visited; + // mark on enqueue + std::set visited{_targetOffset}; while (!toVisit.empty()) { auto offset = *toVisit.begin(); toVisit.erase(toVisit.begin()); - visited.emplace(offset); if (_ops.targetMultiplicity(offset) > 0) { _ops.pushOrDupTarget(offset); @@ -204,11 +204,12 @@ class Shuffler !_ops.isCompatible(nextOffset, nextOffset) && _ops.isCompatible(nextOffset, offset) ) - if (!visited.count(nextOffset)) + if (visited.insert(nextOffset).second) // insert returns false if already present toVisit.emplace_back(nextOffset); } return false; } + /// Performs a single stack operation, transforming the source layout closer to the target layout. template static bool shuffleStep(Args&&... args) diff --git a/test/libyul/yulOptimizerTests/fullSuite/stack_shuffler_bring_up_target_slot_bfs_dedup.yul b/test/libyul/yulOptimizerTests/fullSuite/stack_shuffler_bring_up_target_slot_bfs_dedup.yul new file mode 100644 index 0000000000..53d64ca980 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/stack_shuffler_bring_up_target_slot_bfs_dedup.yul @@ -0,0 +1,83 @@ +// This is a regression test in context of https://github.com/argotorg/solidity/pull/16499: +// A BFS deduplication performance bug in `bringUpTargetSlot` where the same offset could be enqueued multiple times +// before being visited, by marking offsets as seen on enqueue rather than on dequeue. +{ + mstore(memoryguard(0x10000), 1) + sstore(mload(calldataload(0)), 1) + { + function foo_n_0(x_4, x_5) + { + if x_5 { + x_4 := 0x2000000000001 + } + x_4 := call( + call( + blockhash(0x200000000001), + 0x20000000001, + 0x2000000001, + mod("h", 32768), + mod(0x200000001, 32768), + mod(0x20000001, 32768), + mod(0x2000001, 32768) + ), + 0x200001, + 0x20001, + mod(0x2001, 32768), + mod(calldatasize(), 32768), + mod(calldatasize(), 32768), + mod( + call( + call( + x_4, + 0x201, + number(), + mod(0x21, 32768), + mod(0x3, 32768), + mod(0x3f, 32768), + mod(address(), 32768) + ), + 0x3ff, + 0x3fff, + mod(0x3ffff, 32768), + mod(9042383626829830, 32768), + mod(0x3fffff, 32768), + mod(0x3ffffff, 32768) + ), + 32768 + ) + ) + } + foo_n_0(0x200000000000000001, 0x20000000000000001) + foo_n_0(sload(224),calldataload(288)) + foo_n_0(0x3ffffffffffff, 0x3fffffffffffff) + foo_n_0(0x3fffffffffffffff, 0x3ffffffffffffffff) + foo_n_0(0x3ffffffffffffffffff, 0x3fffffffffffffffffff) + } +} +// ==== +// EVMVersion: >homestead +// ---- +// step: fullSuite +// +// { +// { +// mstore(memoryguard(0x010000), 1) +// sstore(mload(calldataload(0)), 1) +// let x := 0x200000000000000001 +// x := 0x2000000000001 +// let _1 := and(call(call(x, 0x201, number(), 0x21, 0x3, 0x3f, and(address(), 32767)), 0x3ff, 0x3fff, 32767, 6, 32767, 32767), 32767) +// pop(call(call(blockhash(0x200000000001), 0x20000000001, 0x2000000001, and("h", 32767), 1, 1, 1), 0x200001, 0x20001, 0x2001, and(calldatasize(), 32767), and(calldatasize(), 32767), _1)) +// let x_1 := sload(224) +// if calldataload(288) { x_1 := x } +// pop(call(call(blockhash(0x200000000001), 0x20000000001, 0x2000000001, and("h", 32767), 1, 1, 1), 0x200001, 0x20001, 0x2001, and(calldatasize(), 32767), and(calldatasize(), 32767), and(call(call(x_1, 0x201, number(), 0x21, 0x3, 0x3f, and(address(), 32767)), 0x3ff, 0x3fff, 32767, 6, 32767, 32767), 32767))) +// let x_2 := 0x3ffffffffffff +// x_2 := x +// pop(call(call(blockhash(0x200000000001), 0x20000000001, 0x2000000001, and("h", 32767), 1, 1, 1), 0x200001, 0x20001, 0x2001, and(calldatasize(), 32767), and(calldatasize(), 32767), and(call(call(x, 0x201, number(), 0x21, 0x3, 0x3f, and(address(), 32767)), 0x3ff, 0x3fff, 32767, 6, 32767, 32767), 32767))) +// let x_3 := 0x3fffffffffffffff +// x_3 := x +// pop(call(call(blockhash(0x200000000001), 0x20000000001, 0x2000000001, and("h", 32767), 1, 1, 1), 0x200001, 0x20001, 0x2001, and(calldatasize(), 32767), and(calldatasize(), 32767), and(call(call(x, 0x201, number(), 0x21, 0x3, 0x3f, and(address(), 32767)), 0x3ff, 0x3fff, 32767, 6, 32767, 32767), 32767))) +// let x_4 := 0x3ffffffffffffffffff +// x_4 := x +// pop(call(call(blockhash(0x200000000001), 0x20000000001, 0x2000000001, and("h", 32767), 1, 1, 1), 0x200001, 0x20001, 0x2001, and(calldatasize(), 32767), and(calldatasize(), 32767), and(call(call(x, 0x201, number(), 0x21, 0x3, 0x3f, and(address(), 32767)), 0x3ff, 0x3fff, 32767, 6, 32767, 32767), 32767))) +// } +// } From de054d1d44e4c530e0c89feb6eb7bb369648fb61 Mon Sep 17 00:00:00 2001 From: Masashi Takahashi <42906988+masshash@users.noreply.github.com> Date: Tue, 10 Mar 2026 19:31:13 +0900 Subject: [PATCH 228/243] docs: fix reentrancy vulnerability in micropayment close() example (#16497) Move freeze() call before external calls to follow the Checks-Effects-Interactions pattern and prevent reentrancy attacks in the SimplePaymentChannel contract. --- docs/examples/micropayment.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/micropayment.rst b/docs/examples/micropayment.rst index 373627c3d9..377ee42f6b 100644 --- a/docs/examples/micropayment.rst +++ b/docs/examples/micropayment.rst @@ -408,9 +408,9 @@ The full contract require(msg.sender == recipient); require(isValidSignature(amount, signature)); + freeze(); (bool success, ) = recipient.call{value: amount}(""); require(success); - freeze(); (success, ) = sender.call{value: address(this).balance}(""); require(success); } From 64e221f12a9342be3ca6017040d6de660caf7026 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Wed, 4 Mar 2026 10:22:22 +0100 Subject: [PATCH 229/243] ssa cfg codegen: make function return label slot handling explicit --- libyul/backends/evm/ssa/CodeTransform.cpp | 54 ++++++++------- libyul/backends/evm/ssa/CodeTransform.h | 2 + .../backends/evm/ssa/StackLayoutGenerator.cpp | 31 +++++++-- .../backends/evm/ssa/StackLayoutGenerator.h | 2 + libyul/backends/evm/ssa/StackUtils.cpp | 5 +- libyul/backends/evm/ssa/StackUtils.h | 2 +- .../ssa/stackLayoutGenerator/can_continue.yul | 67 +++++++++++++++++++ .../ssa/stackLayoutGenerator/function.yul | 24 +++---- .../ssa/stackLayoutGenerator/multi_return.yul | 12 ++-- .../ssa/stackLayoutGenerator/recursion.yul | 24 +++---- .../ssa/stackLayoutGenerator/simple_if.yul | 28 ++++---- 11 files changed, 176 insertions(+), 75 deletions(-) create mode 100644 test/libyul/ssa/stackLayoutGenerator/can_continue.yul diff --git a/libyul/backends/evm/ssa/CodeTransform.cpp b/libyul/backends/evm/ssa/CodeTransform.cpp index 6dc33ad7b3..ea83256946 100644 --- a/libyul/backends/evm/ssa/CodeTransform.cpp +++ b/libyul/backends/evm/ssa/CodeTransform.cpp @@ -107,7 +107,7 @@ CodeTransform::CodeTransform( SSACFG const& _cfg, SSACFGStackLayout const& _stackLayout, Scope::Function const* _function, - ControlFlow::FunctionGraphID + ControlFlow::FunctionGraphID _graphID ): m_assembly(_assembly), m_builtinContext(_builtinContext), @@ -115,6 +115,7 @@ CodeTransform::CodeTransform( m_callSites(_callSites), m_cfg(_cfg), m_stackLayout(_stackLayout), + m_graphID(_graphID), m_blockIsTransformed(_cfg.numBlocks(), false), m_blockLabels([this] { std::vector blockLabels; @@ -142,10 +143,12 @@ CodeTransform::CodeTransform( auto const findIt = m_functionLabels.find(_function); yulAssert(findIt != m_functionLabels.end()); m_assembly.appendLabel(findIt->second); - m_assembly.setStackHeight(static_cast(_function->numArguments)); + m_assembly.setStackHeight(static_cast(_function->numArguments) + (m_cfg.canContinue ? 1 : 0)); } StackData expectedStackTop; - expectedStackTop.reserve(m_cfg.arguments.size()); + expectedStackTop.reserve(m_cfg.arguments.size() + (m_cfg.function && m_cfg.canContinue ? 1 : 0)); + if (m_cfg.function && m_cfg.canContinue) + expectedStackTop.push_back(StackSlot::makeFunctionReturnLabel(m_graphID)); for (auto const& [_, valueID]: m_cfg.arguments | ranges::views::reverse) expectedStackTop.push_back(StackSlot::makeValueID(valueID)); assertLayoutCompatibility(m_stack.data(), expectedStackTop); @@ -339,35 +342,38 @@ void CodeTransform::operator()(SSACFG::BlockId const& _currentBlock, SSACFG::Bas void CodeTransform::operator()(SSACFG::BlockId const&, SSACFG::BasicBlock::FunctionReturn const& _functionReturn) { yulAssert(static_cast(m_stack.size()) == m_assembly.stackHeight()); + // Each CodeTransform instance handles exactly one function's CFG, so a FunctionReturn exit + // here necessarily belongs to m_cfg.function. No identity cross-check is needed. yulAssert(m_cfg.function); - // The return label sits physically below the tracked virtual stack. - // Inform the assembler so SWAP can reach it. - m_assembly.setStackHeight(m_assembly.stackHeight() + 1); - // Build target: [rv1, rv2, ..., rvN-1, rv0] (rv0 at top). - // After shuffle: EVM = [label, rv1, ..., rvN-1, rv0]. - // After SWAP(N): EVM = [rv0, rv1, ..., rvN-1, label]. - // After JUMP: EVM = [rv0, rv1, ..., rvN-1]. - // TODO: it would be better to account for function return labels from the entry of the function to the end symbolically - StackData returnTarget; - if (!_functionReturn.returnValues.empty()) + yulAssert(m_cfg.canContinue); + yulAssert(m_stack.size() == _functionReturn.returnValues.size() + 1, "There must be at least the function return label element on stack"); + yulAssert(m_stack.top().isFunctionReturnLabel()); + yulAssert(m_stack.top().functionReturnLabel() == m_graphID); + for (std::size_t i = 0; i < _functionReturn.returnValues.size(); ++i) { - returnTarget.reserve(_functionReturn.returnValues.size()); - for (std::size_t i = 1; i < _functionReturn.returnValues.size(); ++i) - returnTarget.push_back(StackSlot::makeValueID(_functionReturn.returnValues[i])); - returnTarget.push_back(StackSlot::makeValueID(_functionReturn.returnValues.front())); - StackShuffler::shuffle(m_stack, returnTarget, {}, returnTarget.size()); - m_assembly.appendInstruction(evmasm::swapInstruction( - static_cast(_functionReturn.returnValues.size()) - )); + auto const& returnValueSlot = m_stack.slot(StackOffset{i}); + yulAssert(returnValueSlot.isValueID()); + yulAssert(returnValueSlot.valueID() == _functionReturn.returnValues[i]); } - else - StackShuffler::shuffle(m_stack, returnTarget, {}, 0); m_assembly.appendJump(0, AbstractAssembly::JumpType::OutOfFunction); } -void CodeTransform::operator()(SSACFG::BlockId const&, SSACFG::BasicBlock::Terminated const&) +void CodeTransform::operator()(SSACFG::BlockId const& _blockId, SSACFG::BasicBlock::Terminated const&) { yulAssert(static_cast(m_stack.size()) == m_assembly.stackHeight()); + auto const& block = m_cfg.block(_blockId); + yulAssert(!block.operations.empty(), "Terminated block must have at least one operation."); + std::visit(util::GenericVisitor{ + [](SSACFG::BuiltinCall const& _builtin) { + yulAssert(_builtin.builtin.get().controlFlowSideEffects.terminatesOrReverts(), "Last operation of Terminated block must terminate or revert."); + }, + [](SSACFG::Call const& _call) { + yulAssert(!_call.canContinue, "Last operation of Terminated block must be a non-continuable call."); + }, + [](SSACFG::LiteralAssignment const&) { + yulAssert(false, "Terminated block cannot end with a literal assignment."); + } + }, block.operations.back().kind); // To be sure just emit another INVALID - should be removed by optimizer. m_assembly.appendInstruction(evmasm::Instruction::INVALID); } diff --git a/libyul/backends/evm/ssa/CodeTransform.h b/libyul/backends/evm/ssa/CodeTransform.h index 0764731c93..8898b41b54 100644 --- a/libyul/backends/evm/ssa/CodeTransform.h +++ b/libyul/backends/evm/ssa/CodeTransform.h @@ -67,6 +67,7 @@ struct AssemblyCallbacks case StackSlot::Kind::FunctionCallReturnLabel: { auto const& call = callSites->functionCall(_slot.functionCallReturnLabel()); + yulAssert(returnLabels->count(&call), "FunctionCallReturnLabel not pre-registered before shuffle."); assembly->appendLabelReference(returnLabels->at(&call)); return; } @@ -132,6 +133,7 @@ class CodeTransform CallSites const& m_callSites; SSACFG const& m_cfg; SSACFGStackLayout const& m_stackLayout; + ControlFlow::FunctionGraphID const m_graphID; std::vector m_blockIsTransformed; std::vector m_blockLabels; diff --git a/libyul/backends/evm/ssa/StackLayoutGenerator.cpp b/libyul/backends/evm/ssa/StackLayoutGenerator.cpp index 8e5f9b07a8..78d9dbfec9 100644 --- a/libyul/backends/evm/ssa/StackLayoutGenerator.cpp +++ b/libyul/backends/evm/ssa/StackLayoutGenerator.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -77,15 +78,25 @@ void declareJunk(StackType& _stack, LivenessAnalysis::LivenessData const& _live) } -SSACFGStackLayout StackLayoutGenerator::generate(LivenessAnalysis const& _liveness, CallSites const& _callSites, ControlFlow::FunctionGraphID _graphID) +SSACFGStackLayout StackLayoutGenerator::generate( + LivenessAnalysis const& _liveness, + CallSites const& _callSites, + ControlFlow::FunctionGraphID const _graphID +) { return StackLayoutGenerator(_liveness, _callSites, _graphID).m_resultLayout; } -StackLayoutGenerator::StackLayoutGenerator(LivenessAnalysis const& _liveness, CallSites const& _callSites, ControlFlow::FunctionGraphID): +StackLayoutGenerator::StackLayoutGenerator( + LivenessAnalysis const& _liveness, + CallSites const& _callSites, + ControlFlow::FunctionGraphID const _graphID +): m_cfg(_liveness.cfg()), m_liveness(_liveness), m_callSites(_callSites), + m_graphID(_graphID), + m_hasFunctionReturnLabel(_liveness.cfg().function && _liveness.cfg().canContinue), m_junkAdmittingBlocksFinder(std::make_unique(_liveness.cfg(), _liveness.topologicalSort())), m_resultLayout(m_cfg.numBlocks()) { @@ -133,6 +144,9 @@ void StackLayoutGenerator::defineStackIn(SSACFG::BlockId const& _blockId) { if (m_cfg.function) { + blockLayout.stackIn.reserve(m_cfg.arguments.size() + (m_hasFunctionReturnLabel ? 1u : 0u)); + if (m_hasFunctionReturnLabel) + blockLayout.stackIn.push_back(Slot::makeFunctionReturnLabel(m_graphID)); for (auto const& [_, valueID]: m_cfg.arguments | ranges::views::reverse) blockLayout.stackIn.push_back(Slot::makeValueID(valueID)); } @@ -246,7 +260,7 @@ void StackLayoutGenerator::visitBlock(SSACFG::BlockId const& _blockId) ) stack.declareJunk(depth); - std::size_t const targetSize = findOptimalTargetSize(stack.data(), requiredStackTop, opLiveOutWithoutOutputs, junkCanBeAdded); + std::size_t const targetSize = findOptimalTargetSize(stack.data(), requiredStackTop, opLiveOutWithoutOutputs, junkCanBeAdded, m_hasFunctionReturnLabel); StackShuffler::shuffle( stack, requiredStackTop, @@ -274,7 +288,7 @@ void StackLayoutGenerator::visitBlock(SSACFG::BlockId const& _blockId) if (!conditionSlotAlreadyFinal) { auto const condition = Slot::makeValueID(cjump->condition); - auto const targetSize = findOptimalTargetSize(stack.data(), {condition}, blockLiveOut, false); + auto const targetSize = findOptimalTargetSize(stack.data(), {condition}, blockLiveOut, false, m_hasFunctionReturnLabel); StackShuffler::shuffle( stack, {condition}, blockLiveOut, targetSize ); @@ -306,5 +320,14 @@ void StackLayoutGenerator::visitBlock(SSACFG::BlockId const& _blockId) } } + if (auto const* functionReturn = std::get_if(&block.exit)) + { + yulAssert(m_hasFunctionReturnLabel, "When there is a proper function return, we need to have a label for it"); + // in case there are return values, let's bring the function return label to the top + StackData returnStack = functionReturn->returnValues | ranges::views::transform(StackSlot::makeValueID) | ranges::to; + returnStack.push_back(StackSlot::makeFunctionReturnLabel(m_graphID)); + StackShuffler::shuffle(stack, returnStack); + } + blockLayout.stackOut = currentStackData; } diff --git a/libyul/backends/evm/ssa/StackLayoutGenerator.h b/libyul/backends/evm/ssa/StackLayoutGenerator.h index a1acfa57de..b14586404c 100644 --- a/libyul/backends/evm/ssa/StackLayoutGenerator.h +++ b/libyul/backends/evm/ssa/StackLayoutGenerator.h @@ -51,6 +51,8 @@ class StackLayoutGenerator SSACFG const& m_cfg; LivenessAnalysis const& m_liveness; CallSites const& m_callSites; + ControlFlow::FunctionGraphID m_graphID; + bool m_hasFunctionReturnLabel; std::unique_ptr m_junkAdmittingBlocksFinder; SSACFGStackLayout m_resultLayout; diff --git a/libyul/backends/evm/ssa/StackUtils.cpp b/libyul/backends/evm/ssa/StackUtils.cpp index d15b45a161..7f7a797957 100644 --- a/libyul/backends/evm/ssa/StackUtils.cpp +++ b/libyul/backends/evm/ssa/StackUtils.cpp @@ -44,10 +44,11 @@ std::size_t solidity::yul::ssa::findOptimalTargetSize StackData const& _stackData, StackData const& _targetArgs, LivenessAnalysis::LivenessData const& _targetLiveOut, - bool const _canIntroduceJunk + bool const _canIntroduceJunk, + bool const _hasFunctionReturnLabel ) { - std::size_t const minSize = _targetLiveOut.size() + _targetArgs.size(); + std::size_t const minSize = _targetLiveOut.size() + _targetArgs.size() + (_hasFunctionReturnLabel ? 1 : 0); boost::container::flat_map deficit; for (auto const& v: _targetLiveOut | ranges::views::keys) deficit[StackSlot::makeValueID(v)]++; diff --git a/libyul/backends/evm/ssa/StackUtils.h b/libyul/backends/evm/ssa/StackUtils.h index 7d30888739..cfc3fa96ba 100644 --- a/libyul/backends/evm/ssa/StackUtils.h +++ b/libyul/backends/evm/ssa/StackUtils.h @@ -50,7 +50,7 @@ struct CountingInstructionsCallbacks /// Transform stack data by replacing all its phi variables with their respective preimages. StackData stackPreImage(StackData _stack, PhiInverse const& _phiInverse); -std::size_t findOptimalTargetSize(StackData const& _stackData, StackData const& _targetArgs, LivenessAnalysis::LivenessData const& _targetLiveOut, bool _canIntroduceJunk); +std::size_t findOptimalTargetSize(StackData const& _stackData, StackData const& _targetArgs, LivenessAnalysis::LivenessData const& _targetLiveOut, bool _canIntroduceJunk, bool _hasFunctionReturnLabel); CallSites gatherCallSites(SSACFG const& _cfg); diff --git a/test/libyul/ssa/stackLayoutGenerator/can_continue.yul b/test/libyul/ssa/stackLayoutGenerator/can_continue.yul new file mode 100644 index 0000000000..e04a9b6b67 --- /dev/null +++ b/test/libyul/ssa/stackLayoutGenerator/can_continue.yul @@ -0,0 +1,67 @@ +{ + // a function that can continue depending on condition + function revert_wrapper(val, condition) + { + if iszero(condition) + { + revert(val, val) + } + // if we don't revert, we return nothing and the stack out should contain nothing but the return label + } + + revert_wrapper(42, 1) +} +// ---- +// digraph SSACFG { +// nodesep=0.7; +// graph[fontname="DejaVu Sans", rankdir=LR] +// node[shape=box,fontname="DejaVu Sans"]; +// +// Entry [label="Entry"]; +// Entry -> Block0_0; +// Block0_0 [label="\ +// IN: []\l\ +// \l\ +// [FunctionCallReturnLabel[0], lit0, lit1]\l\ +// revert_wrapper\l\ +// [FunctionCallReturnLabel[0]]\l\ +// \l\ +// OUT: []\l\ +// "]; +// Block0_0Exit [label="MainExit"]; +// Block0_0 -> Block0_0Exit; +// FunctionEntry_revert_wrapper_0 [label="function revert_wrapper: +// revert_wrapper(v0, v1)"]; +// FunctionEntry_revert_wrapper_0 -> Block1_0; +// Block1_0 [label="\ +// IN: [ReturnLabel[1], v1, v0]\l\ +// \l\ +// [ReturnLabel[1], v0, v1]\l\ +// iszero\l\ +// [ReturnLabel[1], v0, v2]\l\ +// \l\ +// OUT: [ReturnLabel[1], v0, v2]\l\ +// "]; +// Block1_0 -> Block1_0Exit; +// Block1_0Exit [label="{ If v2 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; +// Block1_0Exit:0 -> Block1_2 [style="solid"]; +// Block1_0Exit:1 -> Block1_1 [style="solid"]; +// Block1_1 [label="\ +// IN: [ReturnLabel[1], v0]\l\ +// \l\ +// [ReturnLabel[1], v0, v0]\l\ +// revert\l\ +// [ReturnLabel[1]]\l\ +// \l\ +// OUT: [ReturnLabel[1]]\l\ +// "]; +// Block1_1Exit [label="Terminated"]; +// Block1_1 -> Block1_1Exit; +// Block1_2 [label="\ +// IN: [ReturnLabel[1], JUNK]\l\ +// \l\ +// OUT: [ReturnLabel[1]]\l\ +// "]; +// Block1_2Exit [label="FunctionReturn[]"]; +// Block1_2 -> Block1_2Exit; +// } diff --git a/test/libyul/ssa/stackLayoutGenerator/function.yul b/test/libyul/ssa/stackLayoutGenerator/function.yul index a134c36f8b..c7e3c3209d 100644 --- a/test/libyul/ssa/stackLayoutGenerator/function.yul +++ b/test/libyul/ssa/stackLayoutGenerator/function.yul @@ -47,17 +47,17 @@ // r := f(v0, v1)"]; // FunctionEntry_f_0 -> Block1_0; // Block1_0 [label="\ -// IN: [v1, v0]\l\ +// IN: [ReturnLabel[1], v1, v0]\l\ // \l\ -// [v0, v1, v0]\l\ +// [ReturnLabel[1], v0, v1, v0]\l\ // add\l\ -// [v0, v2]\l\ +// [ReturnLabel[1], v0, v2]\l\ // \l\ -// [v0, v2]\l\ +// [ReturnLabel[1], v0, v2]\l\ // sub\l\ -// [v3]\l\ +// [ReturnLabel[1], v3]\l\ // \l\ -// OUT: [v3]\l\ +// OUT: [v3, ReturnLabel[1]]\l\ // "]; // Block1_0Exit [label="FunctionReturn[v3]"]; // Block1_0 -> Block1_0Exit; @@ -65,13 +65,13 @@ // g()"]; // FunctionEntry_g_0 -> Block2_0; // Block2_0 [label="\ -// IN: []\l\ +// IN: [ReturnLabel[2]]\l\ // \l\ -// [lit0, lit1]\l\ +// [ReturnLabel[2], lit0, lit1]\l\ // sstore\l\ -// []\l\ +// [ReturnLabel[2]]\l\ // \l\ -// OUT: []\l\ +// OUT: [ReturnLabel[2]]\l\ // "]; // Block2_0Exit [label="FunctionReturn[]"]; // Block2_0 -> Block2_0Exit; @@ -97,9 +97,9 @@ // v, w := i()"]; // FunctionEntry_i_0 -> Block4_0; // Block4_0 [label="\ -// IN: []\l\ +// IN: [ReturnLabel[4]]\l\ // \l\ -// OUT: []\l\ +// OUT: [lit1, lit2, ReturnLabel[4]]\l\ // "]; // Block4_0Exit [label="FunctionReturn[0x0202, 0x0303]"]; // Block4_0 -> Block4_0Exit; diff --git a/test/libyul/ssa/stackLayoutGenerator/multi_return.yul b/test/libyul/ssa/stackLayoutGenerator/multi_return.yul index c1f2b94d4b..76d4c8808d 100644 --- a/test/libyul/ssa/stackLayoutGenerator/multi_return.yul +++ b/test/libyul/ssa/stackLayoutGenerator/multi_return.yul @@ -42,17 +42,17 @@ // x, y := pair(v0)"]; // FunctionEntry_pair_0 -> Block1_0; // Block1_0 [label="\ -// IN: [v0]\l\ +// IN: [ReturnLabel[1], v0]\l\ // \l\ -// [v0, lit1, v0]\l\ +// [ReturnLabel[1], v0, lit1, v0]\l\ // add\l\ -// [v0, v1]\l\ +// [ReturnLabel[1], v0, v1]\l\ // \l\ -// [v1, lit2, v0]\l\ +// [ReturnLabel[1], v1, lit2, v0]\l\ // add\l\ -// [v1, v2]\l\ +// [ReturnLabel[1], v1, v2]\l\ // \l\ -// OUT: [v1, v2]\l\ +// OUT: [v1, v2, ReturnLabel[1]]\l\ // "]; // Block1_0Exit [label="FunctionReturn[v1, v2]"]; // Block1_0 -> Block1_0Exit; diff --git a/test/libyul/ssa/stackLayoutGenerator/recursion.yul b/test/libyul/ssa/stackLayoutGenerator/recursion.yul index 4a33f172d0..493532337b 100644 --- a/test/libyul/ssa/stackLayoutGenerator/recursion.yul +++ b/test/libyul/ssa/stackLayoutGenerator/recursion.yul @@ -33,38 +33,38 @@ // s := sum(v0)"]; // FunctionEntry_sum_0 -> Block1_0; // Block1_0 [label="\ -// IN: [v0]\l\ +// IN: [ReturnLabel[1], v0]\l\ // \l\ -// OUT: [v0, v0]\l\ +// OUT: [ReturnLabel[1], v0, v0]\l\ // "]; // Block1_0 -> Block1_0Exit; // Block1_0Exit [label="{ If v0 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_0Exit:0 -> Block1_2 [style="solid"]; // Block1_0Exit:1 -> Block1_1 [style="solid"]; // Block1_1 [label="\ -// IN: [v0]\l\ +// IN: [ReturnLabel[1], v0]\l\ // \l\ -// [v0, lit1, v0]\l\ +// [ReturnLabel[1], v0, lit1, v0]\l\ // sub\l\ -// [v0, v1]\l\ +// [ReturnLabel[1], v0, v1]\l\ // \l\ -// [v0, FunctionCallReturnLabel[0], v1]\l\ +// [ReturnLabel[1], v0, FunctionCallReturnLabel[0], v1]\l\ // sum\l\ -// [v0, FunctionCallReturnLabel[0], v2]\l\ +// [ReturnLabel[1], v0, FunctionCallReturnLabel[0], v2]\l\ // \l\ -// [v2, v0]\l\ +// [ReturnLabel[1], v2, v0]\l\ // add\l\ -// [v3]\l\ +// [ReturnLabel[1], v3]\l\ // \l\ -// OUT: [v3]\l\ +// OUT: [ReturnLabel[1], v3]\l\ // "]; // Block1_1 -> Block1_1Exit [arrowhead=none]; // Block1_1Exit [label="Jump" shape=oval]; // Block1_1Exit -> Block1_2 [style="solid"]; // Block1_2 [label="\ -// IN: [JUNK, phi0]\l\ +// IN: [ReturnLabel[1], JUNK, phi0]\l\ // \l\ -// OUT: [JUNK, phi0]\l\ +// OUT: [phi0, ReturnLabel[1]]\l\ // "]; // Block1_2Exit [label="FunctionReturn[phi0]"]; // Block1_2 -> Block1_2Exit; diff --git a/test/libyul/ssa/stackLayoutGenerator/simple_if.yul b/test/libyul/ssa/stackLayoutGenerator/simple_if.yul index f9e0039e33..2b72e0ceaf 100644 --- a/test/libyul/ssa/stackLayoutGenerator/simple_if.yul +++ b/test/libyul/ssa/stackLayoutGenerator/simple_if.yul @@ -35,41 +35,41 @@ // c := f(v0, v1)"]; // FunctionEntry_f_0 -> Block1_0; // Block1_0 [label="\ -// IN: [v1, v0]\l\ +// IN: [ReturnLabel[1], v1, v0]\l\ // \l\ -// [v1, v0, v0]\l\ +// [ReturnLabel[1], v1, v0, v0]\l\ // mload\l\ -// [v1, v0, v2]\l\ +// [ReturnLabel[1], v1, v0, v2]\l\ // \l\ -// OUT: [v1, v0, v2]\l\ +// OUT: [ReturnLabel[1], v1, v0, v2]\l\ // "]; // Block1_0 -> Block1_0Exit; // Block1_0Exit [label="{ If v2 | { <0> Zero | <1> NonZero }}" shape=Mrecord]; // Block1_0Exit:0 -> Block1_2 [style="solid"]; // Block1_0Exit:1 -> Block1_1 [style="solid"]; // Block1_1 [label="\ -// IN: [v1, v0]\l\ +// IN: [ReturnLabel[1], v1, v0]\l\ // \l\ -// [JUNK, v0, lit1, v0]\l\ +// [ReturnLabel[1], JUNK, v0, lit1, v0]\l\ // add\l\ -// [JUNK, v0, v3]\l\ +// [ReturnLabel[1], JUNK, v0, v3]\l\ // \l\ -// [JUNK, JUNK, v3, v3]\l\ +// [ReturnLabel[1], JUNK, JUNK, v3, v3]\l\ // revert\l\ -// [JUNK, JUNK]\l\ +// [ReturnLabel[1], JUNK, JUNK]\l\ // \l\ -// OUT: [JUNK, JUNK]\l\ +// OUT: [ReturnLabel[1], JUNK, JUNK]\l\ // "]; // Block1_1Exit [label="Terminated"]; // Block1_1 -> Block1_1Exit; // Block1_2 [label="\ -// IN: [v1, v0]\l\ +// IN: [ReturnLabel[1], v1, v0]\l\ // \l\ -// [v1, v0]\l\ +// [ReturnLabel[1], v1, v0]\l\ // add\l\ -// [v4]\l\ +// [ReturnLabel[1], v4]\l\ // \l\ -// OUT: [v4]\l\ +// OUT: [v4, ReturnLabel[1]]\l\ // "]; // Block1_2Exit [label="FunctionReturn[v4]"]; // Block1_2 -> Block1_2Exit; From fab9d5c2bcc1078b7f6b97804a903623af5254ad Mon Sep 17 00:00:00 2001 From: Mate Soos Date: Tue, 10 Mar 2026 17:17:59 +0100 Subject: [PATCH 230/243] Fix invalid reference use --- libsolidity/interface/CompilerStack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index a55b907199..96c0d3f6c2 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -379,7 +379,7 @@ bool CompilerStack::parse() for (size_t i = 0; i < sourcesToParse.size(); ++i) { - std::string const& path = sourcesToParse[i]; + std::string const path = sourcesToParse[i]; Source& source = m_sources[path]; source.ast = parser.parse(*source.charStream); if (!source.ast) From f47fa8da1628fb4d909fa790c7c384ef83bf26bd Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Tue, 18 Mar 2025 01:11:48 -0300 Subject: [PATCH 231/243] Add and update tests --- .../args | 1 + .../input.sol | 9 ++ .../output | 35 +++++++ .../yul_optimizer_erc7201_param_calldata/args | 1 + .../input.sol | 9 ++ .../output | 59 +++++++++++ .../yul_optimizer_erc7201_param_memory/args | 1 + .../input.sol | 10 ++ .../yul_optimizer_erc7201_param_memory/output | 48 +++++++++ .../yul_optimizer_erc7201_param_storage/args | 1 + .../input.sol | 10 ++ .../output | 97 +++++++++++++++++++ .../erc7201_equivalent_solidity_spec.sol | 26 +++++ ...7201_equivalent_solidity_spec_comptime.sol | 16 +++ ...1_layout_specifier_slot_match_comptime.sol | 19 ++++ .../erc7201_overflow_expression.sol | 7 ++ .../erc7201_param_abi_encode.sol | 20 ++++ .../erc7201_param_array_string_literal.sol | 7 ++ .../erc7201_param_locations.sol | 37 +++++++ .../erc7201_param_pure_function.sol | 17 ++++ .../erc7201_param_string_concat.sol | 15 +++ ...aram_string_literal_with_escaped_chars.sol | 18 ++++ .../erc7201_param_ternary_operator.sol | 18 ++++ .../erc7201_param_unicode_string_literal.sol | 14 +++ .../erc7201_param_unicode_string_variable.sol | 15 +++ ...aram_with_zero_last_byte_of_inner_hash.sol | 26 +++++ ..._zero_last_byte_of_inner_hash_comptime.sol | 24 +++++ .../array/length/builtin_erc7201_comptime.sol | 5 + .../array/length/string_as_array_length.sol | 8 ++ .../constantEvaluator/erc7201_builtin.sol | 6 ++ .../erc7201_builtin_abi_encode_comptime.sol | 3 + ...01_builtin_function_pointer_assignment.sol | 8 ++ ...rc7201_builtin_function_shadow_warning.sol | 5 + ...01_builtin_invalid_gas_function_option.sol | 7 ++ .../erc7201_builtin_no_call.sol | 7 ++ ...1_builtin_overflow_expression_comptime.sol | 5 + ...uiltin_param_bytes_converted_to_string.sol | 8 ++ ...in_param_constant_string_unitary_tuple.sol | 7 ++ .../erc7201_builtin_param_invalid_bytes.sol | 9 ++ ...7201_builtin_param_invalid_hex_literal.sol | 7 ++ ...1_builtin_param_invalid_number_literal.sol | 8 ++ .../erc7201_builtin_param_named.sol | 5 + ...in_param_string_unitary_tuple_comptime.sol | 2 + ...string_var_with_escaped_chars_comptime.sol | 3 + .../erc7201_builtin_param_string_variable.sol | 21 ++++ ...builtin_param_string_variable_comptime.sol | 6 ++ ...param_string_with_whitespaces_comptime.sol | 6 ++ .../erc7201_builtin_param_unicode_literal.sol | 6 ++ .../erc7201_builtin_param_wrong_count.sol | 9 ++ ...1_builtin_variable_name_shadow_warning.sol | 5 + .../erc7201_builtin_const_var_assignment.sol | 7 ++ .../erc7201_builtin_invalid_member_gas.sol | 7 ++ .../erc7201_builtin_invalid_member_value.sol | 7 ++ ...iltin_param_constant_variable_comptime.sol | 3 + ...am_imported_constant_variable_comptime.sol | 6 ++ ..._builtin_param_string_literal_comptime.sol | 2 + .../erc7201_builtin_as_custom_operator.sol | 3 + .../using/erc7201_builtin_using_for.sol | 3 + 58 files changed, 754 insertions(+) create mode 100644 test/cmdlineTests/yul_optimizer_erc7201_literal_comptime_evaluation/args create mode 100644 test/cmdlineTests/yul_optimizer_erc7201_literal_comptime_evaluation/input.sol create mode 100644 test/cmdlineTests/yul_optimizer_erc7201_literal_comptime_evaluation/output create mode 100644 test/cmdlineTests/yul_optimizer_erc7201_param_calldata/args create mode 100644 test/cmdlineTests/yul_optimizer_erc7201_param_calldata/input.sol create mode 100644 test/cmdlineTests/yul_optimizer_erc7201_param_calldata/output create mode 100644 test/cmdlineTests/yul_optimizer_erc7201_param_memory/args create mode 100644 test/cmdlineTests/yul_optimizer_erc7201_param_memory/input.sol create mode 100644 test/cmdlineTests/yul_optimizer_erc7201_param_memory/output create mode 100644 test/cmdlineTests/yul_optimizer_erc7201_param_storage/args create mode 100644 test/cmdlineTests/yul_optimizer_erc7201_param_storage/input.sol create mode 100644 test/cmdlineTests/yul_optimizer_erc7201_param_storage/output create mode 100644 test/libsolidity/semanticTests/builtinFunctions/erc7201_equivalent_solidity_spec.sol create mode 100644 test/libsolidity/semanticTests/builtinFunctions/erc7201_equivalent_solidity_spec_comptime.sol create mode 100644 test/libsolidity/semanticTests/builtinFunctions/erc7201_layout_specifier_slot_match_comptime.sol create mode 100644 test/libsolidity/semanticTests/builtinFunctions/erc7201_overflow_expression.sol create mode 100644 test/libsolidity/semanticTests/builtinFunctions/erc7201_param_abi_encode.sol create mode 100644 test/libsolidity/semanticTests/builtinFunctions/erc7201_param_array_string_literal.sol create mode 100644 test/libsolidity/semanticTests/builtinFunctions/erc7201_param_locations.sol create mode 100644 test/libsolidity/semanticTests/builtinFunctions/erc7201_param_pure_function.sol create mode 100644 test/libsolidity/semanticTests/builtinFunctions/erc7201_param_string_concat.sol create mode 100644 test/libsolidity/semanticTests/builtinFunctions/erc7201_param_string_literal_with_escaped_chars.sol create mode 100644 test/libsolidity/semanticTests/builtinFunctions/erc7201_param_ternary_operator.sol create mode 100644 test/libsolidity/semanticTests/builtinFunctions/erc7201_param_unicode_string_literal.sol create mode 100644 test/libsolidity/semanticTests/builtinFunctions/erc7201_param_unicode_string_variable.sol create mode 100644 test/libsolidity/semanticTests/builtinFunctions/erc7201_param_with_zero_last_byte_of_inner_hash.sol create mode 100644 test/libsolidity/semanticTests/builtinFunctions/erc7201_param_with_zero_last_byte_of_inner_hash_comptime.sol create mode 100644 test/libsolidity/syntaxTests/array/length/builtin_erc7201_comptime.sol create mode 100644 test/libsolidity/syntaxTests/array/length/string_as_array_length.sol create mode 100644 test/libsolidity/syntaxTests/constantEvaluator/erc7201_builtin.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_abi_encode_comptime.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_function_pointer_assignment.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_function_shadow_warning.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_invalid_gas_function_option.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_no_call.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_overflow_expression_comptime.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_bytes_converted_to_string.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_constant_string_unitary_tuple.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_invalid_bytes.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_invalid_hex_literal.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_invalid_number_literal.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_named.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_unitary_tuple_comptime.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_var_with_escaped_chars_comptime.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_variable.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_variable_comptime.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_with_whitespaces_comptime.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_unicode_literal.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_wrong_count.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_variable_name_shadow_warning.sol create mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/erc7201_builtin_const_var_assignment.sol create mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/erc7201_builtin_invalid_member_gas.sol create mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/erc7201_builtin_invalid_member_value.sol create mode 100644 test/libsolidity/syntaxTests/storageLayoutSpecifier/erc7201_builtin_param_constant_variable_comptime.sol create mode 100644 test/libsolidity/syntaxTests/storageLayoutSpecifier/erc7201_builtin_param_imported_constant_variable_comptime.sol create mode 100644 test/libsolidity/syntaxTests/storageLayoutSpecifier/erc7201_builtin_param_string_literal_comptime.sol create mode 100644 test/libsolidity/syntaxTests/using/erc7201_builtin_as_custom_operator.sol create mode 100644 test/libsolidity/syntaxTests/using/erc7201_builtin_using_for.sol diff --git a/test/cmdlineTests/yul_optimizer_erc7201_literal_comptime_evaluation/args b/test/cmdlineTests/yul_optimizer_erc7201_literal_comptime_evaluation/args new file mode 100644 index 0000000000..a536442c5c --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_erc7201_literal_comptime_evaluation/args @@ -0,0 +1 @@ +--ir-optimized --optimize --debug-info none diff --git a/test/cmdlineTests/yul_optimizer_erc7201_literal_comptime_evaluation/input.sol b/test/cmdlineTests/yul_optimizer_erc7201_literal_comptime_evaluation/input.sol new file mode 100644 index 0000000000..2918637d61 --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_erc7201_literal_comptime_evaluation/input.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + function f() public pure returns (uint) { + // IR output should contain the value calculated in compile time: + // 0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500 + return erc7201("example.main"); + } +} diff --git a/test/cmdlineTests/yul_optimizer_erc7201_literal_comptime_evaluation/output b/test/cmdlineTests/yul_optimizer_erc7201_literal_comptime_evaluation/output new file mode 100644 index 0000000000..c25f058ddf --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_erc7201_literal_comptime_evaluation/output @@ -0,0 +1,35 @@ +Optimized IR: +/// @use-src 0:"input.sol" +object "C_12" { + code { + { + let _1 := memoryguard(0x80) + mstore(64, _1) + if callvalue() { revert(0, 0) } + let _2 := datasize("C_12_deployed") + codecopy(_1, dataoffset("C_12_deployed"), _2) + return(_1, _2) + } + } + /// @use-src 0:"input.sol" + object "C_12_deployed" { + code { + { + let _1 := memoryguard(0x80) + mstore(64, _1) + if iszero(lt(calldatasize(), 4)) + { + if eq(0x26121ff0, shr(224, calldataload(0))) + { + if callvalue() { revert(0, 0) } + if slt(add(calldatasize(), not(3)), 0) { revert(0, 0) } + mstore(_1, 0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500) + return(_1, 32) + } + } + revert(0, 0) + } + } + data ".metadata" hex"" + } +} diff --git a/test/cmdlineTests/yul_optimizer_erc7201_param_calldata/args b/test/cmdlineTests/yul_optimizer_erc7201_param_calldata/args new file mode 100644 index 0000000000..a536442c5c --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_erc7201_param_calldata/args @@ -0,0 +1 @@ +--ir-optimized --optimize --debug-info none diff --git a/test/cmdlineTests/yul_optimizer_erc7201_param_calldata/input.sol b/test/cmdlineTests/yul_optimizer_erc7201_param_calldata/input.sol new file mode 100644 index 0000000000..3175540ec6 --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_erc7201_param_calldata/input.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + function f(string calldata id) public pure returns (uint) { + // Conversion of input from calldata to memory + // requires encoding which will appear in the IR output + return erc7201(id); + } +} diff --git a/test/cmdlineTests/yul_optimizer_erc7201_param_calldata/output b/test/cmdlineTests/yul_optimizer_erc7201_param_calldata/output new file mode 100644 index 0000000000..4ab15b8483 --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_erc7201_param_calldata/output @@ -0,0 +1,59 @@ +Optimized IR: +/// @use-src 0:"input.sol" +object "C_14" { + code { + { + let _1 := memoryguard(0x80) + mstore(64, _1) + if callvalue() { revert(0, 0) } + let _2 := datasize("C_14_deployed") + codecopy(_1, dataoffset("C_14_deployed"), _2) + return(_1, _2) + } + } + /// @use-src 0:"input.sol" + object "C_14_deployed" { + code { + { + let _1 := memoryguard(0x80) + if iszero(lt(calldatasize(), 4)) + { + if eq(0x91e145ef, shr(224, calldataload(0))) + { + if callvalue() { revert(0, 0) } + if slt(add(calldatasize(), not(3)), 32) { revert(0, 0) } + let offset := calldataload(4) + if gt(offset, 0xffffffffffffffff) { revert(0, 0) } + if iszero(slt(add(offset, 35), calldatasize())) { revert(0, 0) } + let length := calldataload(add(4, offset)) + let _2 := gt(length, 0xffffffffffffffff) + if _2 { revert(0, 0) } + let _3 := gt(add(add(offset, length), 36), calldatasize()) + if _3 { revert(0, 0) } + _2 := 0 + let newFreePtr := add(_1, and(add(and(add(length, 0x1f), not(31)), 63), not(31))) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, _1)) + { + mstore(0, shl(224, 0x4e487b71)) + mstore(4, 0x41) + revert(0, 36) + } + mstore(64, newFreePtr) + mstore(_1, length) + let dst := add(_1, 32) + _3 := 0 + calldatacopy(dst, add(offset, 36), length) + mstore(add(add(_1, length), 32), 0) + mstore(0, add(keccak256(dst, mload(_1)), not(0))) + let slot := and(keccak256(0, 32), not(255)) + let memPos := mload(64) + mstore(memPos, slot) + return(memPos, 32) + } + } + revert(0, 0) + } + } + data ".metadata" hex"" + } +} diff --git a/test/cmdlineTests/yul_optimizer_erc7201_param_memory/args b/test/cmdlineTests/yul_optimizer_erc7201_param_memory/args new file mode 100644 index 0000000000..a536442c5c --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_erc7201_param_memory/args @@ -0,0 +1 @@ +--ir-optimized --optimize --debug-info none diff --git a/test/cmdlineTests/yul_optimizer_erc7201_param_memory/input.sol b/test/cmdlineTests/yul_optimizer_erc7201_param_memory/input.sol new file mode 100644 index 0000000000..91ee533eb8 --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_erc7201_param_memory/input.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + function f() public pure returns (uint) { + // No encoding necessary, IR output will show value + // loaded directly from memory. + string memory namespaceID = "example.main"; + return erc7201(namespaceID); + } +} diff --git a/test/cmdlineTests/yul_optimizer_erc7201_param_memory/output b/test/cmdlineTests/yul_optimizer_erc7201_param_memory/output new file mode 100644 index 0000000000..aa9f5871e1 --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_erc7201_param_memory/output @@ -0,0 +1,48 @@ +Optimized IR: +/// @use-src 0:"input.sol" +object "C_16" { + code { + { + let _1 := memoryguard(0x80) + mstore(64, _1) + if callvalue() { revert(0, 0) } + let _2 := datasize("C_16_deployed") + codecopy(_1, dataoffset("C_16_deployed"), _2) + return(_1, _2) + } + } + /// @use-src 0:"input.sol" + object "C_16_deployed" { + code { + { + let _1 := memoryguard(0x80) + if iszero(lt(calldatasize(), 4)) + { + if eq(0x26121ff0, shr(224, calldataload(0))) + { + if callvalue() { revert(0, 0) } + if slt(add(calldatasize(), not(3)), 0) { revert(0, 0) } + let newFreePtr := add(_1, 64) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, _1)) + { + mstore(0, shl(224, 0x4e487b71)) + mstore(4, 0x41) + revert(0, 0x24) + } + mstore(64, newFreePtr) + mstore(_1, 12) + let _2 := add(_1, 32) + mstore(_2, "example.main") + mstore(0, add(keccak256(_2, 12), not(0))) + let slot := and(keccak256(0, 32), not(255)) + let memPos := mload(64) + mstore(memPos, slot) + return(memPos, 32) + } + } + revert(0, 0) + } + } + data ".metadata" hex"" + } +} diff --git a/test/cmdlineTests/yul_optimizer_erc7201_param_storage/args b/test/cmdlineTests/yul_optimizer_erc7201_param_storage/args new file mode 100644 index 0000000000..a536442c5c --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_erc7201_param_storage/args @@ -0,0 +1 @@ +--ir-optimized --optimize --debug-info none diff --git a/test/cmdlineTests/yul_optimizer_erc7201_param_storage/input.sol b/test/cmdlineTests/yul_optimizer_erc7201_param_storage/input.sol new file mode 100644 index 0000000000..9aede654ac --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_erc7201_param_storage/input.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; +contract C { + string namespace = "example.main"; + function f() public view returns (uint) { + // Conversion of input from storage to memory + // requires encoding which will appear in the IR output + return erc7201(namespace); + } +} diff --git a/test/cmdlineTests/yul_optimizer_erc7201_param_storage/output b/test/cmdlineTests/yul_optimizer_erc7201_param_storage/output new file mode 100644 index 0000000000..dea93fb0d1 --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_erc7201_param_storage/output @@ -0,0 +1,97 @@ +Optimized IR: +/// @use-src 0:"input.sol" +object "C_15" { + code { + { + let _1 := memoryguard(0x80) + mstore(64, _1) + if callvalue() { revert(0, 0) } + let _2 := sload(0x00) + let length := 0x00 + length := shr(1, _2) + let outOfPlaceEncoding := and(_2, 1) + if iszero(outOfPlaceEncoding) { length := and(length, 0x7f) } + if eq(outOfPlaceEncoding, lt(length, 32)) + { + mstore(0x00, shl(224, 0x4e487b71)) + mstore(4, 0x22) + revert(0x00, 0x24) + } + if gt(length, 31) + { + if gt(length, 12) + { + mstore(0x00, 0x00) + let data := keccak256(0x00, 32) + let oldSlotCount := shr(5, add(length, 31)) + let i := 0x00 + for { } lt(i, oldSlotCount) { i := add(i, 1) } + { sstore(add(data, i), 0x00) } + } + } + sstore(0x00, add("example.main", 24)) + let _3 := datasize("C_15_deployed") + codecopy(_1, dataoffset("C_15_deployed"), _3) + return(_1, _3) + } + } + /// @use-src 0:"input.sol" + object "C_15_deployed" { + code { + { + let _1 := memoryguard(0x80) + if iszero(lt(calldatasize(), 4)) + { + if eq(0x26121ff0, shr(224, calldataload(0))) + { + if callvalue() { revert(0, 0) } + if slt(add(calldatasize(), not(3)), 0) { revert(0, 0) } + let ret := 0 + let slotValue := sload(0) + let length := 0 + length := shr(1, slotValue) + let outOfPlaceEncoding := and(slotValue, 1) + if iszero(outOfPlaceEncoding) { length := and(length, 0x7f) } + if eq(outOfPlaceEncoding, lt(length, 32)) + { + mstore(0, shl(224, 0x4e487b71)) + mstore(4, 0x22) + revert(0, 0x24) + } + mstore(_1, length) + let updated_pos := add(_1, 32) + switch outOfPlaceEncoding + case 0 { + mstore(updated_pos, and(slotValue, not(255))) + ret := add(add(_1, shl(5, iszero(iszero(length)))), 32) + } + case 1 { + mstore(0, 0) + let dataPos := 18569430475105882587588266137607568536673111973893317399460219858819262702947 + let i := 0 + for { } lt(i, length) { i := add(i, 32) } + { + mstore(add(add(_1, i), 32), sload(dataPos)) + dataPos := add(dataPos, 1) + } + ret := add(add(_1, i), 32) + } + let newFreePtr := add(_1, and(add(sub(ret, _1), 31), not(31))) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, _1)) + { + mstore(0, shl(224, 0x4e487b71)) + mstore(4, 0x41) + revert(0, 0x24) + } + mstore(64, newFreePtr) + mstore(0, add(keccak256(updated_pos, mload(_1)), not(0))) + mstore(newFreePtr, and(keccak256(0, 32), not(255))) + return(newFreePtr, 32) + } + } + revert(0, 0) + } + } + data ".metadata" hex"" + } +} diff --git a/test/libsolidity/semanticTests/builtinFunctions/erc7201_equivalent_solidity_spec.sol b/test/libsolidity/semanticTests/builtinFunctions/erc7201_equivalent_solidity_spec.sol new file mode 100644 index 0000000000..32378c19c7 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/erc7201_equivalent_solidity_spec.sol @@ -0,0 +1,26 @@ +function erc7201Mock(string memory id) pure returns (uint256) { + return uint256( + keccak256(bytes.concat(bytes32(uint256(keccak256(bytes(id))) - 1))) & + ~bytes32(uint256(0xff)) + ); +} + +contract C { + // value from EIP-7201 (https://eips.ethereum.org/EIPS/eip-7201) + uint expectedSlot = 0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500; + string namespace = "example.main"; + + function builtinMatchesSolidityImplementation() public view returns (bool) { + assert(expectedSlot == erc7201Mock(namespace)); + + return + erc7201(namespace) == erc7201Mock("example.main") && + erc7201("example.main") == erc7201Mock("example.main"); + } + function builtinOutput() public view returns (uint) { + return erc7201(namespace); + } +} +// ---- +// builtinMatchesSolidityImplementation() -> true +// builtinOutput() -> 0x183a6125c38840424c4a85fa12bab2ab606c4b6d0e7cc73c0c06ba5300eab500 diff --git a/test/libsolidity/semanticTests/builtinFunctions/erc7201_equivalent_solidity_spec_comptime.sol b/test/libsolidity/semanticTests/builtinFunctions/erc7201_equivalent_solidity_spec_comptime.sol new file mode 100644 index 0000000000..ca387f1ae1 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/erc7201_equivalent_solidity_spec_comptime.sol @@ -0,0 +1,16 @@ +function erc7201Mock(string memory id) pure returns (uint256) { + return uint256( + keccak256(bytes.concat(bytes32(uint256(keccak256(bytes(id))) - 1))) & + ~bytes32(uint256(0xff)) + ); +} + +contract C { + uint8[erc7201("example.main")] array; + + function builtinMatchesSolidityImplementation() public view returns (bool) { + return array.length == erc7201Mock("example.main"); + } +} +// ---- +// builtinMatchesSolidityImplementation() -> true diff --git a/test/libsolidity/semanticTests/builtinFunctions/erc7201_layout_specifier_slot_match_comptime.sol b/test/libsolidity/semanticTests/builtinFunctions/erc7201_layout_specifier_slot_match_comptime.sol new file mode 100644 index 0000000000..88e9f2507c --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/erc7201_layout_specifier_slot_match_comptime.sol @@ -0,0 +1,19 @@ +function erc7201Mock(string memory id) pure returns (uint256) { + return uint256( + keccak256(bytes.concat(bytes32(uint256(keccak256(bytes(id))) - 1))) & + ~bytes32(uint256(0xff)) + ); +} + +contract C layout at erc7201("example.main") { + uint x; + function builtinMatchesSolidityImplementation() public pure returns (bool) { + uint firstSlot; + assembly { + firstSlot := x.slot + } + return firstSlot == erc7201Mock("example.main"); + } +} +// ---- +// builtinMatchesSolidityImplementation() -> true diff --git a/test/libsolidity/semanticTests/builtinFunctions/erc7201_overflow_expression.sol b/test/libsolidity/semanticTests/builtinFunctions/erc7201_overflow_expression.sol new file mode 100644 index 0000000000..9eb9af93da --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/erc7201_overflow_expression.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure returns (uint) { + return erc7201("main:example") + erc7201("main:example"); + } +} +// ---- +// f() -> FAILURE, hex"4e487b71", 0x11 diff --git a/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_abi_encode.sol b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_abi_encode.sol new file mode 100644 index 0000000000..e837564f9d --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_abi_encode.sol @@ -0,0 +1,20 @@ +function erc7201Mock(string memory id) pure returns (uint256) { + return uint256( + keccak256(bytes.concat(bytes32(uint256(keccak256(bytes(id))) - 1))) & + ~bytes32(uint256(0xff)) + ); +} + +contract C { + function builtinMatchesSolidityImplementation() public pure returns (bool) { + return + erc7201(string(abi.encode("example.main"))) == + erc7201Mock(string(abi.encode("example.main"))); + } + function builtinOutput() public pure returns (uint) { + return erc7201(string(abi.encode("example.main"))); + } +} +// ---- +// builtinMatchesSolidityImplementation() -> true +// builtinOutput() -> -14651554186193368082021334953908208762193027200365752719897746810709432803072 diff --git a/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_array_string_literal.sol b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_array_string_literal.sol new file mode 100644 index 0000000000..87f2477fd7 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_array_string_literal.sol @@ -0,0 +1,7 @@ +contract C { + function test() public pure returns (bool) { + return erc7201(["x"][0]) == erc7201(["y", "x"][1]); + } +} +// ---- +// test() -> true diff --git a/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_locations.sol b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_locations.sol new file mode 100644 index 0000000000..88f9f93c3d --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_locations.sol @@ -0,0 +1,37 @@ +function erc7201Mock(string memory id) pure returns (uint256) { + return uint256( + keccak256(bytes.concat(bytes32(uint256(keccak256(bytes(id))) - 1))) & + ~bytes32(uint256(0xff)) + ); +} + +contract C { + string storageVarStr = "example.contract"; + string constant constStorageVarStr = "example.contract"; + function storageVar() public view returns (bool) { + return erc7201(storageVarStr) == erc7201Mock("example.contract"); + } + function constStorageVar() public pure returns (bool) { + return erc7201(constStorageVarStr) == erc7201Mock("example.contract"); + } + function memoryVar() public pure returns (bool) { + string memory memoryVarStr = "example.main"; + return erc7201(memoryVarStr) == erc7201Mock("example.main"); + } + function calldataParam(string calldata calldataVarStr) public pure returns (bool) { + return erc7201(calldataVarStr) == erc7201Mock("example.main"); + } + function calldataSlice(bytes calldata namespaceID) public pure returns (bool) { + return erc7201(string(namespaceID[2:5])) == erc7201Mock("amp"); + } + function literalParam() public pure returns (bool) { + return erc7201("example.main") == erc7201Mock("example.main"); + } +} +// ---- +// storageVar() -> true +// constStorageVar() -> true +// memoryVar() -> true +// calldataParam(string): 0x20, 12, "example.main" -> true +// calldataSlice(bytes): 0x20, 12, "example.main" -> true +// literalParam() -> true diff --git a/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_pure_function.sol b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_pure_function.sol new file mode 100644 index 0000000000..31c4b871a5 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_pure_function.sol @@ -0,0 +1,17 @@ +function erc7201Mock(string memory id) pure returns (uint256) { + return uint256( + keccak256(bytes.concat(bytes32(uint256(keccak256(bytes(id))) - 1))) & + ~bytes32(uint256(0xff)) + ); +} + +contract C { + function f() public pure returns (string memory) { + return "example.main"; + } + function builtinMatchesSolidityImplementation() public pure returns (bool) { + return erc7201(f()) == erc7201Mock("example.main"); + } +} +// ---- +// builtinMatchesSolidityImplementation() -> true diff --git a/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_string_concat.sol b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_string_concat.sol new file mode 100644 index 0000000000..c737466065 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_string_concat.sol @@ -0,0 +1,15 @@ +function erc7201Mock(string memory id) pure returns (uint256) { + return uint256( + keccak256(bytes.concat(bytes32(uint256(keccak256(bytes(id))) - 1))) & + ~bytes32(uint256(0xff)) + ); +} + +contract C { + function builtinMatchesSolidityImplementation() public view returns (bool) { + string memory first = "example."; + return erc7201(string.concat(first, "main")) == erc7201Mock("example.main"); + } +} +// ---- +// builtinMatchesSolidityImplementation() -> true diff --git a/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_string_literal_with_escaped_chars.sol b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_string_literal_with_escaped_chars.sol new file mode 100644 index 0000000000..2ca4efd2c9 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_string_literal_with_escaped_chars.sol @@ -0,0 +1,18 @@ +function erc7201Mock(string memory id) pure returns (uint256) { + return uint256( + keccak256(bytes.concat(bytes32(uint256(keccak256(bytes(id))) - 1))) & + ~bytes32(uint256(0xff)) + ); +} + +contract C { + string constant CONST = "\n\"\'\\abc\ +def"; + function builtinMatchesSolidityImplementation() public pure returns (bool) { + return erc7201("\n\"\'\\abc\ +def") == erc7201Mock("\n\"\'\\abc\ +def"); + } +} +// ---- +// builtinMatchesSolidityImplementation() -> true diff --git a/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_ternary_operator.sol b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_ternary_operator.sol new file mode 100644 index 0000000000..1c88c20edc --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_ternary_operator.sol @@ -0,0 +1,18 @@ +function erc7201Mock(string memory id) pure returns (uint256) { + return uint256( + keccak256(bytes.concat(bytes32(uint256(keccak256(bytes(id))) - 1))) & + ~bytes32(uint256(0xff)) + ); +} + +contract C { + function simple() public pure returns (bool) { + return erc7201(true ? "x" : "y") == erc7201Mock("x"); + } + function compounded(bool c1, bool c2) public pure returns (bool) { + return erc7201(c1 ? "a" : (c2 ? "x" : "c")) == erc7201Mock("x"); + } +} +// ---- +// simple() -> true +// compounded(bool,bool): false, true -> true diff --git a/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_unicode_string_literal.sol b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_unicode_string_literal.sol new file mode 100644 index 0000000000..2b4048e384 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_unicode_string_literal.sol @@ -0,0 +1,14 @@ +function erc7201Mock(string memory id) pure returns (uint256) { + return uint256( + keccak256(bytes.concat(bytes32(uint256(keccak256(bytes(id))) - 1))) & + ~bytes32(uint256(0xff)) + ); +} + +contract C { + function builtinMatchesSolidityImplementation() public pure returns (bool) { + return erc7201(unicode"Hello 😃") == erc7201Mock(unicode"Hello 😃"); + } +} +// ---- +// builtinMatchesSolidityImplementation() -> true diff --git a/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_unicode_string_variable.sol b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_unicode_string_variable.sol new file mode 100644 index 0000000000..387623f9d6 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_unicode_string_variable.sol @@ -0,0 +1,15 @@ +function erc7201Mock(string memory id) pure returns (uint256) { + return uint256( + keccak256(bytes.concat(bytes32(uint256(keccak256(bytes(id))) - 1))) & + ~bytes32(uint256(0xff)) + ); +} + +contract C { + string unicodeStr = unicode"Hello 😃"; + function builtinMatchesSolidityImplementation() public view returns (bool) { + return erc7201(unicodeStr) == erc7201Mock(unicode"Hello 😃"); + } +} +// ---- +// builtinMatchesSolidityImplementation() -> true diff --git a/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_with_zero_last_byte_of_inner_hash.sol b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_with_zero_last_byte_of_inner_hash.sol new file mode 100644 index 0000000000..8f27ae212a --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_with_zero_last_byte_of_inner_hash.sol @@ -0,0 +1,26 @@ +function erc7201Mock(string memory id) pure returns (uint256) { + return uint256( + keccak256(bytes.concat(bytes32(uint256(keccak256(bytes(id))) - 1))) & + ~bytes32(uint256(0xff)) + ); +} + +contract C { + // The purpose of this test is to guarantee that the + // erc7201 implementation does not subtract 1 only from the + // last byte rather than the whole number during its intermediary operations. + function builtinMatchesSolidityImplementation() public pure returns (bool) { + string memory s = "85"; + bytes32 h = keccak256(bytes(s)); + + // "85" is intentionally chosen because it produces + // a keccak256 hash with the last byte zero + assert(uint8(h[31]) == 0); + + return + erc7201(s) == erc7201Mock(s) && + erc7201("85") == erc7201Mock("85"); + } +} +// ---- +// builtinMatchesSolidityImplementation() -> true diff --git a/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_with_zero_last_byte_of_inner_hash_comptime.sol b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_with_zero_last_byte_of_inner_hash_comptime.sol new file mode 100644 index 0000000000..d44fc416d2 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/erc7201_param_with_zero_last_byte_of_inner_hash_comptime.sol @@ -0,0 +1,24 @@ +function erc7201Mock(string memory id) pure returns (uint256) { + return uint256( + keccak256(bytes.concat(bytes32(uint256(keccak256(bytes(id))) - 1))) & + ~bytes32(uint256(0xff)) + ); +} + +contract C { + uint8[erc7201("85")] array; + // The purpose of this test is to guarantee that the + // erc7201 implementation does not subtract 1 only from the + // last byte rather than the whole number during its intermediary operations. + function builtinMatchesSolidityImplementation() public view returns (bool) { + bytes32 h = keccak256(bytes("85")); + + // "85" is intentionally chosen because it produces + // a keccak256 hash with the last byte zero + assert(uint8(h[31]) == 0); + + return array.length == erc7201Mock("85"); + } +} +// ---- +// builtinMatchesSolidityImplementation() -> true diff --git a/test/libsolidity/syntaxTests/array/length/builtin_erc7201_comptime.sol b/test/libsolidity/syntaxTests/array/length/builtin_erc7201_comptime.sol new file mode 100644 index 0000000000..83fe820dbe --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/builtin_erc7201_comptime.sol @@ -0,0 +1,5 @@ +contract C { + uint[erc7201("length")] array; +} +// ---- +// Warning 7325: (17-40): Type uint256[91485909057496517105622548919236807895873764128784270125752891283919605191424] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. diff --git a/test/libsolidity/syntaxTests/array/length/string_as_array_length.sol b/test/libsolidity/syntaxTests/array/length/string_as_array_length.sol new file mode 100644 index 0000000000..511b63ce4a --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/string_as_array_length.sol @@ -0,0 +1,8 @@ +contract C { + string constant length = "length"; + uint["length"] literalString; + uint[length] variableString; +} +// ---- +// TypeError 5462: (61-69): Invalid array length, expected integer literal or constant expression. +// TypeError 5462: (95-101): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/constantEvaluator/erc7201_builtin.sol b/test/libsolidity/syntaxTests/constantEvaluator/erc7201_builtin.sol new file mode 100644 index 0000000000..9878cb8561 --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/erc7201_builtin.sol @@ -0,0 +1,6 @@ +contract C { + uint constant x = erc7201("A"); + uint[x] array; +} +// ---- +// Warning 7325: (53-60): Type uint256[36579005187129934694193755934841191771209741707776365283473783080460440925696] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_abi_encode_comptime.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_abi_encode_comptime.sol new file mode 100644 index 0000000000..728a794d5e --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_abi_encode_comptime.sol @@ -0,0 +1,3 @@ +contract C layout at erc7201(string(abi.encodePacked(bytes32(uint256(2))))) {} +// ---- +// TypeError 1505: (21-75): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_function_pointer_assignment.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_function_pointer_assignment.sol new file mode 100644 index 0000000000..6d100ebee1 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_function_pointer_assignment.sol @@ -0,0 +1,8 @@ +contract C { + function (string memory) pure returns (uint256) functionPointer; + function f() public pure { + functionPointer = erc7201; + } +} +// ---- +// TypeError 7407: (139-146): Type function (string memory) pure returns (uint256) is not implicitly convertible to expected type function (string memory) pure returns (uint256). Special functions cannot be converted to function types. diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_function_shadow_warning.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_function_shadow_warning.sol new file mode 100644 index 0000000000..e66d69c154 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_function_shadow_warning.sol @@ -0,0 +1,5 @@ +function erc7201() pure returns (uint) { + return 42; +} +// ---- +// Warning 2319: (0-57): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_invalid_gas_function_option.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_invalid_gas_function_option.sol new file mode 100644 index 0000000000..b31050f311 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_invalid_gas_function_option.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure returns (uint) { + return erc7201{gas: 10}("x"); + } +} +// ---- +// TypeError 2193: (74-90): Function call options can only be set on external function calls or contract creations. diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_no_call.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_no_call.sol new file mode 100644 index 0000000000..e761cc4b89 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_no_call.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + erc7201; + } +} +// ---- +// Warning 6133: (52-59): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_overflow_expression_comptime.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_overflow_expression_comptime.sol new file mode 100644 index 0000000000..caf471a64d --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_overflow_expression_comptime.sol @@ -0,0 +1,5 @@ +contract C { + uint[erc7201("main:example") + erc7201("main:example")] array; +} +// ---- +// TypeError 2643: (22-71): Arithmetic error when computing constant value. diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_bytes_converted_to_string.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_bytes_converted_to_string.sol new file mode 100644 index 0000000000..80dd2e57d4 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_bytes_converted_to_string.sol @@ -0,0 +1,8 @@ +bytes constant bytesParam = "abcdef"; +contract C { + function f() public pure returns (uint256) { + uint256 x = erc7201(string(bytesParam)); + return x; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_constant_string_unitary_tuple.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_constant_string_unitary_tuple.sol new file mode 100644 index 0000000000..273d0b45bc --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_constant_string_unitary_tuple.sol @@ -0,0 +1,7 @@ +contract C { + string constant const = ("x"); + function constArg() public pure returns (uint) { + return erc7201(const); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_invalid_bytes.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_invalid_bytes.sol new file mode 100644 index 0000000000..e38b7b9faa --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_invalid_bytes.sol @@ -0,0 +1,9 @@ +bytes constant bytesArg = "abcdef"; +contract C { + function f() public pure returns (uint256) { + return erc7201(bytesArg); + } +} +// ---- +// TypeError 6896: (121-129): The argument to erc7201 builtin must be a string. The supplied argument has type bytes. +// TypeError 9553: (121-129): Invalid type for argument in function call. Invalid implicit conversion from bytes memory to string memory requested. diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_invalid_hex_literal.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_invalid_hex_literal.sol new file mode 100644 index 0000000000..6613b20a07 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_invalid_hex_literal.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure returns (uint) { + return erc7201(hex"001122FF"); + } +} +// ---- +// TypeError 9553: (82-95): Invalid type for argument in function call. Invalid implicit conversion from literal_string hex"001122ff" to string memory requested. Contains invalid UTF-8 sequence at position 3. diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_invalid_number_literal.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_invalid_number_literal.sol new file mode 100644 index 0000000000..bc121fcbbb --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_invalid_number_literal.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure returns (uint) { + return erc7201(123); + } +} +// ---- +// TypeError 6896: (82-85): The argument to erc7201 builtin must be a string. +// TypeError 9553: (82-85): Invalid type for argument in function call. Invalid implicit conversion from int_const 123 to string memory requested. diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_named.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_named.sol new file mode 100644 index 0000000000..87d608922f --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_named.sol @@ -0,0 +1,5 @@ +contract C { + uint x = erc7201({namespaceID: "example.main"}); +} +// ---- +// TypeError 4974: (26-64): Named argument "namespaceID" does not match function declaration. diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_unitary_tuple_comptime.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_unitary_tuple_comptime.sol new file mode 100644 index 0000000000..c85cde0937 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_unitary_tuple_comptime.sol @@ -0,0 +1,2 @@ +contract C layout at erc7201(("example.main")) {} +// ---- diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_var_with_escaped_chars_comptime.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_var_with_escaped_chars_comptime.sol new file mode 100644 index 0000000000..65f6fd18e8 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_var_with_escaped_chars_comptime.sol @@ -0,0 +1,3 @@ +contract C layout at erc7201("\n\"\'\\abc\ +def") {} +// ---- diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_variable.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_variable.sol new file mode 100644 index 0000000000..4c39650d42 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_variable.sol @@ -0,0 +1,21 @@ +string constant fileLevelStr = "test.file"; +contract C { + string constant stateVarStr = "example.contract"; + function fileLevel() public pure returns (uint) { + return erc7201(fileLevelStr); + } + function stateVar() public pure returns (uint) { + return erc7201(stateVarStr); + } + function localVar() public pure returns (uint) { + string memory localVarStr = "example.main"; + return erc7201(localVarStr); + } + function funcMemParam(string memory paramStr) public pure returns (uint) { + return erc7201(paramStr); + } + function funcCallDataParam(string calldata paramStr) public pure returns (uint) { + return erc7201(paramStr); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_variable_comptime.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_variable_comptime.sol new file mode 100644 index 0000000000..7d826ea7b1 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_variable_comptime.sol @@ -0,0 +1,6 @@ +string constant CONST = "test.file"; +contract C { + uint8[erc7201(CONST)] array; +} +// ---- +// Warning 7325: (54-75): Type uint8[5237610212305498718603509572682216073844539971870822423088087032295879652864] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_with_whitespaces_comptime.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_with_whitespaces_comptime.sol new file mode 100644 index 0000000000..0382a95cda --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_string_with_whitespaces_comptime.sol @@ -0,0 +1,6 @@ +contract C { + function f() public pure returns (uint) { + return erc7201("\tstring with spaces\n\r"); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_unicode_literal.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_unicode_literal.sol new file mode 100644 index 0000000000..5f22ae9022 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_unicode_literal.sol @@ -0,0 +1,6 @@ +contract C { + function f() public pure returns (uint) { + return erc7201(unicode"Hello 😃"); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_wrong_count.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_wrong_count.sol new file mode 100644 index 0000000000..1f62ae5ef1 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_param_wrong_count.sol @@ -0,0 +1,9 @@ +contract C { + uint x = erc7201(); + uint y = erc7201("12", "34"); + uint z = erc7201("A", "BC", "D"); +} +// ---- +// TypeError 6160: (26-35): Wrong argument count for function call: 0 arguments given but expected 1. +// TypeError 6160: (50-69): Wrong argument count for function call: 2 arguments given but expected 1. +// TypeError 6160: (84-107): Wrong argument count for function call: 3 arguments given but expected 1. diff --git a/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_variable_name_shadow_warning.sol b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_variable_name_shadow_warning.sol new file mode 100644 index 0000000000..59e62528b6 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/erc7201_builtin_variable_name_shadow_warning.sol @@ -0,0 +1,5 @@ +contract C { + uint erc7201; +} +// ---- +// Warning 2319: (17-29): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/erc7201_builtin_const_var_assignment.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/erc7201_builtin_const_var_assignment.sol new file mode 100644 index 0000000000..ec3357e106 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/erc7201_builtin_const_var_assignment.sol @@ -0,0 +1,7 @@ +contract C { + uint constant x = erc7201("abc"); + function f() public pure returns(uint) { + return mulmod(x, 10, 37); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/erc7201_builtin_invalid_member_gas.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/erc7201_builtin_invalid_member_gas.sol new file mode 100644 index 0000000000..d3eabc04f5 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/erc7201_builtin_invalid_member_gas.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + erc7201.gas(); + } +} +// ---- +// TypeError 9582: (47-58): Member "gas" not found or not visible after argument-dependent lookup in function (string memory) pure returns (uint256). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/erc7201_builtin_invalid_member_value.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/erc7201_builtin_invalid_member_value.sol new file mode 100644 index 0000000000..a197e81510 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/erc7201_builtin_invalid_member_value.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + erc7201.value(); + } +} +// ---- +// TypeError 8820: (47-60): Member "value" is only available for payable functions. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/erc7201_builtin_param_constant_variable_comptime.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/erc7201_builtin_param_constant_variable_comptime.sol new file mode 100644 index 0000000000..3b32caae61 --- /dev/null +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/erc7201_builtin_param_constant_variable_comptime.sol @@ -0,0 +1,3 @@ +string constant storageBase = "myStorageBase"; +contract C layout at erc7201(storageBase) { } +// ---- diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/erc7201_builtin_param_imported_constant_variable_comptime.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/erc7201_builtin_param_imported_constant_variable_comptime.sol new file mode 100644 index 0000000000..6f68f8c74c --- /dev/null +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/erc7201_builtin_param_imported_constant_variable_comptime.sol @@ -0,0 +1,6 @@ +==== Source: A ==== +string constant storageBase = "myStorageBase"; +==== Source: B ==== +import "A"; +contract C layout at erc7201(storageBase) {} +// ---- diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/erc7201_builtin_param_string_literal_comptime.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/erc7201_builtin_param_string_literal_comptime.sol new file mode 100644 index 0000000000..e8175eae0a --- /dev/null +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/erc7201_builtin_param_string_literal_comptime.sol @@ -0,0 +1,2 @@ +contract C layout at erc7201("storageBase") { } +// ---- diff --git a/test/libsolidity/syntaxTests/using/erc7201_builtin_as_custom_operator.sol b/test/libsolidity/syntaxTests/using/erc7201_builtin_as_custom_operator.sol new file mode 100644 index 0000000000..282353ac5c --- /dev/null +++ b/test/libsolidity/syntaxTests/using/erc7201_builtin_as_custom_operator.sol @@ -0,0 +1,3 @@ +using {erc7201 as -} for uint; +// ---- +// TypeError 8187: (7-14): Expected function name. diff --git a/test/libsolidity/syntaxTests/using/erc7201_builtin_using_for.sol b/test/libsolidity/syntaxTests/using/erc7201_builtin_using_for.sol new file mode 100644 index 0000000000..5e1cf4fd9e --- /dev/null +++ b/test/libsolidity/syntaxTests/using/erc7201_builtin_using_for.sol @@ -0,0 +1,3 @@ +using {erc7201} for string; +// ---- +// TypeError 8187: (7-14): Expected function name. From 64893d8b9cfb126a6689176128a8d67b970049f1 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Tue, 18 Mar 2025 00:34:53 -0300 Subject: [PATCH 232/243] Introduce new builtin function erc7201 --- Changelog.md | 1 + libsolidity/analysis/ConstantEvaluator.cpp | 133 +++++++++++++----- libsolidity/analysis/ConstantEvaluator.h | 16 ++- .../analysis/DeclarationTypeChecker.cpp | 6 +- libsolidity/analysis/GlobalContext.cpp | 4 +- .../analysis/PostTypeContractLevelChecker.cpp | 9 +- libsolidity/analysis/StaticAnalyzer.cpp | 36 +++-- libsolidity/analysis/TypeChecker.cpp | 32 +++++ libsolidity/analysis/TypeChecker.h | 2 + libsolidity/ast/ASTUtils.cpp | 19 +++ libsolidity/ast/ASTUtils.h | 7 +- libsolidity/ast/Types.cpp | 4 +- libsolidity/ast/Types.h | 1 + libsolidity/codegen/ExpressionCompiler.cpp | 45 ++++++ libsolidity/codegen/YulUtilFunctions.cpp | 17 +++ libsolidity/codegen/YulUtilFunctions.h | 8 ++ .../codegen/ir/IRGeneratorForStatements.cpp | 31 ++++ libsolidity/formal/SMTEncoder.cpp | 7 +- 18 files changed, 310 insertions(+), 68 deletions(-) diff --git a/Changelog.md b/Changelog.md index aa3e2951f0..d9f94b3495 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.8.35 (unreleased) Language Features: +* General: Add a builtin that computes the base slot of a storage namespace using the `erc7201` formula from ERC-7201. Compiler Features: * Commandline Interface: Introduce `--experimental` flag required for enabling the experimental mode. diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index 691d273d58..f094988d9b 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -26,14 +26,18 @@ #include #include #include +#include +#include +#include #include using namespace solidity; using namespace solidity::frontend; using namespace solidity::langutil; +using namespace solidity::util; -using TypedRational = ConstantEvaluator::TypedRational; +using TypedValue = ConstantEvaluator::TypedValue; namespace { @@ -231,37 +235,59 @@ std::optional ConstantEvaluator::evaluateUnaryOperator(Token _operator namespace { -std::optional convertType(rational const& _value, Type const& _type) +TypedValue convertType(rational const& _value, Type const& _type) { if (_type.category() == Type::Category::RationalNumber) - return TypedRational{TypeProvider::rationalNumber(_value), _value}; + return TypedValue{TypeProvider::rationalNumber(_value), _value}; else if (auto const* integerType = dynamic_cast(&_type)) { if (_value > integerType->maxValue() || _value < integerType->minValue()) - return std::nullopt; + return TypedValue{}; else - return TypedRational{&_type, _value.numerator() / _value.denominator()}; + return TypedValue{&_type, _value.numerator() / _value.denominator()}; } else - return std::nullopt; + return TypedValue{}; +} + +TypedValue convertType(std::string const& _value, Type const& _type) +{ + if ( + _type.category() != Type::Category::StringLiteral && + _type.category() != Type::Category::Array + ) + return TypedValue{}; + return TypedValue{&_type, _value}; } -std::optional convertType(std::optional const& _value, Type const& _type) +TypedValue convertType(TypedValue const& _value, Type const& _type) { - return _value ? convertType(_value->value, _type) : std::nullopt; + return std::visit(util::GenericVisitor{ + [&](std::string const& value) { + return convertType(value, _type); + }, + [&](rational const& value) { + return convertType(value, _type); + }, + [&](std::monostate const&) { + return TypedValue{}; + } + }, _value.value); } -std::optional constantToTypedValue(Type const& _type) +TypedValue constantToTypedValue(Type const& _type) { if (_type.category() == Type::Category::RationalNumber) - return TypedRational{&_type, dynamic_cast(_type).value()}; - else - return std::nullopt; + return TypedValue{&_type, dynamic_cast(_type).value()}; + else if (_type.category() == Type::Category::StringLiteral) + return TypedValue{&_type, dynamic_cast(_type).value()}; + + return TypedValue{}; } } -std::optional ConstantEvaluator::evaluate( +TypedValue ConstantEvaluator::evaluate( langutil::ErrorReporter& _errorReporter, Expression const& _expr ) @@ -269,7 +295,7 @@ std::optional ConstantEvaluator::evaluate( return ConstantEvaluator{_errorReporter}.evaluate(_expr); } -std::optional ConstantEvaluator::tryEvaluate(Expression const& _expr) +TypedValue ConstantEvaluator::tryEvaluate(Expression const& _expr) { ErrorList errorList; ErrorReporter errorReporter(errorList); @@ -279,12 +305,12 @@ std::optional ConstantEvaluator::tryEvaluate(Expression const& _e } catch (FatalError const&) { - return std::nullopt; + return TypedValue{}; } } -std::optional ConstantEvaluator::evaluate(ASTNode const& _node) +TypedValue ConstantEvaluator::evaluate(ASTNode const& _node) { if (!m_values.count(&_node)) { @@ -293,7 +319,7 @@ std::optional ConstantEvaluator::evaluate(ASTNode const& _node) solAssert(varDecl->isConstant(), ""); // In some circumstances, we do not yet have a type for the variable. if (!varDecl->value() || !varDecl->type()) - m_values[&_node] = std::nullopt; + m_values[&_node] = TypedValue{}; else { m_depth++; @@ -311,7 +337,7 @@ std::optional ConstantEvaluator::evaluate(ASTNode const& _node) { expression->accept(*this); if (!m_values.count(&_node)) - m_values[&_node] = std::nullopt; + m_values[&_node] = TypedValue{}; } } return m_values.at(&_node); @@ -319,21 +345,21 @@ std::optional ConstantEvaluator::evaluate(ASTNode const& _node) void ConstantEvaluator::endVisit(UnaryOperation const& _operation) { - std::optional value = evaluate(_operation.subExpression()); - if (!value) + TypedValue value = evaluate(_operation.subExpression()); + if (!value.type) return; - Type const* resultType = value->type->unaryOperatorResult(_operation.getOperator()); + Type const* resultType = value.type->unaryOperatorResult(_operation.getOperator()); if (!resultType) return; value = convertType(value, *resultType); - if (!value) + if (!std::holds_alternative(value.value)) return; - if (std::optional result = evaluateUnaryOperator(_operation.getOperator(), value->value)) + if (std::optional result = evaluateUnaryOperator(_operation.getOperator(), std::get(value.value))) { - std::optional convertedValue = convertType(*result, *resultType); - if (!convertedValue) + TypedValue convertedValue = convertType(*result, *resultType); + if (!convertedValue.type) m_errorReporter.fatalTypeError( 3667_error, _operation.location(), @@ -345,9 +371,9 @@ void ConstantEvaluator::endVisit(UnaryOperation const& _operation) void ConstantEvaluator::endVisit(BinaryOperation const& _operation) { - std::optional left = evaluate(_operation.leftExpression()); - std::optional right = evaluate(_operation.rightExpression()); - if (!left || !right) + TypedValue left = evaluate(_operation.leftExpression()); + TypedValue right = evaluate(_operation.rightExpression()); + if (!left.type || !right.type) return; // If this is implemented in the future: Comparison operators have a "binaryOperatorResult" @@ -355,7 +381,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) if (TokenTraits::isCompareOp(_operation.getOperator())) return; - Type const* resultType = left->type->binaryOperatorResult(_operation.getOperator(), right->type); + Type const* resultType = left.type->binaryOperatorResult(_operation.getOperator(), right.type); if (!resultType) { m_errorReporter.fatalTypeError( @@ -364,22 +390,29 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) "Operator " + std::string(TokenTraits::toString(_operation.getOperator())) + " not compatible with types " + - left->type->toString() + + left.type->toString() + " and " + - right->type->toString() + right.type->toString() ); return; } left = convertType(left, *resultType); right = convertType(right, *resultType); - if (!left || !right) + if ( + !std::holds_alternative(left.value) || + !std::holds_alternative(right.value) + ) return; - if (std::optional value = evaluateBinaryOperator(_operation.getOperator(), left->value, right->value)) + if (std::optional value = evaluateBinaryOperator( + _operation.getOperator(), + std::get(left.value), + std::get(right.value) + )) { - std::optional convertedValue = convertType(*value, *resultType); - if (!convertedValue) + TypedValue convertedValue = convertType(*value, *resultType); + if (!convertedValue.type) m_errorReporter.fatalTypeError( 2643_error, _operation.location(), @@ -407,3 +440,33 @@ void ConstantEvaluator::endVisit(TupleExpression const& _tuple) if (!_tuple.isInlineArray() && _tuple.components().size() == 1) m_values[&_tuple] = evaluate(*_tuple.components().front()); } + +void ConstantEvaluator::endVisit(FunctionCall const& _functionCall) +{ + auto const* builtinFunction = dynamic_cast(ASTNode::referencedDeclaration(_functionCall.expression())); + if (!builtinFunction) + return; + + auto const* functionType = builtinFunction->functionType(true); + solAssert(functionType); + switch (functionType->kind()) + { + case FunctionType::Kind::ERC7201: + { + solAssert(_functionCall.arguments().size() == 1); + auto stringArg = evaluate(*(_functionCall.arguments()[0].get())); + if (!std::holds_alternative(stringArg.value)) + return; + + h256 innerKeccak = keccak256(std::get(stringArg.value)); + h256 outerKeccak = keccak256(h256(u256(innerKeccak) - 1)); + outerKeccak.data()[31] = 0; + u256 slot = outerKeccak; + solAssert(functionType->returnParameterTypes().size() == 1); + m_values[&_functionCall] = TypedValue{functionType->returnParameterTypes()[0], rational{slot}}; + break; + } + default: + break; + } +} diff --git a/libsolidity/analysis/ConstantEvaluator.h b/libsolidity/analysis/ConstantEvaluator.h index 297d5f6e80..67adc0be1a 100644 --- a/libsolidity/analysis/ConstantEvaluator.h +++ b/libsolidity/analysis/ConstantEvaluator.h @@ -46,20 +46,21 @@ class TypeChecker; class ConstantEvaluator: private ASTConstVisitor { public: - struct TypedRational + struct TypedValue { + // Type may be RationalType or IntegerType for value rational Type const* type; - rational value; + std::variant value; }; - static std::optional evaluate( + static TypedValue evaluate( langutil::ErrorReporter& _errorReporter, Expression const& _expr ); /// Works the same as `evaluate` but swallows any errors that might occur in the evaluation and simply returns - /// `std::nullopt` instead. - static std::optional tryEvaluate(Expression const& _expr); + /// `TypedValue` containing `std::monostate` instead. + static TypedValue tryEvaluate(Expression const& _expr); /// Performs arbitrary-precision evaluation of a binary operator. Returns nullopt on cases like /// division by zero or e.g. bit operators applied to fractional values. @@ -72,19 +73,20 @@ class ConstantEvaluator: private ASTConstVisitor private: explicit ConstantEvaluator(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} - std::optional evaluate(ASTNode const& _node); + TypedValue evaluate(ASTNode const& _node); void endVisit(BinaryOperation const& _operation) override; void endVisit(UnaryOperation const& _operation) override; void endVisit(Literal const& _literal) override; void endVisit(Identifier const& _identifier) override; void endVisit(TupleExpression const& _tuple) override; + void endVisit(FunctionCall const& _functionCall) override; langutil::ErrorReporter& m_errorReporter; /// Current recursion depth. size_t m_depth = 0; /// Values of sub-expressions and variable declarations. - std::map> m_values; + std::map m_values; }; } diff --git a/libsolidity/analysis/DeclarationTypeChecker.cpp b/libsolidity/analysis/DeclarationTypeChecker.cpp index e5549f0350..e2f0cf6e8b 100644 --- a/libsolidity/analysis/DeclarationTypeChecker.cpp +++ b/libsolidity/analysis/DeclarationTypeChecker.cpp @@ -338,8 +338,10 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName) std::optional lengthValue; if (length->annotation().type && length->annotation().type->category() == Type::Category::RationalNumber) lengthValue = dynamic_cast(*length->annotation().type).value(); - else if (std::optional value = ConstantEvaluator::evaluate(m_errorReporter, *length)) - lengthValue = value->value; + else if (ConstantEvaluator::TypedValue value = ConstantEvaluator::evaluate(m_errorReporter, *length); + std::holds_alternative(value.value) + ) + lengthValue = std::get(value.value); if (!lengthValue) m_errorReporter.typeError( diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 0c265d2451..58b8dac34c 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -62,7 +62,8 @@ int magicVariableToID(std::string const& _name) {"tx", -26}, {"type", -27}, {"this", -28}, - {"blobhash", -29} + {"blobhash", -29}, + {"erc7201", -30} }; if (auto id = magicVariables.find(_name); id != magicVariables.end()) @@ -83,6 +84,7 @@ inline std::vector> constructMag magicVarDecl("block", TypeProvider::magic(MagicType::Kind::Block)), magicVarDecl("blockhash", TypeProvider::function(strings{"uint256"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, StateMutability::View)), magicVarDecl("ecrecover", TypeProvider::function(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, StateMutability::Pure)), + magicVarDecl("erc7201", TypeProvider::function(strings{"string memory"}, strings{"uint256"}, FunctionType::Kind::ERC7201, StateMutability::Pure)), magicVarDecl("gasleft", TypeProvider::function(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, StateMutability::View)), magicVarDecl("keccak256", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, StateMutability::Pure)), magicVarDecl("msg", TypeProvider::magic(MagicType::Kind::Message)), diff --git a/libsolidity/analysis/PostTypeContractLevelChecker.cpp b/libsolidity/analysis/PostTypeContractLevelChecker.cpp index f8f4b6ef69..be5d008a61 100644 --- a/libsolidity/analysis/PostTypeContractLevelChecker.cpp +++ b/libsolidity/analysis/PostTypeContractLevelChecker.cpp @@ -92,7 +92,6 @@ void PostTypeContractLevelChecker::checkStorageLayoutSpecifier(ContractDefinitio if (!*baseSlotExpression.annotation().isPure) { - // TODO: introduce and handle erc7201 as a builtin function m_errorReporter.typeError( 1139_error, baseSlotExpression.location(), @@ -137,8 +136,9 @@ void PostTypeContractLevelChecker::checkStorageLayoutSpecifier(ContractDefinitio rational baseSlotRationalValue; if (integerType) { - std::optional typedRational = ConstantEvaluator::evaluate(m_errorReporter, baseSlotExpression); - if (!typedRational) + ConstantEvaluator::TypedValue typedRational = ConstantEvaluator::evaluate(m_errorReporter, baseSlotExpression); + solAssert(!typedRational.type || dynamic_cast(typedRational.type)); + if (!typedRational.type) { m_errorReporter.typeError( 1505_error, @@ -148,7 +148,8 @@ void PostTypeContractLevelChecker::checkStorageLayoutSpecifier(ContractDefinitio ); return; } - baseSlotRationalValue = typedRational->value; + solAssert(std::holds_alternative(typedRational.value)); + baseSlotRationalValue = std::get(typedRational.value); } else { diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index dfd262c8de..1ad3e77441 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -314,15 +314,18 @@ bool StaticAnalyzer::visit(BinaryOperation const& _operation) if ( *_operation.rightExpression().annotation().isPure && (_operation.getOperator() == Token::Div || _operation.getOperator() == Token::Mod) && - ConstantEvaluator::evaluate(m_errorReporter, _operation.leftExpression()) + ConstantEvaluator::evaluate(m_errorReporter, _operation.leftExpression()).type ) - if (auto rhs = ConstantEvaluator::evaluate(m_errorReporter, _operation.rightExpression())) - if (rhs->value == 0) - m_errorReporter.typeError( - 1211_error, - _operation.location(), - (_operation.getOperator() == Token::Div) ? "Division by zero." : "Modulo zero." - ); + if ( + auto rhs = ConstantEvaluator::evaluate(m_errorReporter, _operation.rightExpression()); + std::holds_alternative(rhs.value) && + std::get(rhs.value) == 0 + ) + m_errorReporter.typeError( + 1211_error, + _operation.location(), + (_operation.getOperator() == Token::Div) ? "Division by zero." : "Modulo zero." + ); return true; } @@ -337,13 +340,16 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall) { solAssert(_functionCall.arguments().size() == 3, ""); if (*_functionCall.arguments()[2]->annotation().isPure) - if (auto lastArg = ConstantEvaluator::evaluate(m_errorReporter, *(_functionCall.arguments())[2])) - if (lastArg->value == 0) - m_errorReporter.typeError( - 4195_error, - _functionCall.location(), - "Arithmetic modulo zero." - ); + if ( + auto lastArg = ConstantEvaluator::evaluate(m_errorReporter, *(_functionCall.arguments())[2]); + std::holds_alternative(lastArg.value) && + std::get(lastArg.value) == 0 + ) + m_errorReporter.typeError( + 4195_error, + _functionCall.location(), + "Arithmetic modulo zero." + ); } if ( m_currentContract && diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index b54e591d20..c613dd796d 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2398,6 +2398,34 @@ void TypeChecker::typeCheckBytesConcatFunction( } } +void TypeChecker::typeCheckERC7201Builtin(FunctionCall const& _functionCall, FunctionType const* _functionType) +{ + // erc7201 builtin accepts only 1 argument. Invalid cases with different number + // of arguments are treated after by function `typeCheckFunctionGeneralChecks` + if (_functionCall.arguments().size() > 0) + { + Type const* argumentType = _functionCall.arguments()[0].get()->annotation().type; + solAssert(argumentType); + auto const* arrayType = dynamic_cast(argumentType); + + if ( + !dynamic_cast(argumentType) && + (!arrayType || !arrayType->isString()) + ) + { + std::string errorMsg = "The argument to erc7201 builtin must be a string."; + if (arrayType && arrayType->isByteArray()) + errorMsg += " The supplied argument has type bytes."; + m_errorReporter.typeError( + 6896_error, + _functionCall.arguments()[0]->location(), + errorMsg + ); + } + } + typeCheckFunctionGeneralChecks(_functionCall, _functionType); +} + void TypeChecker::typeCheckFunctionGeneralChecks( FunctionCall const& _functionCall, FunctionTypePointer _functionType @@ -2825,6 +2853,10 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) returnTypes = functionType->returnParameterTypes(); break; } + case FunctionType::Kind::ERC7201: + typeCheckERC7201Builtin(_functionCall, functionType); + returnTypes = functionType->returnParameterTypes(); + break; default: { typeCheckFunctionCall(_functionCall, functionType); diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index e4bc127053..de446b7d14 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -117,6 +117,8 @@ class TypeChecker: private ASTConstVisitor FunctionType const* _functionType ); + void typeCheckERC7201Builtin(FunctionCall const& _functionCall, FunctionType const* _functionType); + bool visit(ImportDirective const&) override; void endVisit(InheritanceSpecifier const& _inheritance) override; diff --git a/libsolidity/ast/ASTUtils.cpp b/libsolidity/ast/ASTUtils.cpp index 5ae3366194..532b0bc7af 100644 --- a/libsolidity/ast/ASTUtils.cpp +++ b/libsolidity/ast/ASTUtils.cpp @@ -16,6 +16,7 @@ */ // SPDX-License-Identifier: GPL-3.0 +#include #include #include #include @@ -137,4 +138,22 @@ u256 layoutBaseForInheritanceHierarchy(ContractDefinition const& _topLevelContra return 0; } +std::optional erc7201CompileTimeValue(FunctionCall const& _erc7201Call) +{ + auto const* erc7201Function = dynamic_cast(ASTNode::referencedDeclaration(_erc7201Call.expression())); + solAssert(erc7201Function); + solAssert(erc7201Function->functionType(true /* internal */)->kind() == FunctionType::Kind::ERC7201); + + auto evaluatedResult = ConstantEvaluator::tryEvaluate(_erc7201Call); + + if (std::holds_alternative(evaluatedResult.value)) + return std::nullopt; + + solAssert(std::holds_alternative(evaluatedResult.value)); + auto rationalValue = std::get(evaluatedResult.value); + solAssert(rationalValue.denominator() == 1); + bigint baseSlot = rationalValue.numerator(); + solAssert(baseSlot <= std::numeric_limits::max()); + return u256(baseSlot); +} } diff --git a/libsolidity/ast/ASTUtils.h b/libsolidity/ast/ASTUtils.h index d29bda35ff..cb54f5868c 100644 --- a/libsolidity/ast/ASTUtils.h +++ b/libsolidity/ast/ASTUtils.h @@ -24,11 +24,12 @@ namespace solidity::frontend { class ASTNode; +class ContractDefinition; class Declaration; class Expression; +class FunctionCall; class SourceUnit; class VariableDeclaration; -class ContractDefinition; /// Find the topmost referenced constant variable declaration when the given variable /// declaration value is an identifier. Works only for constant variable declarations. @@ -61,4 +62,8 @@ bigint contractStorageSizeUpperBound(ContractDefinition const& _contract, Variab /// Assumes analysis was successful. u256 layoutBaseForInheritanceHierarchy(ContractDefinition const& _topLevelContract, DataLocation _location); +/// @returns The storage namespace base address using the ERC-7201 formula, if it can be calculated at compile time. +/// Assumes FunctionCall refers to erc7201 builtin. +std::optional erc7201CompileTimeValue(FunctionCall const& _erc7201Call); + } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 8454fd1306..601788eaf6 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3125,6 +3125,7 @@ std::string FunctionType::richIdentifier() const case Kind::ABIDecode: id += "abidecode"; break; case Kind::BlobHash: id += "blobhash"; break; case Kind::MetaType: id += "metatype"; break; + case Kind::ERC7201: id += "erc7201"; break; } id += "_" + stateMutabilityToString(m_stateMutability); id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes); @@ -3715,7 +3716,8 @@ bool FunctionType::isPure() const m_kind == Kind::Wrap || m_kind == Kind::Unwrap || m_kind == Kind::BytesConcat || - m_kind == Kind::StringConcat; + m_kind == Kind::StringConcat || + m_kind == Kind::ERC7201; } TypePointers FunctionType::parseElementaryTypeVector(strings const& _types) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 8d2453d8d5..cddedc0f8c 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -1267,6 +1267,7 @@ class FunctionType: public Type Send, ///< CALL, but without data and gas Transfer, ///< CALL, but without data and throws on error KECCAK256, ///< KECCAK256 + ERC7201, ///< erc7201(...) Selfdestruct, ///< SELFDESTRUCT Revert, ///< REVERT ECRecover, ///< CALL to special contract for ecrecover diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 49313c8925..e7f8df859b 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -32,6 +32,8 @@ #include #include +#include + #include #include #include @@ -926,6 +928,49 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } break; } + case FunctionType::Kind::ERC7201: + { + solAssert(arguments.size() == 1); + Type const* argType = arguments.front()->annotation().type; + solAssert(argType); + arguments.front()->accept(*this); + if (dynamic_cast(argType)) + { + std::optional slot = erc7201CompileTimeValue(_functionCall); + solAssert(slot.has_value()); + m_context << *slot; + } + // `bytes memory` does not conform to ERC7201 spec, so analysis rejects it. + // Internally string and bytes have the same representation though, so generating code for it is still possible. + else if (*argType == *TypeProvider::stringMemory() || *argType == *TypeProvider::bytesMemory()) + { + // stack layout: string_mem + ArrayUtils(m_context).retrieveLength(*TypeProvider::bytesMemory()); + // stack layout: string_mem length + m_context << Instruction::SWAP1; + // stack layout: length string_mem + // adjust to start of string data, first slot is size + m_context << u256(32) << Instruction::ADD; + // stack layout: length string_data_ptr + m_context.callYulFunction(m_context.utilFunctions().erc7201(), 2, 1); + } + else + { + solAssert(!argType->dataStoredIn(DataLocation::Memory)); + solAssert(argType->isImplicitlyConvertibleTo(*TypeProvider::stringMemory())); + + // stack layout: string_ref + utils().fetchFreeMemoryPointer(); + // stack layout: string_ref mem_start_ptr + utils().packedEncode({argType}, TypePointers()); + // stack layout: mem_end_ptr + utils().toSizeAfterFreeMemoryPointer(); + // stack layout: length mem_ptr_start + + m_context.callYulFunction(m_context.utilFunctions().erc7201(), 2, 1); + } + break; + } case FunctionType::Kind::Event: { _functionCall.expression().accept(*this); diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index bb18217f2f..f014207951 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1297,6 +1297,23 @@ std::string YulUtilFunctions::wrappingIntExpFunction( }); } +std::string YulUtilFunctions::erc7201() +{ + std::string functionName = "erc7201"; + return m_functionCollector.createFunction(functionName, [&]() { + Whiskers templ(R"( + function erc7201(namespaceIDDataPtr, namespaceIDLength) -> slot { + let innerKeccak := keccak256(namespaceIDDataPtr, namespaceIDLength) + mstore(0, sub(innerKeccak, 1)) + // slot = keccak256(keccak256(id) - 1) & ~0xff + slot := and(keccak256(0, 32), not(0xff)) + } + )"); + + return templ.render(); + }); +} + std::string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type) { std::string functionName = "array_length_" + _type.identifier(); diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index f4172017b4..a5e5479355 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -566,6 +566,14 @@ class YulUtilFunctions /// @return the name of a function that checks if two external functions pointers are equal or not std::string externalFunctionPointersEqualFunction(); + /// Generates a function that calculates storage namespace base address using the ERC-7201 formula. + /// The function expects an address pointing to the data of a byte array stored in memory + /// and the size of the data as its arguments. + /// The function uses scratch space. + /// @return The name of the function. + /// Signature: (namespaceIDDataPtr, namespaceIDLength) -> slot + std::string erc7201(); + private: /// @returns the name of a function that copies a struct from calldata or memory to storage /// signature: (slot, value) -> diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 66736da5f4..d5cb27b6e1 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include @@ -1424,6 +1425,36 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) } break; } + case FunctionType::Kind::ERC7201: + { + solAssert(arguments.size() == 1); + Type const* argType = arguments.front()->annotation().type; + solAssert(argType); + if (dynamic_cast(argType)) + { + std::optional slot = erc7201CompileTimeValue(_functionCall); + solAssert(slot.has_value()); + define(_functionCall) << formatNumber(*slot) << "\n"; + } + else + { + Whiskers templ(R"( + ((), ()) + )"); + + IRVariable stringArg = convert(*arguments[0], *TypeProvider::stringMemory()); + solAssert(stringArg.stackSlots().size() == 1); + std::string namespaceID = stringArg.stackSlots().front(); + + templ("arrayDataArea", m_utils.arrayDataAreaFunction(*TypeProvider::stringMemory())); + templ("arrayLength", m_utils.arrayLengthFunction(*TypeProvider::stringMemory())); + templ("namespaceID", namespaceID); + templ("erc7201Builtin", m_utils.erc7201()); + + define(_functionCall) << templ.render(); + } + break; + } case FunctionType::Kind::ArrayPop: { solAssert(functionType->hasBoundFirstArgument()); diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 6cf6cea932..df902fd862 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -3122,8 +3122,11 @@ RationalNumberType const* SMTEncoder::isConstant(Expression const& _expr) if (auto type = dynamic_cast(_expr.annotation().type)) return type; - if (auto typedRational = ConstantEvaluator::tryEvaluate(_expr)) - return TypeProvider::rationalNumber(typedRational->value); + if ( + auto typedValue = ConstantEvaluator::tryEvaluate(_expr); + std::holds_alternative(typedValue.value) + ) + return TypeProvider::rationalNumber(std::get(typedValue.value)); return nullptr; } From fcd94ee1d78a62c83f456e9335d79c7cc442d25e Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Mon, 2 Mar 2026 23:21:11 -0300 Subject: [PATCH 233/243] Update docs --- docs/cheatsheet.rst | 4 +++- docs/contracts/custom-storage-layout.rst | 3 ++- docs/units-and-global-variables.rst | 8 +++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index 1a3085f129..3a064fdf30 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -97,7 +97,7 @@ Validations and Assertions - ``revert()``: abort execution and revert state changes - ``revert(string memory message)``: abort execution and revert state changes providing an explanatory string -.. index:: cryptography, keccak256, sha256, ripemd160, ecrecover, addmod, mulmod +.. index:: cryptography, keccak256, sha256, ripemd160, ecrecover, addmod, mulmod, erc7201 Mathematical and Cryptographic Functions ======================================== @@ -111,6 +111,8 @@ Mathematical and Cryptographic Functions arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. - ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. +- ``erc7201(string memory id) returns (uint)``: compute the base slot of an ``erc7201`` storage namespace. + Can be used in compile time context. .. index:: this, super, selfdestruct diff --git a/docs/contracts/custom-storage-layout.rst b/docs/contracts/custom-storage-layout.rst index 72f281d7f9..def06f3d5a 100644 --- a/docs/contracts/custom-storage-layout.rst +++ b/docs/contracts/custom-storage-layout.rst @@ -25,7 +25,8 @@ and is located in the header of a contract definition. The layout specifier can be placed either before or after the inheritance specifier, and can appear at most once. The ``base-slot-expression`` must be an :ref:`integer literal` expression that can be evaluated at compilation time and yields a value in the range of ``uint256``. -The use of constants initialized using such expressions is also allowed. +The use of constants initialized using such expressions and +the :ref:`built-in function erc7201` is also allowed. A custom layout cannot make contract's storage "wrap around". If the selected base slot would push the static variables past the end of storage, diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 4d8fed283f..d46bdaddf6 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -193,7 +193,7 @@ more details on error handling and when to use which function. ``revert(string memory reason)`` abort execution and revert state changes, providing an explanatory string -.. index:: keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, +.. index:: keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, erc7201, .. _mathematical-and-cryptographic-functions: @@ -246,6 +246,12 @@ Mathematical and Cryptographic Functions When running ``sha256``, ``ripemd160`` or ``ecrecover`` on a *private blockchain*, you might encounter Out-of-Gas. This is because these functions are implemented as "precompiled contracts" and only really exist after they receive the first message (although their contract code is hardcoded). Messages to non-existing contracts are more expensive and thus the execution might run into an Out-of-Gas error. A workaround for this problem is to first send Wei (1 for example) to each of the contracts before you use them in your actual contracts. This is not an issue on the main or test net. +``erc7201(string memory id) returns (uint)`` + compute the base slot of a storage namespace of a given ``id`` according to the ``erc7201`` formula defined by `ERC-7201`. + The formula is equivalent to ``keccak256(keccak256(id) - 1) & ~0xff``. + The builtin accepts arbitrary strings, including ones containing whitespace. + The function can be used in compile-time context. + .. index:: balance, codehash, send, transfer, call, callcode, delegatecall, staticcall .. _address_related: From 8c180694d1927e69cf069904c9124b4be8efe911 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Thu, 5 Mar 2026 22:47:00 +0100 Subject: [PATCH 234/243] Add experimental --via-ssa-cfg to CLI --- Changelog.md | 1 + docs/using-the-compiler.rst | 2 + solc/CommandLineInterface.cpp | 3 +- solc/CommandLineParser.cpp | 24 +++++++++++- solc/CommandLineParser.h | 1 + test/cmdlineTests/via_ssa_cfg_ethdebug/args | 1 + test/cmdlineTests/via_ssa_cfg_ethdebug/err | 1 + test/cmdlineTests/via_ssa_cfg_ethdebug/exit | 1 + .../via_ssa_cfg_ethdebug/input.sol | 4 ++ .../via_ssa_cfg_without_experimental/args | 1 + .../via_ssa_cfg_without_experimental/err | 1 + .../via_ssa_cfg_without_experimental/exit | 1 + .../input.sol | 6 +++ test/solc/CommandLineParser.cpp | 38 +++++++++++++++++-- 14 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 test/cmdlineTests/via_ssa_cfg_ethdebug/args create mode 100644 test/cmdlineTests/via_ssa_cfg_ethdebug/err create mode 100644 test/cmdlineTests/via_ssa_cfg_ethdebug/exit create mode 100644 test/cmdlineTests/via_ssa_cfg_ethdebug/input.sol create mode 100644 test/cmdlineTests/via_ssa_cfg_without_experimental/args create mode 100644 test/cmdlineTests/via_ssa_cfg_without_experimental/err create mode 100644 test/cmdlineTests/via_ssa_cfg_without_experimental/exit create mode 100644 test/cmdlineTests/via_ssa_cfg_without_experimental/input.sol diff --git a/Changelog.md b/Changelog.md index aa3e2951f0..bac62ba185 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Language Features: Compiler Features: * Commandline Interface: Introduce `--experimental` flag required for enabling the experimental mode. +* General: Introduce the SSA CFG codegen (experimental). * General: Restrict the existing experimental features (`generic-solidity`, `lsp`, `ethdebug`, `eof`, `evm`, `ast-import`, `evmasm-import`, `ir-ast`, `ssa-cfg`) to experimental mode. * Metadata: Store the state of the experimental mode in JSON and CBOR metadata. In CBOR this broadens the meaning of the existing `experimental` field, which used to indicate only the presence of certain experimental pragmas in the source. * Standard JSON Interface: Introduce `settings.experimental` setting required for enabling the experimental mode. diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index ce82aa0778..485b705485 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -768,3 +768,5 @@ The table below details all currently available experimental features. +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ | Yul SSA CFG exporter | ``ssa-cfg`` | no | ``--yul-cfg-json`` | +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ +| SSA CFG codegen | ``ssa-cfg-codegen`` | yes | ``--via-ssa-cfg``, ``settings.viaSSACFG`` | ++-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 90c67868d5..a40853e49d 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -937,6 +937,7 @@ void CommandLineInterface::compile() m_compiler->setRemappings(m_options.input.remappings); m_compiler->setLibraries(m_options.linker.libraries); m_compiler->setViaIR(m_options.output.viaIR); + m_compiler->setViaSSACFG(m_options.output.viaSSACFG); m_compiler->setEVMVersion(m_options.output.evmVersion); m_compiler->setEOFVersion(m_options.output.eofVersion); m_compiler->setRevertStringBehaviour(m_options.output.revertStrings); @@ -1322,7 +1323,7 @@ void CommandLineInterface::assembleYul(yul::YulStack::Language _language, yul::Y stack.optimize(); - yul::MachineAssemblyObject object = stack.assemble(_targetMachine); + yul::MachineAssemblyObject object = stack.assemble(_targetMachine, m_options.output.viaSSACFG); if (object.bytecode) object.bytecode->link(m_options.linker.libraries); objects.insert({sourceUnitName, std::move(object)}); diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 7f143695d5..2d4cc28df7 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -49,6 +49,7 @@ static std::string const g_strEVM = "evm"; static std::string const g_strEVMVersion = "evm-version"; static std::string const g_strEOFVersion = "experimental-eof-version"; static std::string const g_strViaIR = "via-ir"; +static std::string const g_strViaSSACFG = "via-ssa-cfg"; static std::string const g_strExperimentalViaIR = "experimental-via-ir"; static std::string const g_strExperimental = "experimental"; static std::string const g_strGas = "gas"; @@ -625,6 +626,10 @@ General Information)").c_str(), g_strViaIR.c_str(), "Turn on compilation mode via the IR." ) + ( + g_strViaSSACFG.c_str(), + "(experimental) Turn on SSA CFG-based code generation. Implies compilation via IR." + ) ( g_strRevertStrings.c_str(), po::value()->value_name(util::joinHumanReadable(g_revertStringsArgs, ",")), @@ -999,6 +1004,7 @@ void CommandLineParser::processArgs() "ethdebug", "ethdebug-runtime", g_strEOFVersion, + g_strViaSSACFG, }); if (m_args.count(g_strHelp) > 0) @@ -1063,7 +1069,8 @@ void CommandLineParser::processArgs() {g_strModelCheckerTimeout, {InputMode::Compiler, InputMode::CompilerWithASTImport}}, {g_strModelCheckerBMCLoopIterations, {InputMode::Compiler, InputMode::CompilerWithASTImport}}, {g_strModelCheckerContracts, {InputMode::Compiler, InputMode::CompilerWithASTImport}}, - {g_strModelCheckerTargets, {InputMode::Compiler, InputMode::CompilerWithASTImport}} + {g_strModelCheckerTargets, {InputMode::Compiler, InputMode::CompilerWithASTImport}}, + {g_strViaSSACFG, {InputMode::Compiler, InputMode::CompilerWithASTImport, InputMode::Assembler}} }; std::vector invalidOptionsForCurrentInputMode; for (auto const& [optionName, inputModes]: validOptionInputModeCombinations) @@ -1372,8 +1379,17 @@ void CommandLineParser::processArgs() "Optimizer can only be used for strict assembly. Use --" + g_strStrictAssembly + "." ); + m_options.output.viaSSACFG = m_args.contains(g_strViaSSACFG); + if (m_options.output.viaSSACFG) + if (m_options.assembly.inputLanguage != Input::StrictAssembly) + solThrow( + CommandLineValidationError, + "--" + g_strViaSSACFG + " can only be used with strict assembly. Use --" + g_strStrictAssembly + "." + ); if (m_options.compiler.outputs.ethdebug || m_options.compiler.outputs.ethdebugRuntime) { + if (m_options.output.viaSSACFG) + solUnimplemented("ethdebug is not yet supported with --" + g_strViaSSACFG + "."); if (m_options.optimiserSettings().runYulOptimiser) solUnimplemented( "Optimization (using --" + g_strOptimize + ") is not yet supported with ethdebug." @@ -1514,6 +1530,9 @@ void CommandLineParser::processArgs() m_args.count(g_strModelCheckerTargets) || m_args.count(g_strModelCheckerTimeout); m_options.output.viaIR = (m_args.count(g_strExperimentalViaIR) > 0 || m_args.count(g_strViaIR) > 0); + m_options.output.viaSSACFG = m_args.contains(g_strViaSSACFG); + if (m_options.output.viaSSACFG) + m_options.output.viaIR = true; solAssert( m_options.input.mode == InputMode::Compiler || @@ -1540,6 +1559,9 @@ void CommandLineParser::processArgs() enableEthdebugMessage + " output can only be selected, if --via-ir was specified." ); + if (m_options.output.viaSSACFG) + solUnimplemented("ethdebug is not yet supported with --" + g_strViaSSACFG + "."); + if (incompatibleEthdebugOutputs) solThrow( CommandLineValidationError, diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index 9d9dc28b6a..5f5527c619 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -200,6 +200,7 @@ struct CommandLineOptions bool overwriteFiles = false; langutil::EVMVersion evmVersion; bool viaIR = false; + bool viaSSACFG = false; RevertStrings revertStrings = RevertStrings::Default; std::optional debugInfoSelection; CompilerStack::State stopAfter = CompilerStack::State::CompilationSuccessful; diff --git a/test/cmdlineTests/via_ssa_cfg_ethdebug/args b/test/cmdlineTests/via_ssa_cfg_ethdebug/args new file mode 100644 index 0000000000..fa8be093c0 --- /dev/null +++ b/test/cmdlineTests/via_ssa_cfg_ethdebug/args @@ -0,0 +1 @@ +--experimental --via-ssa-cfg --ethdebug diff --git a/test/cmdlineTests/via_ssa_cfg_ethdebug/err b/test/cmdlineTests/via_ssa_cfg_ethdebug/err new file mode 100644 index 0000000000..170609e36f --- /dev/null +++ b/test/cmdlineTests/via_ssa_cfg_ethdebug/err @@ -0,0 +1 @@ +Error: ethdebug is not yet supported with --via-ssa-cfg. diff --git a/test/cmdlineTests/via_ssa_cfg_ethdebug/exit b/test/cmdlineTests/via_ssa_cfg_ethdebug/exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/test/cmdlineTests/via_ssa_cfg_ethdebug/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/via_ssa_cfg_ethdebug/input.sol b/test/cmdlineTests/via_ssa_cfg_ethdebug/input.sol new file mode 100644 index 0000000000..f123f6c8be --- /dev/null +++ b/test/cmdlineTests/via_ssa_cfg_ethdebug/input.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C {} diff --git a/test/cmdlineTests/via_ssa_cfg_without_experimental/args b/test/cmdlineTests/via_ssa_cfg_without_experimental/args new file mode 100644 index 0000000000..fe15a47cb3 --- /dev/null +++ b/test/cmdlineTests/via_ssa_cfg_without_experimental/args @@ -0,0 +1 @@ +--via-ssa-cfg diff --git a/test/cmdlineTests/via_ssa_cfg_without_experimental/err b/test/cmdlineTests/via_ssa_cfg_without_experimental/err new file mode 100644 index 0000000000..0d92cf2651 --- /dev/null +++ b/test/cmdlineTests/via_ssa_cfg_without_experimental/err @@ -0,0 +1 @@ +Error: The following options are only available in experimental mode: --via-ssa-cfg. To enable experimental mode, use the --experimental flag. diff --git a/test/cmdlineTests/via_ssa_cfg_without_experimental/exit b/test/cmdlineTests/via_ssa_cfg_without_experimental/exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/test/cmdlineTests/via_ssa_cfg_without_experimental/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/via_ssa_cfg_without_experimental/input.sol b/test/cmdlineTests/via_ssa_cfg_without_experimental/input.sol new file mode 100644 index 0000000000..136e28fdf7 --- /dev/null +++ b/test/cmdlineTests/via_ssa_cfg_without_experimental/input.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C { + function f() public {} +} diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index 6d91533816..1e5896604a 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -155,7 +155,8 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) "--model-checker-show-unsupported", "--model-checker-solvers=z3,smtlib2", "--model-checker-targets=underflow,divByZero", - "--model-checker-timeout=5" + "--model-checker-timeout=5", + "--via-ssa-cfg" }; if (inputMode == InputMode::CompilerWithASTImport) @@ -183,6 +184,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) expectedOptions.output.overwriteFiles = true; expectedOptions.output.evmVersion = EVMVersion::spuriousDragon(); expectedOptions.output.viaIR = true; + expectedOptions.output.viaSSACFG = true; expectedOptions.output.revertStrings = RevertStrings::Strip; expectedOptions.output.debugInfoSelection = DebugInfoSelection::fromString("location"); expectedOptions.formatting.json = JsonFormat{JsonFormat::Pretty, 7}; @@ -318,9 +320,11 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) commandLine += assemblyOptions; if (expectedLanguage == YulStack::Language::StrictAssembly) commandLine += std::vector{ + "--experimental", "--optimize", "--optimize-runs=1000", "--yul-optimizations=agf", + "--via-ssa-cfg", }; CommandLineOptions expectedOptions; @@ -357,10 +361,12 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) expectedOptions.compiler.outputs.astCompactJson = true; if (expectedLanguage == YulStack::Language::StrictAssembly) { + expectedOptions.experimental = true; expectedOptions.optimizer.optimizeEvmasm = true; expectedOptions.optimizer.optimizeYul = true; expectedOptions.optimizer.yulSteps = "agf"; expectedOptions.optimizer.expectedExecutionsPerDeployment = 1000; + expectedOptions.output.viaSSACFG = true; } CommandLineOptions parsedOptions = parseCommandLine(commandLine); @@ -436,7 +442,8 @@ BOOST_AUTO_TEST_CASE(invalid_options_input_modes_combinations) {"--model-checker-solvers=z3,smtlib2", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, {"--model-checker-timeout=5", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, {"--model-checker-contracts=contract1.yul:A,contract2.yul:B", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, - {"--model-checker-targets=underflow,divByZero", {"--assemble", "--strict-assembly", "--standard-json", "--link"}} + {"--model-checker-targets=underflow,divByZero", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, + {"--via-ssa-cfg", {"--standard-json", "--link", "--import-asm-json"}} }; for (auto const& [optionName, inputModes]: invalidOptionInputModeCombinations) @@ -448,9 +455,24 @@ BOOST_AUTO_TEST_CASE(invalid_options_input_modes_combinations) soltestAssert(!optionNameWithoutValue.empty()); std::vector commandLine = {"solc", optionName, "file", inputMode}; + bool experimentalMode = false; + if (optionNameWithoutValue == "--via-ssa-cfg") + { + commandLine.push_back("--experimental"); + experimentalMode = true; + } std::string expectedMessage = "The following options are not supported in the current input mode: " + optionNameWithoutValue; - auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) { return _exception.what() == expectedMessage; }; + // When --experimental is combined with --standard-json, a different error fires first + // (standard JSON mode is incompatible with --experimental flag). Accept that too. + auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) + { + std::string const what = _exception.what(); + return what == expectedMessage || ( + experimentalMode && + what.starts_with("Standard JSON input mode is incompatible with the --experimental flag.") + ); + }; BOOST_CHECK_EXCEPTION(parseCommandLine(commandLine), CommandLineValidationError, hasCorrectMessage); } @@ -679,7 +701,8 @@ BOOST_AUTO_TEST_CASE(experimental_features_without_experimental_flag) "--ir-optimized-ast-json", "--yul-cfg-json", "--ethdebug", - "--ethdebug-runtime" + "--ethdebug-runtime", + "--via-ssa-cfg" }; std::string expectedErrorMessage; @@ -703,6 +726,13 @@ BOOST_AUTO_TEST_CASE(experimental_features_without_experimental_flag) BOOST_CHECK_EXCEPTION(parseCommandLine(commandLineOptions), CommandLineValidationError, hasCorrectMessage); } +BOOST_AUTO_TEST_CASE(via_ssa_cfg_smoke) +{ + auto const commandLineOptions = parseCommandLine({"solc", "--experimental", "--via-ssa-cfg", "contract.sol"}); + BOOST_CHECK_EQUAL(commandLineOptions.output.viaSSACFG, true); + BOOST_CHECK_EQUAL(commandLineOptions.output.viaIR, true); +} + BOOST_AUTO_TEST_CASE(debug_info_ethdebug_without_experimental_flag) { std::string const expectedErrorMessage = From 5d9ba67b2cf87db1b40737b5cfdece0014c97fc8 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Thu, 5 Mar 2026 22:47:21 +0100 Subject: [PATCH 235/243] Add experimental settings.viaSSACFG to Standard JSON Interface --- docs/using-the-compiler.rst | 9 ++++-- libsolidity/interface/StandardCompiler.cpp | 28 +++++++++++++++++-- libsolidity/interface/StandardCompiler.h | 1 + .../standard_via_ssa_cfg_ethdebug/input.json | 15 ++++++++++ .../standard_via_ssa_cfg_ethdebug/output.json | 11 ++++++++ .../input.json | 11 ++++++++ .../output.json | 11 ++++++++ .../input.json | 13 +++++++++ .../output.json | 11 ++++++++ test/libsolidity/StandardCompiler.cpp | 26 +++++++++++++++++ 10 files changed, 131 insertions(+), 5 deletions(-) create mode 100644 test/cmdlineTests/standard_via_ssa_cfg_ethdebug/input.json create mode 100644 test/cmdlineTests/standard_via_ssa_cfg_ethdebug/output.json create mode 100644 test/cmdlineTests/standard_via_ssa_cfg_no_experimental/input.json create mode 100644 test/cmdlineTests/standard_via_ssa_cfg_no_experimental/output.json create mode 100644 test/cmdlineTests/standard_via_ssa_cfg_via_ir_false/input.json create mode 100644 test/cmdlineTests/standard_via_ssa_cfg_via_ir_false/output.json diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 485b705485..604f8b05ad 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -369,6 +369,9 @@ Input Description // Optional: Change compilation pipeline to go through the Yul intermediate representation. // This is false by default. "viaIR": true, + // Optional: Turn on SSA CFG-based code generation via the IR (experimental). + // Implies viaIR: true. This is false by default. + "viaSSACFG": false, // Optional: Debugging settings "debug": { // How to treat revert (and require) reason strings. Settings are @@ -766,7 +769,7 @@ The table below details all currently available experimental features. +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ | Ethdebug | ``ethdebug`` | no | ``--ethdebug``, ``--ethdebug-runtime``, ``--debug-info ethdebug`` | +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ -| Yul SSA CFG exporter | ``ssa-cfg`` | no | ``--yul-cfg-json`` | -+-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ -| SSA CFG codegen | ``ssa-cfg-codegen`` | yes | ``--via-ssa-cfg``, ``settings.viaSSACFG`` | +| | | no | ``--yul-cfg-json`` | +| SSA CFG + ``ssa-cfg`` +------------------+-------------------------------------------------------------------+ +| | | yes | ``--via-ssa-cfg`` | +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 74307bbc05..a556c74db1 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -450,7 +450,7 @@ std::optional checkAuxiliaryInputKeys(Json const& _input) std::optional checkSettingsKeys(Json const& _input) { - static std::set keys{"debug", "evmVersion", "experimental", "eofVersion", "libraries", "metadata", "modelChecker", "optimizer", "outputSelection", "remappings", "stopAfter", "viaIR"}; + static std::set keys{"debug", "evmVersion", "experimental", "eofVersion", "libraries", "metadata", "modelChecker", "optimizer", "outputSelection", "remappings", "stopAfter", "viaIR", "viaSSACFG"}; return checkKeys(_input, keys, "settings"); } @@ -848,6 +848,22 @@ std::variant StandardCompiler::parseI ret.viaIR = settings["viaIR"].get(); } + if (settings.contains("viaSSACFG")) + { + if (!settings["viaSSACFG"].is_boolean()) + return formatFatalError(Error::Type::JSONError, "\"settings.viaSSACFG\" must be a Boolean."); + ret.viaSSACFG = settings["viaSSACFG"].get(); + if (ret.viaSSACFG) + { + if (settings.contains("viaIR") && !ret.viaIR) + return formatFatalError( + Error::Type::JSONError, + "\"settings.viaSSACFG\" requires compilation via IR." + ); + ret.viaIR = true; + } + } + if (settings.contains("evmVersion")) { if (!settings["evmVersion"].is_string()) @@ -1239,8 +1255,12 @@ std::variant StandardCompiler::parseI } if (isEthdebugRequested(ret.outputSelection)) + { if (ret.optimiserSettings.runYulOptimiser) solUnimplemented("Optimization is not yet supported with ethdebug."); + if (ret.viaSSACFG) + solUnimplemented("SSA CFG codegen does not yet support ethdebug."); + } if (!ret.experimental) { @@ -1256,6 +1276,9 @@ std::variant StandardCompiler::parseI if (ret.eofVersion.has_value()) return formatFatalError(Error::Type::FatalError, "'eofVersion' setting is experimental and can only be used with the 'settings.experimental' option enabled."); + + if (ret.viaSSACFG) + return formatFatalError(Error::Type::FatalError, "'viaSSACFG' setting is experimental and can only be used with the 'settings.experimental' option enabled."); } return {std::move(ret)}; @@ -1406,6 +1429,7 @@ Json StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inpu for (auto const& smtLib2Response: _inputsAndSettings.smtLib2Responses) compilerStack.addSMTLib2Response(smtLib2Response.first, smtLib2Response.second); compilerStack.setViaIR(_inputsAndSettings.viaIR); + compilerStack.setViaSSACFG(_inputsAndSettings.viaSSACFG); compilerStack.setEVMVersion(_inputsAndSettings.evmVersion); compilerStack.setEOFVersion(_inputsAndSettings.eofVersion); compilerStack.setRemappings(std::move(_inputsAndSettings.remappings)); @@ -1763,7 +1787,7 @@ Json StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) output["sources"][sourceName] = sourceResult; } stack.optimize(); - std::tie(object, deployedObject) = stack.assembleWithDeployed(); + std::tie(object, deployedObject) = stack.assembleWithDeployed({}, _inputsAndSettings.viaSSACFG); if (object.bytecode) object.bytecode->link(_inputsAndSettings.libraries); if (deployedObject.bytecode) diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h index 493e637cd6..0d184e139e 100644 --- a/libsolidity/interface/StandardCompiler.h +++ b/libsolidity/interface/StandardCompiler.h @@ -89,6 +89,7 @@ class StandardCompiler Json outputSelection; ModelCheckerSettings modelCheckerSettings = ModelCheckerSettings{}; bool viaIR = false; + bool viaSSACFG = false; bool experimental = false; }; diff --git a/test/cmdlineTests/standard_via_ssa_cfg_ethdebug/input.json b/test/cmdlineTests/standard_via_ssa_cfg_ethdebug/input.json new file mode 100644 index 0000000000..d5ad9153e9 --- /dev/null +++ b/test/cmdlineTests/standard_via_ssa_cfg_ethdebug/input.json @@ -0,0 +1,15 @@ +{ + "language": "Solidity", + "sources": { + "A.sol": { + "content": "contract A {}" + } + }, + "settings": { + "experimental": true, + "viaSSACFG": true, + "outputSelection": { + "*": {"*": ["evm.bytecode.ethdebug"]} + } + } +} diff --git a/test/cmdlineTests/standard_via_ssa_cfg_ethdebug/output.json b/test/cmdlineTests/standard_via_ssa_cfg_ethdebug/output.json new file mode 100644 index 0000000000..7e4cdbc8dc --- /dev/null +++ b/test/cmdlineTests/standard_via_ssa_cfg_ethdebug/output.json @@ -0,0 +1,11 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "SSA CFG codegen does not yet support ethdebug.", + "message": "SSA CFG codegen does not yet support ethdebug.", + "severity": "error", + "type": "UnimplementedFeatureError" + } + ] +} diff --git a/test/cmdlineTests/standard_via_ssa_cfg_no_experimental/input.json b/test/cmdlineTests/standard_via_ssa_cfg_no_experimental/input.json new file mode 100644 index 0000000000..4515a2489c --- /dev/null +++ b/test/cmdlineTests/standard_via_ssa_cfg_no_experimental/input.json @@ -0,0 +1,11 @@ +{ + "language": "Solidity", + "sources": { + "A.sol": { + "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" + } + }, + "settings": { + "viaSSACFG": true + } +} diff --git a/test/cmdlineTests/standard_via_ssa_cfg_no_experimental/output.json b/test/cmdlineTests/standard_via_ssa_cfg_no_experimental/output.json new file mode 100644 index 0000000000..16de05ce37 --- /dev/null +++ b/test/cmdlineTests/standard_via_ssa_cfg_no_experimental/output.json @@ -0,0 +1,11 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "'viaSSACFG' setting is experimental and can only be used with the 'settings.experimental' option enabled.", + "message": "'viaSSACFG' setting is experimental and can only be used with the 'settings.experimental' option enabled.", + "severity": "error", + "type": "FatalError" + } + ] +} diff --git a/test/cmdlineTests/standard_via_ssa_cfg_via_ir_false/input.json b/test/cmdlineTests/standard_via_ssa_cfg_via_ir_false/input.json new file mode 100644 index 0000000000..2428834164 --- /dev/null +++ b/test/cmdlineTests/standard_via_ssa_cfg_via_ir_false/input.json @@ -0,0 +1,13 @@ +{ + "language": "Solidity", + "sources": { + "A.sol": { + "content": "contract A {}" + } + }, + "settings": { + "experimental": true, + "viaIR": false, + "viaSSACFG": true + } +} diff --git a/test/cmdlineTests/standard_via_ssa_cfg_via_ir_false/output.json b/test/cmdlineTests/standard_via_ssa_cfg_via_ir_false/output.json new file mode 100644 index 0000000000..938367890f --- /dev/null +++ b/test/cmdlineTests/standard_via_ssa_cfg_via_ir_false/output.json @@ -0,0 +1,11 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "\"settings.viaSSACFG\" requires compilation via IR.", + "message": "\"settings.viaSSACFG\" requires compilation via IR.", + "severity": "error", + "type": "JSONError" + } + ] +} diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 43b68223be..18c366e3e8 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -2347,6 +2347,32 @@ BOOST_AUTO_TEST_CASE(experimental_non_boolean) BOOST_CHECK(containsError(result, "JSONError", "'settings.experimental' must be a Boolean.")); } +BOOST_AUTO_TEST_CASE(via_ssa_cfg_with_experimental) +{ + frontend::StandardCompiler compiler; + static auto constexpr input = R"( + { + "language": "Solidity", + "sources": { + "A.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0\npragma solidity >=0.0;\ncontract A { function f() public pure returns (uint) { return 1; } }" + } + }, + "settings": { + "experimental": true, + "viaSSACFG": true, + "outputSelection": {"*": {"*": ["evm.bytecode"]}} + } + } + )"; + + Json parsedInput; + BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput)); + Json result = compiler.compile(parsedInput); + // Should compile without fatal errors (warnings are acceptable) + BOOST_CHECK(!containsError(result, "FatalError", "")); +} + BOOST_AUTO_TEST_SUITE_END() } // end namespaces From 92673f638630ca8071bdae970489c34c120397df Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Mon, 9 Mar 2026 09:29:59 +0100 Subject: [PATCH 236/243] Add viaSSACFG to compilation metadata --- libsolidity/interface/CompilerStack.cpp | 2 + test/cmdlineTests/metadata_via_ssa_cfg/args | 1 + .../metadata_via_ssa_cfg/input.sol | 4 ++ test/cmdlineTests/metadata_via_ssa_cfg/output | 4 ++ test/libsolidity/Metadata.cpp | 45 +++++++++++++++++++ 5 files changed, 56 insertions(+) create mode 100644 test/cmdlineTests/metadata_via_ssa_cfg/args create mode 100644 test/cmdlineTests/metadata_via_ssa_cfg/input.sol create mode 100644 test/cmdlineTests/metadata_via_ssa_cfg/output diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 96c0d3f6c2..152a0059ad 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -1842,6 +1842,8 @@ std::string CompilerStack::createMetadata(Contract const& _contract, bool _forIR meta["settings"]["experimental"] = m_experimental; if (m_eofVersion.has_value()) meta["settings"]["eofVersion"] = *m_eofVersion; + if (m_viaSSACFG) + meta["settings"]["viaSSACFG"] = m_viaSSACFG; meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] = *_contract.contract->annotation().canonicalName; diff --git a/test/cmdlineTests/metadata_via_ssa_cfg/args b/test/cmdlineTests/metadata_via_ssa_cfg/args new file mode 100644 index 0000000000..565e519225 --- /dev/null +++ b/test/cmdlineTests/metadata_via_ssa_cfg/args @@ -0,0 +1 @@ +--metadata --experimental --via-ssa-cfg diff --git a/test/cmdlineTests/metadata_via_ssa_cfg/input.sol b/test/cmdlineTests/metadata_via_ssa_cfg/input.sol new file mode 100644 index 0000000000..a3a86cc8d3 --- /dev/null +++ b/test/cmdlineTests/metadata_via_ssa_cfg/input.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity *; + +contract C {} diff --git a/test/cmdlineTests/metadata_via_ssa_cfg/output b/test/cmdlineTests/metadata_via_ssa_cfg/output new file mode 100644 index 0000000000..5752237261 --- /dev/null +++ b/test/cmdlineTests/metadata_via_ssa_cfg/output @@ -0,0 +1,4 @@ + +======= input.sol:C ======= +Metadata: +{"compiler":{"version": ""},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"input.sol":"C"},"evmVersion":"osaka","experimental":true,"libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[],"viaIR":true,"viaSSACFG":true},"sources":{"input.sol":{"keccak256":"0x5cf617b1707a484e3c4bd59643013dec76ab7d75900b46855214729ae3e0ceb0","license":"GPL-3.0","urls":["bzz-raw://ac418a02dfadf87234150d3568f33269e3f49460345cb39300e017a6d755eff2","dweb:/ipfs/QmQq3owBu25x2WV46HB1WyKzJpxiAPecU7eMKqtXCF7eeS"]}},"version":1} diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index 683522693c..476fcd3a00 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -423,6 +423,51 @@ BOOST_AUTO_TEST_CASE(metadata_viair) check(sourceCode, false); } +BOOST_AUTO_TEST_CASE(metadata_viassacfg) +{ + static auto constexpr sourceCode = R"( + pragma solidity >=0.0; + contract test { + } + )"; + + auto check = [](char const* _src, bool _viaSSACFG) + { + CompilerStack compilerStack; + compilerStack.setSources({{"", _src}}); + compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); + compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize); + compilerStack.setViaIR(true); + compilerStack.setExperimental(true); + compilerStack.setViaSSACFG(_viaSSACFG); + BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); + + Json metadata; + BOOST_REQUIRE(util::jsonParseStrict(compilerStack.metadata("test"), metadata)); + BOOST_CHECK(solidity::test::isValidMetadata(metadata)); + BOOST_CHECK(metadata.contains("settings")); + BOOST_CHECK(metadata["settings"].contains("viaIR")); + BOOST_CHECK(metadata["settings"]["viaIR"].get()); + if (_viaSSACFG) + { + BOOST_CHECK(metadata["settings"].contains("viaSSACFG")); + BOOST_CHECK(metadata["settings"]["viaSSACFG"].get()); + } + else + BOOST_CHECK(!metadata["settings"].contains("viaSSACFG")); + + std::map const parsedCBORMetadata = requireParsedCBORMetadata( + compilerStack.runtimeObject("test").bytecode, + CompilerStack::MetadataFormat::WithReleaseVersionTag + ); + + BOOST_CHECK(parsedCBORMetadata.contains("experimental")); + }; + + check(sourceCode, true); + check(sourceCode, false); +} + BOOST_AUTO_TEST_CASE(metadata_revert_strings) { CompilerStack compilerStack; From 11beecc016ca5a22a0200f2cd6d8f723f441221b Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Tue, 10 Mar 2026 13:24:15 +0100 Subject: [PATCH 237/243] Update wording of --via-ir help message. --- solc/CommandLineParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 2d4cc28df7..c40840a075 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -624,7 +624,7 @@ General Information)").c_str(), ) ( g_strViaIR.c_str(), - "Turn on compilation mode via the IR." + "Turn on compilation via IR." ) ( g_strViaSSACFG.c_str(), From 781aeb19d5855e728988c1c1c03514428eb69771 Mon Sep 17 00:00:00 2001 From: blishko Date: Wed, 11 Mar 2026 14:12:45 +0100 Subject: [PATCH 238/243] SSA: Tighten condition in shuffler The condition did not match the comment above it. This is the part of the algorithm that attempts to fix a slot in the arguments section of the target stack. But if the current stack size matches exactly the target tail size, we don't have any slot in the arguments section at that moment. I believe the comment is correct and we should enter this code only if there is a slot in args section. The condition has been tightened accordingly. --- libyul/backends/evm/ssa/StackShuffler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libyul/backends/evm/ssa/StackShuffler.h b/libyul/backends/evm/ssa/StackShuffler.h index 5dddf5e5a6..59b2bec6b3 100644 --- a/libyul/backends/evm/ssa/StackShuffler.h +++ b/libyul/backends/evm/ssa/StackShuffler.h @@ -394,7 +394,7 @@ class StackShuffler return false; // if we have at least one slot in the args section, try to fix something there - if (!_stack.empty() && _stack.size() >= _state.target().tailSize) + if (_stack.size() > _state.target().tailSize) { StackOffset const stackTop{_stack.size() - 1}; // if the stack top isn't where it likes to be right now, try to put it somewhere more sensible From c18c5a4c839d53283fcf5fd8ee75f996a949fee7 Mon Sep 17 00:00:00 2001 From: Haowei Hsu Date: Thu, 12 Mar 2026 23:51:10 +0800 Subject: [PATCH 239/243] docs: move nav links to Jinja template for i18n (#16471) Move navigation link strings from constants.js to Jinja template using the `_()` function. This change enables the strings to be extracted by Sphinx's "Internationalization for Gettext" feature. --- docs/_static/js/constants.js | 12 ++++++------ docs/_templates/layout.html | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/docs/_static/js/constants.js b/docs/_static/js/constants.js index 67fa16cdb0..c88d259b02 100644 --- a/docs/_static/js/constants.js +++ b/docs/_static/js/constants.js @@ -26,12 +26,12 @@ const CONTRIBUTE_PATH = `/en/latest/contributing.html`; const ABOUT_PATH = `${SOLIDITY_HOME_URL}/about`; const FORUM_URL = "https://forum.soliditylang.org/"; const NAV_LINKS = [ - { name: "Blog", href: BLOG_URL }, - { name: "Documentation", href: DOCS_URL }, - { name: "Use cases", href: USE_CASES_PATH }, - { name: "Contribute", href: CONTRIBUTE_PATH }, - { name: "About", href: ABOUT_PATH }, - { name: "Forum", href: FORUM_URL }, + { name: window.NAV_LINKS.blog, href: BLOG_URL }, + { name: window.NAV_LINKS.documentation, href: DOCS_URL }, + { name: window.NAV_LINKS.use_cases, href: USE_CASES_PATH }, + { name: window.NAV_LINKS.contribute, href: CONTRIBUTE_PATH }, + { name: window.NAV_LINKS.about, href: ABOUT_PATH }, + { name: window.NAV_LINKS.forum, href: FORUM_URL }, ]; const MOBILE_MENU_TOGGLE_CLASS = "shift"; diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html index 498c0e6e9b..e4795757fa 100644 --- a/docs/_templates/layout.html +++ b/docs/_templates/layout.html @@ -1,5 +1,19 @@ {% extends "!layout.html" %} +{% block scripts %} + + {{ super() }} +{% endblock %} + {% block menu %} {{ super() }}
    From f72c159914ec3963773e59c5f88911793fd5b65d Mon Sep 17 00:00:00 2001 From: rodiazet Date: Fri, 13 Mar 2026 13:28:17 +0100 Subject: [PATCH 240/243] Pin @uniswap/v3-sdk Fix euler --- test/externalTests/euler.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/test/externalTests/euler.sh b/test/externalTests/euler.sh index 9ccfcd781c..eb547b41ab 100755 --- a/test/externalTests/euler.sh +++ b/test/externalTests/euler.sh @@ -71,6 +71,7 @@ function euler_test force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH" force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" force_hardhat_unlimited_contract_size "$config_file" + npm install @uniswap/v3-sdk@3.29.1 # TODO: Remove after https://github.com/Uniswap/sdks/issues/557 is resolved npm install replace_version_pragmas neutralize_packaged_contracts From fa16df60225d8cd749e0740129260e7ee240d6c8 Mon Sep 17 00:00:00 2001 From: rodiazet Date: Fri, 13 Mar 2026 14:19:01 +0100 Subject: [PATCH 241/243] Fix yield-liquidator --- test/externalTests/yield-liquidator.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/externalTests/yield-liquidator.sh b/test/externalTests/yield-liquidator.sh index 16a6287e53..df0c193b97 100755 --- a/test/externalTests/yield-liquidator.sh +++ b/test/externalTests/yield-liquidator.sh @@ -63,6 +63,23 @@ function yield_liquidator_test force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH" force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" "$config_var" force_hardhat_unlimited_contract_size "$config_file" "$config_var" + # TODO: Remove after https://github.com/Uniswap/sdks/issues/557 is resolved + # Pin broken @uniswap/* packages via overrides. The following versions were published with + # 'workspace:*' deps that npm cannot resolve (EUNSUPPORTEDPROTOCOL), causing a silent exit-1: + # @uniswap/v3-sdk@3.29.2 (via smart-order-router -> ^3.7.0) + # @uniswap/v4-sdk@1.29.2 (via smart-order-router -> router-sdk -> ^1.20.0) + # @uniswap/v2-sdk@4.19.2 (via smart-order-router -> router-sdk -> ^4.14.0) + # Cannot just run npm install with unbroken version because these are indirect dependencies. + node -e " + const fs = require('fs'); + const p = JSON.parse(fs.readFileSync('package.json')); + p.overrides = Object.assign(p.overrides || {}, { + '@uniswap/v3-sdk': '3.29.1', + '@uniswap/v4-sdk': '1.29.1', + '@uniswap/v2-sdk': '4.19.1', + }); + fs.writeFileSync('package.json', JSON.stringify(p, null, 2)); + " npm install # The contract below is not used in any test and it depends on ISwapRouter which does not exists From d50fcadce40de8a18b0e242c6f08146771635dfc Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Thu, 12 Mar 2026 14:30:36 +0100 Subject: [PATCH 242/243] Test invalid import asm json input mode combinations with other cli options --- test/solc/CommandLineParser.cpp | 46 ++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index 1e5896604a..2233d0df3e 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -31,6 +31,8 @@ #include #include +#include + #include #include #include @@ -429,20 +431,20 @@ BOOST_AUTO_TEST_CASE(invalid_options_input_modes_combinations) { std::map> invalidOptionInputModeCombinations = { // TODO: This should eventually contain all options. - {"--experimental-via-ir", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, - {"--via-ir", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, - {"--metadata-literal", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, - {"--metadata-hash=swarm", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, - {"--model-checker-show-proved-safe", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, - {"--model-checker-show-unproved", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, - {"--model-checker-show-unsupported", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, - {"--model-checker-div-mod-no-slacks", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, - {"--model-checker-engine=bmc", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, - {"--model-checker-invariants=contract,reentrancy", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, - {"--model-checker-solvers=z3,smtlib2", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, - {"--model-checker-timeout=5", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, - {"--model-checker-contracts=contract1.yul:A,contract2.yul:B", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, - {"--model-checker-targets=underflow,divByZero", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, + {"--experimental-via-ir", {"--assemble", "--strict-assembly", "--standard-json", "--link", "--import-asm-json"}}, + {"--via-ir", {"--assemble", "--strict-assembly", "--standard-json", "--link", "--import-asm-json"}}, + {"--metadata-literal", {"--assemble", "--strict-assembly", "--standard-json", "--link", "--import-asm-json"}}, + {"--metadata-hash=swarm", {"--assemble", "--strict-assembly", "--standard-json", "--link", "--import-asm-json"}}, + {"--model-checker-show-proved-safe", {"--assemble", "--strict-assembly", "--standard-json", "--link", "--import-asm-json"}}, + {"--model-checker-show-unproved", {"--assemble", "--strict-assembly", "--standard-json", "--link", "--import-asm-json"}}, + {"--model-checker-show-unsupported", {"--assemble", "--strict-assembly", "--standard-json", "--link", "--import-asm-json"}}, + {"--model-checker-div-mod-no-slacks", {"--assemble", "--strict-assembly", "--standard-json", "--link", "--import-asm-json"}}, + {"--model-checker-engine=bmc", {"--assemble", "--strict-assembly", "--standard-json", "--link", "--import-asm-json"}}, + {"--model-checker-invariants=contract,reentrancy", {"--assemble", "--strict-assembly", "--standard-json", "--link", "--import-asm-json"}}, + {"--model-checker-solvers=z3,smtlib2", {"--assemble", "--strict-assembly", "--standard-json", "--link", "--import-asm-json"}}, + {"--model-checker-timeout=5", {"--assemble", "--strict-assembly", "--standard-json", "--link", "--import-asm-json"}}, + {"--model-checker-contracts=contract1.yul:A,contract2.yul:B", {"--assemble", "--strict-assembly", "--standard-json", "--link", "--import-asm-json"}}, + {"--model-checker-targets=underflow,divByZero", {"--assemble", "--strict-assembly", "--standard-json", "--link", "--import-asm-json"}}, {"--via-ssa-cfg", {"--standard-json", "--link", "--import-asm-json"}} }; @@ -455,12 +457,14 @@ BOOST_AUTO_TEST_CASE(invalid_options_input_modes_combinations) soltestAssert(!optionNameWithoutValue.empty()); std::vector commandLine = {"solc", optionName, "file", inputMode}; - bool experimentalMode = false; - if (optionNameWithoutValue == "--via-ssa-cfg") - { - commandLine.push_back("--experimental"); - experimentalMode = true; - } + + static auto constexpr isExperimental = [](std::string_view const _cliFlag) -> bool { + auto const& experimentalOpts = CommandLineParser::experimentalOptionNames(); + return ranges::find(experimentalOpts, _cliFlag.substr(2)) != ranges::end(experimentalOpts); + }; + bool needsExperimentalMode = isExperimental(optionNameWithoutValue) || isExperimental(inputMode); + if (needsExperimentalMode) + commandLine.emplace_back("--experimental"); std::string expectedMessage = "The following options are not supported in the current input mode: " + optionNameWithoutValue; // When --experimental is combined with --standard-json, a different error fires first @@ -469,7 +473,7 @@ BOOST_AUTO_TEST_CASE(invalid_options_input_modes_combinations) { std::string const what = _exception.what(); return what == expectedMessage || ( - experimentalMode && + needsExperimentalMode && what.starts_with("Standard JSON input mode is incompatible with the --experimental flag.") ); }; From bb64ae44fe60837bf92d631948b13d3b6edb9a58 Mon Sep 17 00:00:00 2001 From: clonker <1685266+clonker@users.noreply.github.com> Date: Thu, 12 Mar 2026 14:47:34 +0100 Subject: [PATCH 243/243] cli parser: expose set of experimental cli options --- solc/CommandLineParser.cpp | 30 ++++++++++++++++++------------ solc/CommandLineParser.h | 3 +++ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index c40840a075..297a2f4976 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -159,6 +159,23 @@ void CommandLineParser::checkMutuallyExclusive(std::vector const& _ } } +std::vector const& CommandLineParser::experimentalOptionNames() +{ + static std::vector const names{ + g_strLSP, + g_strImportAst, + g_strImportEvmAssemblerJson, + "ir-ast-json", + "ir-optimized-ast-json", + "yul-cfg-json", + "ethdebug", + "ethdebug-runtime", + g_strEOFVersion, + g_strViaSSACFG, + }; + return names; +} + void CommandLineParser::checkExperimental(std::vector const& _optionNames) const { if (!m_args.contains(g_strExperimental) && countEnabledOptions(_optionNames) > 0) @@ -994,18 +1011,7 @@ void CommandLineParser::processArgs() g_strImportEvmAssemblerJson, }); - checkExperimental({ - g_strLSP, - g_strImportAst, - g_strImportEvmAssemblerJson, - "ir-ast-json", - "ir-optimized-ast-json", - "yul-cfg-json", - "ethdebug", - "ethdebug-runtime", - g_strEOFVersion, - g_strViaSSACFG, - }); + checkExperimental(experimentalOptionNames()); if (m_args.count(g_strHelp) > 0) m_options.input.mode = InputMode::Help; diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index 5f5527c619..bc13acf9cf 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -289,6 +289,9 @@ class CommandLineParser CommandLineOptions const& options() const { return m_options; } + /// yields CLI option names (without leading "--") that require --experimental. + static std::vector const& experimentalOptionNames(); + static void printHelp(std::ostream& _out) { _out << optionsDescription(); } private: