From f121e7e9dd34a65474d6f92fc9ce23d6fd0396d3 Mon Sep 17 00:00:00 2001 From: Markus Osterlund / robriks <80549215+robriks@users.noreply.github.com> Date: Wed, 17 May 2023 07:10:07 -0400 Subject: [PATCH 001/189] Update abi-spec.rst Rearranged the calldata encoding explanations for the example functions so they are more easy to understand since they now visually match the order the functions are declared: Foo.bar() Foo.baz() Foo.sam() Maybe I'm smolbrain but it was confusing to reference the functions when they are declared in a different order than they are explained. I kept imagining calldata encoding layouts for bar() even though the example I was looking at discussed baz() instead. Minor change but makes a big difference for less experienced calldataooors like me. --- docs/abi-spec.rst | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 41967dd46d6b..0d48b51dd706 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -252,7 +252,21 @@ Given the contract: } -Thus, for our ``Foo`` example if we wanted to call ``baz`` with the parameters ``69`` and +Thus, for our ``Foo`` example, if we wanted to call ``bar`` with the argument ``["abc", "def"]``, we would pass 68 bytes total, broken down into: + +- ``0xfce353f6``: the Method ID. This is derived from the signature ``bar(bytes3[2])``. +- ``0x6162630000000000000000000000000000000000000000000000000000000000``: the first part of the first + parameter, a ``bytes3`` value ``"abc"`` (left-aligned). +- ``0x6465660000000000000000000000000000000000000000000000000000000000``: the second part of the first + parameter, a ``bytes3`` value ``"def"`` (left-aligned). + +In total: + +.. code-block:: none + + 0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000 + +If we wanted to call ``baz`` with the parameters ``69`` and ``true``, we would pass 68 bytes total, which can be broken down into: - ``0xcdcd77c0``: the Method ID. This is derived as the first 4 bytes of the Keccak hash of @@ -271,20 +285,6 @@ In total: It returns a single ``bool``. If, for example, it were to return ``false``, its output would be the single byte array ``0x0000000000000000000000000000000000000000000000000000000000000000``, a single bool. -If we wanted to call ``bar`` with the argument ``["abc", "def"]``, we would pass 68 bytes total, broken down into: - -- ``0xfce353f6``: the Method ID. This is derived from the signature ``bar(bytes3[2])``. -- ``0x6162630000000000000000000000000000000000000000000000000000000000``: the first part of the first - parameter, a ``bytes3`` value ``"abc"`` (left-aligned). -- ``0x6465660000000000000000000000000000000000000000000000000000000000``: the second part of the first - parameter, a ``bytes3`` value ``"def"`` (left-aligned). - -In total: - -.. code-block:: none - - 0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000 - If we wanted to call ``sam`` with the arguments ``"dave"``, ``true`` and ``[1,2,3]``, we would pass 292 bytes total, broken down into: From a582684d328100da20491c592544a55eeaa562ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 25 Oct 2023 11:02:47 +0200 Subject: [PATCH 002/189] Set release date for 0.8.21 and update the bug list --- Changelog.md | 2 +- docs/bugs_by_version.json | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 279fbec6348f..8c74b49cf72c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,4 @@ -### 0.8.22 (unreleased) +### 0.8.22 (2023-10-25) Language Features: * Allow defining events at file level. diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index d15c72a65424..982444b10330 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1863,6 +1863,10 @@ "bugs": [], "released": "2023-07-19" }, + "0.8.22": { + "bugs": [], + "released": "2023-10-25" + }, "0.8.3": { "bugs": [ "FullInlinerNonExpressionSplitArgumentEvaluationOrder", From 609ef154d08d1e947097ff6343dd7511813db8ea Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 25 Oct 2023 14:00:37 +0200 Subject: [PATCH 003/189] Set version to 0.8.23 --- CMakeLists.txt | 2 +- Changelog.md | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 73e7930b7222..298df418cd11 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.22") +set(PROJECT_VERSION "0.8.23") # 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 8c74b49cf72c..4e48228d3f56 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,14 @@ +### 0.8.23 (unreleased) + +Language Features: + + +Compiler Features: + + +Bugfixes: + + ### 0.8.22 (2023-10-25) Language Features: From 23bcc69478ae5d9b2759bedb2ede47ea7f1ff78d Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 26 Oct 2023 16:34:49 +0200 Subject: [PATCH 004/189] Fix wrong file path in standard json tests --- .../input.json | 2 +- .../input.json | 2 +- .../input.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_cleanup_sequence/input.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_cleanup_sequence/input.json index 0903ccfebd44..7d2f529ccf7f 100644 --- a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_cleanup_sequence/input.json +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_cleanup_sequence/input.json @@ -1,7 +1,7 @@ { "language": "Solidity", "sources": { - "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_cleanup_sequence/in.sol"]} + "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_empty_cleanup_sequence/in.sol"]} }, "settings": { "optimizer": { diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_optimisation_sequence/input.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_optimisation_sequence/input.json index 4086aff9fb14..9c603d6f7f0b 100644 --- a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_optimisation_sequence/input.json +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_optimisation_sequence/input.json @@ -1,7 +1,7 @@ { "language": "Solidity", "sources": { - "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_cleanup_sequence/in.sol"]} + "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_empty_optimisation_sequence/in.sol"]} }, "settings": { "optimizer": { diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence/input.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence/input.json index 4f66682223c3..5780bea83aa8 100644 --- a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence/input.json +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence/input.json @@ -1,7 +1,7 @@ { "language": "Solidity", "sources": { - "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_cleanup_sequence/in.sol"]} + "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence/in.sol"]} }, "settings": { "optimizer": { From 74c2440416f9dcd2472d4d19203bae6ac359b8a7 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 25 Oct 2023 12:45:23 +0200 Subject: [PATCH 005/189] Update the release checklist --- ReleaseChecklist.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index db07fb888555..505852496f83 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -58,6 +58,7 @@ At least a day before the release: Remove different variants of the same name manually before using the output. - [ ] Check that all tests on the latest commit in ``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 From 79b1f55cc51334cee7a38364bdcb0599702dd598 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 26 Oct 2023 13:42:55 +0200 Subject: [PATCH 006/189] Remove references to homebrew-ethereum --- ReleaseChecklist.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 505852496f83..db8bd426b66d 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -2,8 +2,7 @@ ### 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), [homebrew-ethereum](https://github.com/ethereum/homebrew-ethereum), - [solidity-website](https://github.com/ethereum/solidity-website). + [solc-bin](https://github.com/ethereum/solc-bin), [solidity-website](https://github.com/ethereum/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). @@ -21,7 +20,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) - - [ ] ``homebrew-ethereum`` - [ ] (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. @@ -74,7 +72,6 @@ At least a day before the release: ### Homebrew and MacOS - [ ] Update the version and the hash (``sha256sum solidity_$VERSION.tar.gz``) in the [``solidity`` formula in Homebrew core repository](https://github.com/Homebrew/homebrew-core/blob/master/Formula/solidity.rb). - - [ ] Update the version and the hash (``sha256sum solidity_$VERSION.tar.gz``) in [our custom ``solidity`` Homebrew formula](https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb). ### Docker - [ ] Run ``./scripts/docker_deploy_manual.sh v$VERSION``. From 7bf058b0c53a30b5040cb8905fdf7313f25bd42b Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 25 Oct 2023 14:26:22 +0200 Subject: [PATCH 007/189] Update ubuntu versions in PPA scripts. --- scripts/deps-ppa/static_z3.sh | 2 +- scripts/release_ppa.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/deps-ppa/static_z3.sh b/scripts/deps-ppa/static_z3.sh index 5ee6623a6922..67cb15ce8a2c 100755 --- a/scripts/deps-ppa/static_z3.sh +++ b/scripts/deps-ppa/static_z3.sh @@ -41,7 +41,7 @@ sourcePPAConfig # Sanity check checkDputEntries "\[cpp-build-deps\]" -DISTRIBUTIONS="focal jammy kinetic lunar" +DISTRIBUTIONS="focal jammy lunar mantic" for distribution in $DISTRIBUTIONS do diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh index bd98865d6ebc..252fd4c49c64 100755 --- a/scripts/release_ppa.sh +++ b/scripts/release_ppa.sh @@ -68,7 +68,7 @@ packagename=solc # This needs to be a still active release static_build_distribution=focal -DISTRIBUTIONS="focal jammy kinetic lunar" +DISTRIBUTIONS="focal jammy lunar mantic" if is_release then @@ -127,7 +127,7 @@ mv solidity solc mkdir -p ./solc/deps/downloads/ 2>/dev/null || true wget -O ./solc/deps/downloads/jsoncpp-1.9.3.tar.gz https://github.com/open-source-parsers/jsoncpp/archive/1.9.3.tar.gz wget -O ./solc/deps/downloads/range-v3-0.12.0.tar.gz https://github.com/ericniebler/range-v3/archive/0.12.0.tar.gz -wget -O ./solc/deps/downloads/fmt-8.0.1.tar.gz https://github.com/fmtlib/fmt/archive/8.0.1.tar.gz +wget -O ./solc/deps/downloads/fmt-9.1.0.tar.gz https://github.com/fmtlib/fmt/archive/9.1.0.tar.gz # Determine version cd solc From 4d10cb57241f35a0b5e500c46476e183a113de08 Mon Sep 17 00:00:00 2001 From: Nuzair Date: Tue, 7 Nov 2023 20:02:48 +0900 Subject: [PATCH 008/189] Update README.md for X (Twitter) new brand guidelines (#14665) * Update README.md for X (Twitter) new brand guidelines * suggested changes --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6242a69f0b54..e64f8cbfd332 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,10 @@ [![Matrix Chat](https://img.shields.io/badge/Matrix%20-chat-brightgreen?style=plastic&logo=matrix)](https://matrix.to/#/#ethereum_solidity:gitter.im) [![Gitter Chat](https://img.shields.io/badge/Gitter%20-chat-brightgreen?style=plastic&logo=gitter)](https://gitter.im/ethereum/solidity) [![Solidity Forum](https://img.shields.io/badge/Solidity_Forum%20-discuss-brightgreen?style=plastic&logo=discourse)](https://forum.soliditylang.org/) -[![Twitter Follow](https://img.shields.io/twitter/follow/solidity_lang?style=plastic&logo=twitter)](https://twitter.com/solidity_lang) +[![X Follow](https://img.shields.io/twitter/follow/solidity_lang?style=plastic&logo=x)](https://X.com/solidity_lang) [![Mastodon Follow](https://img.shields.io/mastodon/follow/000335908?domain=https%3A%2F%2Ffosstodon.org%2F&logo=mastodon&style=plastic)](https://fosstodon.org/@solidity) -You can talk to us on Gitter and Matrix, tweet at us on Twitter or create a new topic in the Solidity forum. Questions, feedback, and suggestions are welcome! +You can talk to us on Gitter and Matrix, tweet at us on X (previously Twitter) or create a new topic in the Solidity forum. Questions, feedback, and suggestions are welcome! Solidity is a statically typed, contract-oriented, high-level language for implementing smart contracts on the Ethereum platform. From d899d9c2c1095efe958c64c260c3b0a3fad726f0 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Mon, 30 Oct 2023 11:44:00 +0100 Subject: [PATCH 009/189] Accept empty optimizer sequence with Yul optimizer disabled Whitespace and newline validation for empty sequence Changelog and docs Kamil Revert "fixup! Accept empty optimizer sequence with Yul optimizer disabled" This reverts commit 1cbcc8e6b1edb0859876a8781f7b5ceb1722c154. --- Changelog.md | 3 +- docs/internals/optimizer.rst | 6 + libsolidity/interface/CompilerStack.cpp | 17 +++ libsolidity/interface/StandardCompiler.cpp | 20 ++- libyul/YulStack.cpp | 33 ++++- libyul/optimiser/Suite.cpp | 19 +++ libyul/optimiser/Suite.h | 4 + solc/CommandLineParser.cpp | 5 +- .../in.sol | 6 + .../input.json | 16 +++ .../output.json | 12 ++ .../in.sol | 6 + .../input.json | 16 +++ .../output.json | 9 ++ .../in.sol | 6 + .../input.json | 16 +++ .../output.json | 9 ++ .../output.json | 4 +- .../yul_optimizer_steps_disabled/err | 2 +- .../args | 1 + .../err | 11 ++ .../input.sol | 18 +++ .../output | 133 ++++++++++++++++++ test/solc/CommandLineParser.cpp | 36 +++++ 24 files changed, 393 insertions(+), 15 deletions(-) create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/in.sol create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/input.json create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/output.json create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/in.sol create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/input.json create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/output.json create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/in.sol create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/input.json create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/output.json create mode 100644 test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/args create mode 100644 test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/err create mode 100644 test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/input.sol create mode 100644 test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/output diff --git a/Changelog.md b/Changelog.md index 4e48228d3f56..d096eeddd1e6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,7 +4,8 @@ Language Features: Compiler Features: - + * Commandline Interface: An empty ``--yul-optimizations`` sequence can now be always provided. + * Standard JSON Interface: An empty ``optimizerSteps`` sequence can now always be provided. Bugfixes: diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index b3e144e94744..15a25f960003 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -30,6 +30,11 @@ for a stand-alone Yul mode. The `peephole optimizer `_ is always enabled by default and can only be turned off via the :ref:`Standard JSON `. +.. note:: + An empty optimizer sequence is accepted even without ``--optimize`` in order to fully disable + the user-supplied portion of the Yul :ref:`optimizer sequence `, as by default, + even when the optimizer is not turned on, the :ref:`unused pruner ` step will be run. + You can find more details on both optimizer modules and their optimization steps below. Benefits of Optimizing Solidity Code @@ -329,6 +334,7 @@ Abbreviation Full name Some steps depend on properties ensured by ``BlockFlattener``, ``FunctionGrouper``, ``ForLoopInitRewriter``. For this reason the Yul optimizer always applies them before applying any steps supplied by the user. +.. _selecting-optimizations: Selecting Optimizations ----------------------- diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 14b335016395..eb0ed3550036 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -69,6 +69,7 @@ #include #include #include +#include #include #include @@ -99,6 +100,7 @@ using namespace solidity; using namespace solidity::langutil; using namespace solidity::frontend; using namespace solidity::stdlib; +using namespace solidity::yul; using namespace std::string_literals; using solidity::util::errinfo_comment; @@ -1672,6 +1674,21 @@ std::string CompilerStack::createMetadata(Contract const& _contract, bool _forIR details["yulDetails"]["stackAllocation"] = m_optimiserSettings.optimizeStackAllocation; details["yulDetails"]["optimizerSteps"] = m_optimiserSettings.yulOptimiserSteps + ":" + m_optimiserSettings.yulOptimiserCleanupSteps; } + else if ( + OptimiserSuite::isEmptyOptimizerSequence(m_optimiserSettings.yulOptimiserSteps) && + OptimiserSuite::isEmptyOptimizerSequence(m_optimiserSettings.yulOptimiserCleanupSteps) + ) + { + solAssert(m_optimiserSettings.optimizeStackAllocation == false); + details["yulDetails"] = Json::objectValue; + details["yulDetails"]["optimizerSteps"] = ":"; + } + else + { + solAssert(m_optimiserSettings.optimizeStackAllocation == false); + solAssert(m_optimiserSettings.yulOptimiserSteps == OptimiserSettings::DefaultYulOptimiserSteps); + solAssert(m_optimiserSettings.yulOptimiserCleanupSteps == OptimiserSettings::DefaultYulOptimiserCleanupSteps); + } meta["settings"]["optimizer"]["details"] = std::move(details); } diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 91a748053131..b74f764c5592 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -456,12 +456,16 @@ std::optional checkOptimizerDetail(Json::Value const& _details, std return {}; } -std::optional checkOptimizerDetailSteps(Json::Value const& _details, std::string const& _name, std::string& _optimiserSetting, std::string& _cleanupSetting) +std::optional checkOptimizerDetailSteps(Json::Value const& _details, std::string const& _name, std::string& _optimiserSetting, std::string& _cleanupSetting, bool _runYulOptimizer) { if (_details.isMember(_name)) { if (_details[_name].isString()) { + std::string const fullSequence = _details[_name].asString(); + if (!_runYulOptimizer && !OptimiserSuite::isEmptyOptimizerSequence(fullSequence)) + return formatFatalError(Error::Type::JSONError, "If Yul optimizer is disabled, only an empty optimizerSteps sequence is accepted."); + try { yul::OptimiserSuite::validateSequence(_details[_name].asString()); @@ -474,7 +478,6 @@ std::optional checkOptimizerDetailSteps(Json::Value const& _details ); } - std::string const fullSequence = _details[_name].asString(); auto const delimiterPos = fullSequence.find(":"); _optimiserSetting = fullSequence.substr(0, delimiterPos); @@ -605,22 +608,27 @@ std::variant parseOptimizerSettings(Json::Value if (details.isMember("yulDetails")) { if (!settings.runYulOptimiser) - return formatFatalError(Error::Type::JSONError, "\"Providing yulDetails requires Yul optimizer to be enabled."); + { + if (checkKeys(details["yulDetails"], {"optimizerSteps"}, "settings.optimizer.details.yulDetails")) + return formatFatalError(Error::Type::JSONError, "Only optimizerSteps can be set in yulDetails when Yul optimizer is disabled."); + if (auto error = checkOptimizerDetailSteps(details["yulDetails"], "optimizerSteps", settings.yulOptimiserSteps, settings.yulOptimiserCleanupSteps, settings.runYulOptimiser)) + return *error; + return {std::move(settings)}; + } if (auto result = checkKeys(details["yulDetails"], {"stackAllocation", "optimizerSteps"}, "settings.optimizer.details.yulDetails")) return *result; if (auto error = checkOptimizerDetail(details["yulDetails"], "stackAllocation", settings.optimizeStackAllocation)) return *error; - if (auto error = checkOptimizerDetailSteps(details["yulDetails"], "optimizerSteps", settings.yulOptimiserSteps, settings.yulOptimiserCleanupSteps)) + if (auto error = checkOptimizerDetailSteps(details["yulDetails"], "optimizerSteps", settings.yulOptimiserSteps, settings.yulOptimiserCleanupSteps, settings.runYulOptimiser)) return *error; } } - return { std::move(settings) }; + return {std::move(settings)}; } } - std::variant StandardCompiler::parseInput(Json::Value const& _input) { InputsAndSettings ret; diff --git a/libyul/YulStack.cpp b/libyul/YulStack.cpp index a5d72c7bb5d7..6c89cadbdfd9 100644 --- a/libyul/YulStack.cpp +++ b/libyul/YulStack.cpp @@ -34,12 +34,14 @@ #include #include #include +#include #include #include using namespace solidity; +using namespace solidity::frontend; using namespace solidity::yul; using namespace solidity::langutil; @@ -164,14 +166,39 @@ void YulStack::optimize(Object& _object, bool _isCreation) if (EVMDialect const* evmDialect = dynamic_cast(&dialect)) meter = std::make_unique(*evmDialect, _isCreation, m_optimiserSettings.expectedExecutionsPerDeployment); + auto [optimizeStackAllocation, yulOptimiserSteps, yulOptimiserCleanupSteps] = [&]() -> std::tuple + { + if (!m_optimiserSettings.runYulOptimiser) + { + // Yul optimizer disabled, but empty sequence (:) explicitly provided + if (OptimiserSuite::isEmptyOptimizerSequence(m_optimiserSettings.yulOptimiserSteps + ":" + m_optimiserSettings.yulOptimiserCleanupSteps)) + return std::make_tuple(true, "", ""); + // Yul optimizer disabled, and no sequence explicitly provided (assumes default sequence) + else + { + yulAssert( + m_optimiserSettings.yulOptimiserSteps == OptimiserSettings::DefaultYulOptimiserSteps && + m_optimiserSettings.yulOptimiserCleanupSteps == OptimiserSettings::DefaultYulOptimiserCleanupSteps + ); + return std::make_tuple(true, "u", ""); + } + + } + return std::make_tuple( + m_optimiserSettings.optimizeStackAllocation, + m_optimiserSettings.yulOptimiserSteps, + m_optimiserSettings.yulOptimiserCleanupSteps + ); + }(); + OptimiserSuite::run( dialect, meter.get(), _object, // Defaults are the minimum necessary to avoid running into "Stack too deep" constantly. - m_optimiserSettings.runYulOptimiser ? m_optimiserSettings.optimizeStackAllocation : true, - m_optimiserSettings.runYulOptimiser ? m_optimiserSettings.yulOptimiserSteps : "u", - m_optimiserSettings.runYulOptimiser ? m_optimiserSettings.yulOptimiserCleanupSteps : "", + optimizeStackAllocation, + yulOptimiserSteps, + yulOptimiserCleanupSteps, _isCreation ? std::nullopt : std::make_optional(m_optimiserSettings.expectedExecutionsPerDeployment), {} ); diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 58561ee10a15..545ae4de06d7 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -383,6 +383,25 @@ void OptimiserSuite::validateSequence(std::string_view _stepAbbreviations) assertThrow(nestingLevel == 0, OptimizerException, "Unbalanced brackets"); } +bool OptimiserSuite::isEmptyOptimizerSequence(std::string const& _sequence) +{ + size_t delimiterCount{0}; + for (char const step: _sequence) + switch (step) + { + case ':': + if (++delimiterCount > 1) + return false; + break; + case ' ': + case '\n': + break; + default: + return false; + } + return true; +} + void OptimiserSuite::runSequence(std::string_view _stepAbbreviations, Block& _ast, bool _repeatUntilStable) { validateSequence(_stepAbbreviations); diff --git a/libyul/optimiser/Suite.h b/libyul/optimiser/Suite.h index abed2720e2a5..6e3886da3b2d 100644 --- a/libyul/optimiser/Suite.h +++ b/libyul/optimiser/Suite.h @@ -76,6 +76,10 @@ class OptimiserSuite /// Ensures that specified sequence of step abbreviations is well-formed and can be executed. /// @throw OptimizerException if the sequence is invalid static void validateSequence(std::string_view _stepAbbreviations); + /// Check whether the provided sequence is empty provided that the allowed characters are + /// whitespace, newline and : + static bool isEmptyOptimizerSequence(std::string const& _sequence); + void runSequence(std::vector const& _steps, Block& _ast); void runSequence(std::string_view _stepAbbreviations, Block& _ast, bool _repeatUntilStable = false); diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 0c7484fd4017..3ed1b7b634ca 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -33,6 +33,7 @@ #include using namespace solidity::langutil; +using namespace solidity::yul; namespace po = boost::program_options; @@ -1228,8 +1229,8 @@ void CommandLineParser::processArgs() if (m_args.count(g_strYulOptimizations)) { OptimiserSettings optimiserSettings = m_options.optimiserSettings(); - if (!optimiserSettings.runYulOptimiser) - solThrow(CommandLineValidationError, "--" + g_strYulOptimizations + " is invalid if Yul optimizer is disabled"); + if (!optimiserSettings.runYulOptimiser && !OptimiserSuite::isEmptyOptimizerSequence(m_args[g_strYulOptimizations].as())) + solThrow(CommandLineValidationError, "--" + g_strYulOptimizations + " is invalid with a non-empty sequence if Yul optimizer is disabled."); try { diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/in.sol b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/in.sol new file mode 100644 index 000000000000..9e5350072a2c --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/in.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C { + function f() public pure {} +} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/input.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/input.json new file mode 100644 index 000000000000..511860250476 --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_no_yul/in.sol"]} + }, + "settings": { + "optimizer": { + "details": { + "yul": false, + "yulDetails": { + "optimizerSteps": "u:fdntOc" + } + } + } + } +} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/output.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/output.json new file mode 100644 index 000000000000..fa3c9d93edc5 --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/output.json @@ -0,0 +1,12 @@ +{ + "errors": + [ + { + "component": "general", + "formattedMessage": "If Yul optimizer is disabled, only an empty optimizerSteps sequence is accepted.", + "message": "If Yul optimizer is disabled, only an empty optimizerSteps sequence is accepted.", + "severity": "error", + "type": "JSONError" + } + ] +} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/in.sol b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/in.sol new file mode 100644 index 000000000000..9e5350072a2c --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/in.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C { + function f() public pure {} +} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/input.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/input.json new file mode 100644 index 000000000000..9de7beea915a --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/in.sol"]} + }, + "settings": { + "optimizer": { + "details": { + "yul": false, + "yulDetails": { + "optimizerSteps": ":" + } + } + } + } +} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/output.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/output.json new file mode 100644 index 000000000000..acf3b74ef1de --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/output.json @@ -0,0 +1,9 @@ +{ + "sources": + { + "A": + { + "id": 0 + } + } +} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/in.sol b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/in.sol new file mode 100644 index 000000000000..9e5350072a2c --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/in.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C { + function f() public pure {} +} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/input.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/input.json new file mode 100644 index 000000000000..321ac8e99ab8 --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/in.sol"]} + }, + "settings": { + "optimizer": { + "details": { + "yul": false, + "yulDetails": { + "optimizerSteps": "\n : " + } + } + } + } +} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/output.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/output.json new file mode 100644 index 000000000000..acf3b74ef1de --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/output.json @@ -0,0 +1,9 @@ +{ + "sources": + { + "A": + { + "id": 0 + } + } +} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_without_yul/output.json b/test/cmdlineTests/standard_optimizer_yulDetails_without_yul/output.json index 4e6d7b6f4ddc..2470015e1fb0 100644 --- a/test/cmdlineTests/standard_optimizer_yulDetails_without_yul/output.json +++ b/test/cmdlineTests/standard_optimizer_yulDetails_without_yul/output.json @@ -3,8 +3,8 @@ [ { "component": "general", - "formattedMessage": "\"Providing yulDetails requires Yul optimizer to be enabled.", - "message": "\"Providing yulDetails requires Yul optimizer to be enabled.", + "formattedMessage": "Only optimizerSteps can be set in yulDetails when Yul optimizer is disabled.", + "message": "Only optimizerSteps can be set in yulDetails when Yul optimizer is disabled.", "severity": "error", "type": "JSONError" } diff --git a/test/cmdlineTests/yul_optimizer_steps_disabled/err b/test/cmdlineTests/yul_optimizer_steps_disabled/err index a557351ea247..1abbab8bde55 100644 --- a/test/cmdlineTests/yul_optimizer_steps_disabled/err +++ b/test/cmdlineTests/yul_optimizer_steps_disabled/err @@ -1 +1 @@ -Error: --yul-optimizations is invalid if Yul optimizer is disabled +Error: --yul-optimizations is invalid with a non-empty sequence if Yul optimizer is disabled. diff --git a/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/args b/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/args new file mode 100644 index 000000000000..fef6c6531e19 --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/args @@ -0,0 +1 @@ +--ir-optimized --metadata --yul-optimizations : diff --git a/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/err b/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/err new file mode 100644 index 000000000000..95e30df2e83d --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/err @@ -0,0 +1,11 @@ +Warning: Unused local variable. + --> yul_optimizer_steps_without_optimize_empty_sequence/input.sol:13:9: + | +13 | uint b = a; + | ^^^^^^ + +Warning: Unused local variable. + --> yul_optimizer_steps_without_optimize_empty_sequence/input.sol:14:9: + | +14 | uint c = a; + | ^^^^^^ diff --git a/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/input.sol b/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/input.sol new file mode 100644 index 000000000000..9e1b04ab1c47 --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/input.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C +{ + constructor() {} + + function foo() public pure returns (bool) + { + // given the empty optimizer sequence ``:``, ``b`` and ``c`` should not be removed in the + // optimized IR as the ``UnusedPruner`` step will not be run. + uint a = 100; + uint b = a; + uint c = a; + + return a == 100; + } +} diff --git a/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/output b/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/output new file mode 100644 index 000000000000..9ee68107929a --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/output @@ -0,0 +1,133 @@ + +======= yul_optimizer_steps_without_optimize_empty_sequence/input.sol:C ======= +Optimized IR: +/// @use-src 0:"yul_optimizer_steps_without_optimize_empty_sequence/input.sol" +object "C_28" { + code { + { + /// @src 0:60:410 "contract C..." + mstore(64, memoryguard(0x80)) + if callvalue() + { + revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() + } + constructor_C() + let _1 := allocate_unbounded() + codecopy(_1, dataoffset("C_28_deployed"), datasize("C_28_deployed")) + return(_1, datasize("C_28_deployed")) + } + function allocate_unbounded() -> memPtr + { memPtr := mload(64) } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() + { revert(0, 0) } + /// @ast-id 5 @src 0:77:93 "constructor() {}" + function constructor_C() + { } + } + /// @use-src 0:"yul_optimizer_steps_without_optimize_empty_sequence/input.sol" + object "C_28_deployed" { + code { + { + /// @src 0:60:410 "contract C..." + mstore(64, memoryguard(0x80)) + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_unsigned(calldataload(0)) + switch selector + case 0xc2985578 { external_fun_foo() } + 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_bool(value) -> cleaned + { + cleaned := iszero(iszero(value)) + } + function abi_encode_bool_to_bool(value, pos) + { + mstore(pos, cleanup_bool(value)) + } + function abi_encode_bool(headStart, value0) -> tail + { + tail := add(headStart, 32) + abi_encode_bool_to_bool(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_bool(memPos, ret) + return(memPos, sub(memEnd, memPos)) + } + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + { revert(0, 0) } + function zero_value_for_split_bool() -> ret + { ret := 0 } + 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))) + } + /// @ast-id 27 @src 0:99:408 "function foo() public pure returns (bool)..." + function fun_foo() -> var + { + /// @src 0:135:139 "bool" + let zero_bool := zero_value_for_split_bool() + var := zero_bool + /// @src 0:332:335 "100" + let expr := 0x64 + /// @src 0:323:335 "uint a = 100" + let var_a := convert_rational_by_to_uint256(expr) + /// @src 0:354:355 "a" + let _1 := var_a + let expr_1 := _1 + /// @src 0:345:355 "uint b = a" + let var_b := expr_1 + /// @src 0:374:375 "a" + let _2 := var_a + let expr_2 := _2 + /// @src 0:365:375 "uint c = a" + let var_c := expr_2 + /// @src 0:393:394 "a" + let _3 := var_a + let expr_3 := _3 + /// @src 0:398:401 "100" + let expr_4 := 0x64 + /// @src 0:393:401 "a == 100" + let expr_5 := eq(cleanup_uint256(expr_3), convert_rational_by_to_uint256(expr_4)) + /// @src 0:386:401 "return a == 100" + var := expr_5 + leave + } + } + data ".metadata" hex"" + } +} + +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":{"yul_optimizer_steps_without_optimize_empty_sequence/input.sol":"C"},"evmVersion":"shanghai","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":{"yul_optimizer_steps_without_optimize_empty_sequence/input.sol":{"keccak256":"0x3fc910e345ce1ee62bfa6b0f66931ee632c08265b25b6139cfbbfe4d2f8d5dd8","license":"GPL-3.0","urls":["bzz-raw://e557e9ad2c2e420a669c06ae456b0b790d77d2d6d492cd8540e6b244388a5140","dweb:/ipfs/QmaNiZmC2Mo3YxGiehs1n3dVTjZwD7FguX7EUtpeshMVuR"]}},"version":1} diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index caad98f999a2..28ca2c48a1a0 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -572,6 +572,42 @@ BOOST_AUTO_TEST_CASE(invalid_optimiser_sequences) } } +BOOST_AUTO_TEST_CASE(valid_empty_optimizer_sequences_without_optimize) +{ + vector const validSequenceInputs { + " :", + ": ", + "\n : \n", + ":" + }; + + vector> const expectedParsedSequences { + {" ", ""}, + {"", " "}, + {"\n ", " \n"}, + {"", ""} + }; + + BOOST_CHECK_EQUAL(validSequenceInputs.size(), expectedParsedSequences.size()); + + for (size_t i = 0; i < validSequenceInputs.size(); ++i) + { + CommandLineOptions const& commandLineOptions = parseCommandLine({"solc", "contract.sol", "--yul-optimizations=" + validSequenceInputs[i]}); + auto const& [expectedYulOptimiserSteps, expectedYulCleanupSteps] = expectedParsedSequences[i]; + BOOST_CHECK_EQUAL(commandLineOptions.optimiserSettings().yulOptimiserSteps, expectedYulOptimiserSteps); + BOOST_CHECK_EQUAL(commandLineOptions.optimiserSettings().yulOptimiserCleanupSteps, expectedYulCleanupSteps); + } +} + +BOOST_AUTO_TEST_CASE(invalid_optimizer_sequence_without_optimize) +{ + string const invalidSequence{"u: "}; + string const expectedErrorMessage{"--yul-optimizations is invalid with a non-empty sequence if Yul optimizer is disabled."}; + vector commandLineOptions{"solc", "contract.sol", "--yul-optimizations=" + invalidSequence}; + auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) { return _exception.what() == expectedErrorMessage; }; + BOOST_CHECK_EXCEPTION(parseCommandLine(commandLineOptions), CommandLineValidationError, hasCorrectMessage); +} + BOOST_AUTO_TEST_SUITE_END() } // namespace solidity::frontend::test From cc56cb5921e28b3a80b8120702cde92f773f4ad9 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 8 Nov 2023 00:45:37 +0100 Subject: [PATCH 010/189] Add verbatim bug test case. --- test/libyul/objectCompiler/verbatim_bug.yul | 109 ++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 test/libyul/objectCompiler/verbatim_bug.yul diff --git a/test/libyul/objectCompiler/verbatim_bug.yul b/test/libyul/objectCompiler/verbatim_bug.yul new file mode 100644 index 000000000000..dcd39af8d500 --- /dev/null +++ b/test/libyul/objectCompiler/verbatim_bug.yul @@ -0,0 +1,109 @@ +object "a" { + code { + let dummy := 0xAABBCCDDEEFF + let input := sload(0) + let output + + switch input + case 0x00 { + // Note that due to a bug the following disappeared from the assembly output. + output := verbatim_1i_1o(hex"506000", dummy) + } + case 0x01 { + output := 1 + } + case 0x02 { + output := verbatim_1i_1o(hex"506002", dummy) + } + case 0x03 { + output := 3 + } + + sstore(0, output) + } +} +// ==== +// optimizationPreset: full +// ---- +// Assembly: +// /* "source":87:88 */ +// 0x00 +// /* "source":81:89 */ +// dup1 +// sload +// /* "source":139:307 */ +// dup1 +// iszero +// tag_5 +// jumpi +// /* "source":316:361 */ +// dup1 +// /* "source":321:325 */ +// 0x01 +// /* "source":316:361 */ +// eq +// tag_3 +// jumpi +// /* "source":370:448 */ +// dup1 +// /* "source":375:379 */ +// 0x02 +// /* "source":370:448 */ +// eq +// tag_5 +// jumpi +// /* "source":462:466 */ +// 0x03 +// /* "source":457:502 */ +// eq +// tag_7 +// jumpi +// /* "source":87:88 */ +// 0x00 +// /* "source":512:529 */ +// sstore +// /* "source":118:502 */ +// stop +// /* "source":467:502 */ +// tag_7: +// /* "source":481:492 */ +// pop +// /* "source":491:492 */ +// 0x03 +// /* "source":87:88 */ +// 0x00 +// /* "source":512:529 */ +// sstore +// /* "source":118:502 */ +// stop +// /* "source":380:448 */ +// tag_5: +// /* "source":404:438 */ +// pop +// pop +// /* "source":45:59 */ +// 0xaabbccddeeff +// /* "source":404:438 */ +// verbatimbytecode_506002 +// /* "source":87:88 */ +// 0x00 +// /* "source":512:529 */ +// sstore +// /* "source":118:502 */ +// stop +// /* "source":326:361 */ +// tag_3: +// /* "source":340:351 */ +// pop +// pop +// /* "source":350:351 */ +// 0x01 +// /* "source":87:88 */ +// 0x00 +// /* "source":512:529 */ +// sstore +// /* "source":118:502 */ +// stop +// Bytecode: 5f805480156026578060011460365780600214602657600314601f575f55005b5060035f55005b505065aabbccddeeff5060025f55005b505060015f5500 +// Opcodes: PUSH0 DUP1 SLOAD DUP1 ISZERO PUSH1 0x26 JUMPI DUP1 PUSH1 0x1 EQ PUSH1 0x36 JUMPI DUP1 PUSH1 0x2 EQ PUSH1 0x26 JUMPI PUSH1 0x3 EQ PUSH1 0x1F JUMPI PUSH0 SSTORE STOP JUMPDEST POP PUSH1 0x3 PUSH0 SSTORE STOP JUMPDEST POP POP PUSH6 0xAABBCCDDEEFF POP PUSH1 0x2 PUSH0 SSTORE STOP JUMPDEST POP POP PUSH1 0x1 PUSH0 SSTORE STOP +// SourceMappings: 87:1:0:-:0;81:8;;139:168;;;;316:45;321:4;316:45;;;370:78;375:4;370:78;;;462:4;457:45;;;87:1;512:17;118:384;467:35;481:11;491:1;87;512:17;118:384;380:68;404:34;;45:14;404:34;87:1;512:17;118:384;326:35;340:11;;350:1;87;512:17;118:384 From 48fdbd39b3db163ee563222d15b15c79fcd3cc95 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 26 Oct 2023 01:11:00 +0200 Subject: [PATCH 011/189] Fix assembly item comparison for verbatim. Co-authored-by: Matheus Aguiar --- Changelog.md | 4 ++++ docs/bugs.json | 10 +++++++++ docs/bugs_by_version.json | 24 +++++++++++++++++++-- libevmasm/AssemblyItem.h | 2 +- test/libyul/objectCompiler/verbatim_bug.yul | 23 ++++++++++++++++---- 5 files changed, 56 insertions(+), 7 deletions(-) diff --git a/Changelog.md b/Changelog.md index d096eeddd1e6..c72aea22c114 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,9 @@ ### 0.8.23 (unreleased) +Important Bugfixes: + * Optimizer: Fix block deduplicator bug which led to blocks which are identical apart from the contents of ``verbatim`` instructions to be treated as equivalent and thus collapsed into a single one. + + Language Features: diff --git a/docs/bugs.json b/docs/bugs.json index 6245bdeda5af..6e2468600537 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -1,4 +1,14 @@ [ + { + "uid": "SOL-2023-3", + "name": "VerbatimInvalidDeduplication", + "summary": "All ``verbatim`` blocks are considered identical by deduplicator and can incorrectly be unified when surrounded by identical opcodes.", + "description": "The block deduplicator is a step of the opcode-based optimizer which identifies equivalent assembly blocks and merges them into a single one. However, when blocks contained ``verbatim``, their comparison was performed incorrectly, leading to the collapse of assembly blocks which are identical except for the contents of the ``verbatim`` items. Since ``verbatim`` is only available in Yul, compilation of Solidity sources is not affected.", + "link": "https://blog.soliditylang.org/2023/X/Y/Z/", + "introduced": "0.8.5", + "fixed": "0.8.23", + "severity": "low" + }, { "uid": "SOL-2023-2", "name": "FullInlinerNonExpressionSplitArgumentEvaluationOrder", diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 982444b10330..50132d0ed9ef 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1743,6 +1743,7 @@ }, "0.8.10": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1754,6 +1755,7 @@ }, "0.8.11": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1766,6 +1768,7 @@ }, "0.8.12": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1778,6 +1781,7 @@ }, "0.8.13": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "StorageWriteRemovalBeforeConditionalTermination", @@ -1791,6 +1795,7 @@ }, "0.8.14": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "StorageWriteRemovalBeforeConditionalTermination", @@ -1802,6 +1807,7 @@ }, "0.8.15": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "StorageWriteRemovalBeforeConditionalTermination", @@ -1811,6 +1817,7 @@ }, "0.8.16": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "StorageWriteRemovalBeforeConditionalTermination" @@ -1819,6 +1826,7 @@ }, "0.8.17": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" ], @@ -1826,6 +1834,7 @@ }, "0.8.18": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" ], @@ -1833,6 +1842,7 @@ }, "0.8.19": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" ], @@ -1854,17 +1864,22 @@ }, "0.8.20": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" ], "released": "2023-05-10" }, "0.8.21": { - "bugs": [], + "bugs": [ + "VerbatimInvalidDeduplication" + ], "released": "2023-07-19" }, "0.8.22": { - "bugs": [], + "bugs": [ + "VerbatimInvalidDeduplication" + ], "released": "2023-10-25" }, "0.8.3": { @@ -1894,6 +1909,7 @@ }, "0.8.5": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1906,6 +1922,7 @@ }, "0.8.6": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1918,6 +1935,7 @@ }, "0.8.7": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1930,6 +1948,7 @@ }, "0.8.8": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1943,6 +1962,7 @@ }, "0.8.9": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index 40f98dae875c..93be394733ca 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -140,7 +140,7 @@ class AssemblyItem else if (type() == Operation) return instruction() < _other.instruction(); else if (type() == VerbatimBytecode) - return *m_verbatimBytecode == *_other.m_verbatimBytecode; + return *m_verbatimBytecode < *_other.m_verbatimBytecode; else return data() < _other.data(); } diff --git a/test/libyul/objectCompiler/verbatim_bug.yul b/test/libyul/objectCompiler/verbatim_bug.yul index dcd39af8d500..778494f0b311 100644 --- a/test/libyul/objectCompiler/verbatim_bug.yul +++ b/test/libyul/objectCompiler/verbatim_bug.yul @@ -34,7 +34,7 @@ object "a" { // /* "source":139:307 */ // dup1 // iszero -// tag_5 +// tag_1 // jumpi // /* "source":316:361 */ // dup1 @@ -104,6 +104,21 @@ object "a" { // sstore // /* "source":118:502 */ // stop -// Bytecode: 5f805480156026578060011460365780600214602657600314601f575f55005b5060035f55005b505065aabbccddeeff5060025f55005b505060015f5500 -// Opcodes: PUSH0 DUP1 SLOAD DUP1 ISZERO PUSH1 0x26 JUMPI DUP1 PUSH1 0x1 EQ PUSH1 0x36 JUMPI DUP1 PUSH1 0x2 EQ PUSH1 0x26 JUMPI PUSH1 0x3 EQ PUSH1 0x1F JUMPI PUSH0 SSTORE STOP JUMPDEST POP PUSH1 0x3 PUSH0 SSTORE STOP JUMPDEST POP POP PUSH6 0xAABBCCDDEEFF POP PUSH1 0x2 PUSH0 SSTORE STOP JUMPDEST POP POP PUSH1 0x1 PUSH0 SSTORE STOP -// SourceMappings: 87:1:0:-:0;81:8;;139:168;;;;316:45;321:4;316:45;;;370:78;375:4;370:78;;;462:4;457:45;;;87:1;512:17;118:384;467:35;481:11;491:1;87;512:17;118:384;380:68;404:34;;45:14;404:34;87:1;512:17;118:384;326:35;340:11;;350:1;87;512:17;118:384 +// /* "source":149:307 */ +// tag_1: +// /* "source":263:297 */ +// pop +// pop +// /* "source":45:59 */ +// 0xaabbccddeeff +// /* "source":263:297 */ +// verbatimbytecode_506000 +// /* "source":87:88 */ +// 0x00 +// /* "source":512:529 */ +// sstore +// /* "source":118:502 */ +// stop +// Bytecode: 5f80548015603e578060011460365780600214602657600314601f575f55005b5060035f55005b505065aabbccddeeff5060025f55005b505060015f55005b505065aabbccddeeff5060005f5500 +// Opcodes: PUSH0 DUP1 SLOAD DUP1 ISZERO PUSH1 0x3E JUMPI DUP1 PUSH1 0x1 EQ PUSH1 0x36 JUMPI DUP1 PUSH1 0x2 EQ PUSH1 0x26 JUMPI PUSH1 0x3 EQ PUSH1 0x1F JUMPI PUSH0 SSTORE STOP JUMPDEST POP PUSH1 0x3 PUSH0 SSTORE STOP JUMPDEST POP POP PUSH6 0xAABBCCDDEEFF POP PUSH1 0x2 PUSH0 SSTORE STOP JUMPDEST POP POP PUSH1 0x1 PUSH0 SSTORE STOP JUMPDEST POP POP PUSH6 0xAABBCCDDEEFF POP PUSH1 0x0 PUSH0 SSTORE STOP +// SourceMappings: 87:1:0:-:0;81:8;;139:168;;;;316:45;321:4;316:45;;;370:78;375:4;370:78;;;462:4;457:45;;;87:1;512:17;118:384;467:35;481:11;491:1;87;512:17;118:384;380:68;404:34;;45:14;404:34;87:1;512:17;118:384;326:35;340:11;;350:1;87;512:17;118:384;149:158;263:34;;45:14;263:34;87:1;512:17;118:384 From 41ceb483fafb550ae9dd121f322cfc1f2e32cd91 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Tue, 7 Nov 2023 23:00:23 -0300 Subject: [PATCH 012/189] Add cmdline test --- .../~deduplicator-verbatim-bug/test.sh | 19 +++++++++++++++++ .../verbatim_inside_identical_blocks.yul | 21 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100755 test/cmdlineTests/~deduplicator-verbatim-bug/test.sh create mode 100644 test/cmdlineTests/~deduplicator-verbatim-bug/verbatim_inside_identical_blocks.yul diff --git a/test/cmdlineTests/~deduplicator-verbatim-bug/test.sh b/test/cmdlineTests/~deduplicator-verbatim-bug/test.sh new file mode 100755 index 000000000000..19b57e8caab3 --- /dev/null +++ b/test/cmdlineTests/~deduplicator-verbatim-bug/test.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -eo pipefail + +# This is a regression test against https://github.com/ethereum/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. + +# shellcheck source=scripts/common.sh +source "${REPO_ROOT}/scripts/common.sh" +# shellcheck source=scripts/common_cmdline.sh +source "${REPO_ROOT}/scripts/common_cmdline.sh" + +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +YUL_SOURCE="${SCRIPT_DIR}/verbatim_inside_identical_blocks.yul" + +VERBATIM_OCCURRENCES=$(< "$YUL_SOURCE" "$SOLC" --strict-assembly - --optimize --asm | grep -e "verbatim" -c) + +[[ $VERBATIM_OCCURRENCES == 2 ]] || assertFail "Incorrect number of verbatim items in assembly." diff --git a/test/cmdlineTests/~deduplicator-verbatim-bug/verbatim_inside_identical_blocks.yul b/test/cmdlineTests/~deduplicator-verbatim-bug/verbatim_inside_identical_blocks.yul new file mode 100644 index 000000000000..1dc9a4615eaa --- /dev/null +++ b/test/cmdlineTests/~deduplicator-verbatim-bug/verbatim_inside_identical_blocks.yul @@ -0,0 +1,21 @@ +{ + let special := 0xFFFFFFFFFFFF + let input := sload(0) + let output + + switch input + case 0x00 { + output := verbatim_1i_1o(hex"506000", special) + } + case 0x01 { + output := 1 + } + case 0x02 { + output := verbatim_1i_1o(hex"506002", special) + } + case 0x03 { + output := 3 + } + + sstore(0, output) +} From 8292d7b53a1572dad7d4b422497078d42d17c273 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 8 Nov 2023 09:17:49 +0100 Subject: [PATCH 013/189] Remove superflous language features category from Changelog --- Changelog.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index c72aea22c114..03bfd2ce4920 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,15 +4,10 @@ Important Bugfixes: * Optimizer: Fix block deduplicator bug which led to blocks which are identical apart from the contents of ``verbatim`` instructions to be treated as equivalent and thus collapsed into a single one. -Language Features: - - Compiler Features: * Commandline Interface: An empty ``--yul-optimizations`` sequence can now be always provided. * Standard JSON Interface: An empty ``optimizerSteps`` sequence can now always be provided. -Bugfixes: - ### 0.8.22 (2023-10-25) From 7e84be61e31bab9a7d9a6659ff4cf95417c13bbc Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 8 Nov 2023 10:05:42 +0100 Subject: [PATCH 014/189] Set date for 0.8.23 release --- Changelog.md | 2 +- docs/bugs_by_version.json | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 03bfd2ce4920..ef5416fb3dcb 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,4 @@ -### 0.8.23 (unreleased) +### 0.8.23 (2023-11-08) Important Bugfixes: * Optimizer: Fix block deduplicator bug which led to blocks which are identical apart from the contents of ``verbatim`` instructions to be treated as equivalent and thus collapsed into a single one. diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 50132d0ed9ef..152e8d97a4d5 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1882,6 +1882,10 @@ ], "released": "2023-10-25" }, + "0.8.23": { + "bugs": [], + "released": "2023-11-08" + }, "0.8.3": { "bugs": [ "FullInlinerNonExpressionSplitArgumentEvaluationOrder", From 5cafa8c82618db013546755864412a3db8074641 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 8 Nov 2023 12:25:03 +0100 Subject: [PATCH 015/189] Fix link in bugs.json for 0.8.23 --- docs/bugs.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/bugs.json b/docs/bugs.json index 6e2468600537..c853f95eddea 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -4,7 +4,7 @@ "name": "VerbatimInvalidDeduplication", "summary": "All ``verbatim`` blocks are considered identical by deduplicator and can incorrectly be unified when surrounded by identical opcodes.", "description": "The block deduplicator is a step of the opcode-based optimizer which identifies equivalent assembly blocks and merges them into a single one. However, when blocks contained ``verbatim``, their comparison was performed incorrectly, leading to the collapse of assembly blocks which are identical except for the contents of the ``verbatim`` items. Since ``verbatim`` is only available in Yul, compilation of Solidity sources is not affected.", - "link": "https://blog.soliditylang.org/2023/X/Y/Z/", + "link": "https://blog.soliditylang.org/2023/11/08/verbatim-invalid-deduplication-bug/", "introduced": "0.8.5", "fixed": "0.8.23", "severity": "low" From 4fd869e92c241cf99e64c6acf6de9bded299efa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 8 Nov 2023 11:48:55 +0100 Subject: [PATCH 016/189] Set version to 0.8.24 --- CMakeLists.txt | 2 +- Changelog.md | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 298df418cd11..f6a905e39aa8 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.23") +set(PROJECT_VERSION "0.8.24") # 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 ef5416fb3dcb..01a77ce17f7a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,14 @@ +### 0.8.24 (unreleased) + +Language Features: + + +Compiler Features: + + +Bugfixes: + + ### 0.8.23 (2023-11-08) Important Bugfixes: From 35b5ebd2109ad0068a1c8c6c7f599e5c999c9800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 9 Nov 2023 17:03:03 +0100 Subject: [PATCH 017/189] prepare_report.js: Add the --report-file option to avoid printing the report to stdout --- .circleci/parallel_bytecode_report.sh | 2 +- scripts/bytecodecompare/prepare_report.js | 23 ++++++++++++++++--- .../bytecode_reports_for_modified_binaries.sh | 2 +- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/.circleci/parallel_bytecode_report.sh b/.circleci/parallel_bytecode_report.sh index 4a19015ce62f..9b316266a21e 100755 --- a/.circleci/parallel_bytecode_report.sh +++ b/.circleci/parallel_bytecode_report.sh @@ -75,5 +75,5 @@ else # shellcheck disable=SC2035 ./prepare_report.js \ --preset "$preset" \ - *.sol > "../bytecode-report-${label}-${preset}.txt" + *.sol --report-file "../bytecode-report-${label}-${preset}.txt" fi diff --git a/scripts/bytecodecompare/prepare_report.js b/scripts/bytecodecompare/prepare_report.js index cba7a0800116..f1db9f93fb9d 100755 --- a/scripts/bytecodecompare/prepare_report.js +++ b/scripts/bytecodecompare/prepare_report.js @@ -32,6 +32,7 @@ function cleanString(string) let inputFiles = [] let stripSMTPragmas = false let presets = [] +let reportFilePath = undefined for (let i = 2; i < process.argv.length; ++i) { @@ -45,10 +46,26 @@ for (let i = 2; i < process.argv.length; ++i) presets.push(process.argv[i + 1]) ++i; } + else if (process.argv[i] === '--report-file') + { + if (reportFilePath !== undefined) + throw Error("Option --report-file was specified multiple times.") + + if (i + 1 === process.argv.length) + throw Error("Option --report-file was used, but no file name given.") + + reportFilePath = process.argv[i + 1] + ++i; + } else inputFiles.push(process.argv[i]) } +if (reportFilePath === undefined) + throw Error("Use --report-file option to specify the report file path.") + +let reportFile = fs.createWriteStream(reportFilePath, {flags : 'w'}); + if (presets.length === 0) presets = ['legacy-no-optimize', 'legacy-optimize'] @@ -114,7 +131,7 @@ for (const preset of presets) Object.keys(result['contracts']).every(file => Object.keys(result['contracts'][file]).length === 0) ) // NOTE: do not exit here because this may be run on source which cannot be compiled - console.log(filename + ': ') + reportFile.write(filename + ': \n') else for (const contractFile in result['contracts']) for (const contractName in result['contracts'][contractFile]) @@ -135,8 +152,8 @@ for (const preset of presets) if ('metadata' in contractResults && cleanString(contractResults.metadata) !== undefined) metadata = contractResults.metadata - console.log(filename + ':' + contractName + ' ' + bytecode) - console.log(filename + ':' + contractName + ' ' + metadata) + reportFile.write(filename + ':' + contractName + ' ' + bytecode + '\n') + reportFile.write(filename + ':' + contractName + ' ' + metadata + '\n') } } } diff --git a/scripts/solc-bin/bytecode_reports_for_modified_binaries.sh b/scripts/solc-bin/bytecode_reports_for_modified_binaries.sh index 92c522f8f555..bd6251913243 100755 --- a/scripts/solc-bin/bytecode_reports_for_modified_binaries.sh +++ b/scripts/solc-bin/bytecode_reports_for_modified_binaries.sh @@ -160,7 +160,7 @@ for binary_name in $platform_binaries; do "$solidity_version_and_commit" # shellcheck disable=SC2035 - ./prepare_report.js --strip-smt-pragmas *.sol > "${report_dir}/report-${binary_name}.txt" + ./prepare_report.js --strip-smt-pragmas *.sol --report-file "${report_dir}/report-${binary_name}.txt" else yul_optimizer_flags=() if [[ $solidity_version == 0.6.0 ]] || [[ $solidity_version == 0.6.1 ]]; then From d668330f9768afc69b1067b3a7115af338835047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 9 Nov 2023 19:22:38 +0100 Subject: [PATCH 018/189] prepare_report.js: Show script progress on stdout with dots --- .circleci/config.yml | 1 - scripts/bytecodecompare/prepare_report.js | 15 ++++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7ec500e2e823..4b793865d323 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -88,7 +88,6 @@ commands: steps: - run: name: Generate bytecode reports for the selected preset - no_output_timeout: 30m command: | .circleci/parallel_bytecode_report.sh \ "<< parameters.label >>" \ diff --git a/scripts/bytecodecompare/prepare_report.js b/scripts/bytecodecompare/prepare_report.js index f1db9f93fb9d..821b5cb750eb 100755 --- a/scripts/bytecodecompare/prepare_report.js +++ b/scripts/bytecodecompare/prepare_report.js @@ -130,8 +130,11 @@ for (const preset of presets) Object.keys(result['contracts']).length === 0 || Object.keys(result['contracts']).every(file => Object.keys(result['contracts'][file]).length === 0) ) + { // NOTE: do not exit here because this may be run on source which cannot be compiled reportFile.write(filename + ': \n') + process.stdout.write('E') + } else for (const contractFile in result['contracts']) for (const contractName in result['contracts'][contractFile]) @@ -140,6 +143,12 @@ for (const preset of presets) let bytecode = '' let metadata = '' + let progressIndicator = '.' + + if ('metadata' in contractResults && cleanString(contractResults.metadata) !== undefined) + metadata = contractResults.metadata + else + progressIndicator = 'M' if ( 'evm' in contractResults && @@ -148,12 +157,12 @@ for (const preset of presets) cleanString(contractResults.evm.bytecode.object) !== undefined ) bytecode = cleanString(contractResults.evm.bytecode.object) - - if ('metadata' in contractResults && cleanString(contractResults.metadata) !== undefined) - metadata = contractResults.metadata + else + progressIndicator = 'B' reportFile.write(filename + ':' + contractName + ' ' + bytecode + '\n') reportFile.write(filename + ':' + contractName + ' ' + metadata + '\n') + process.stdout.write(progressIndicator) } } } From 103e39706aeaed3fcd27e8fef0ce5d73e612e52e Mon Sep 17 00:00:00 2001 From: Jitendra Kumar <76531339+jitendragangwar123@users.noreply.github.com> Date: Thu, 23 Nov 2023 17:35:11 +0530 Subject: [PATCH 019/189] fix:typo errors --- docs/yul.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/yul.rst b/docs/yul.rst index cf47c1e877fc..0e50d137761c 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -1258,7 +1258,7 @@ In Solidity mode, the Yul optimizer is activated together with the regular optim Optimization Step Sequence -------------------------- -Detailed information regrading the optimization sequence as well a list of abbreviations is +Detailed information regarding the optimization sequence as well as a list of abbreviations is available in the :ref:`optimizer docs `. .. _erc20yul: From 158330ba8ce64054d1bb14eb6f731fef7c30ca1c Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Thu, 26 Oct 2023 00:27:28 -0300 Subject: [PATCH 020/189] Update optimizer docs to include codegen-based optimizer section --- docs/internals/optimizer.rst | 81 ++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index 15a25f960003..70896333c2bc 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -5,8 +5,11 @@ The Optimizer ************* -The Solidity compiler uses two different optimizer modules: The "old" optimizer -that operates at the opcode level and the "new" optimizer that operates on Yul IR code. +The Solidity compiler involves optimizations at three different levels (in order of execution): + +- Optimizations during code generation based on a direct analysis of Solidity code. +- Optimizing transformations on the Yul IR code. +- Optimizations at the opcode level. The opcode-based optimizer applies a set of `simplification rules `_ to opcodes. It also combines equal code sets and removes unused code. @@ -20,6 +23,17 @@ the function calls. Similarly, if a function is side-effect free and its result is multiplied by zero, you can remove the function call completely. +The codegen-based optimizer affects the initial low-level code produced from the Solidity input. +In the legacy pipeline, the bytecode is generated immediately and most of the optimizations of this +kind are implicit and not configurable, the only exception being an optimization which changes the +order of literals in binary operations. +The IR-based pipeline takes a different approach and produces Yul IR closely matching the structure +of the Solidity code, with nearly all optimizations deferred to the Yul optimizer module. +In that case codegen-level optimization is done only in very limited cases which are difficult to +handle in Yul IR, but are straightforward with the high-level information from analysis phase at hand. +An example of such an optimization is the bypass of checked arithmetic when incrementing the counter +in certain idiomatic ``for`` loops. + Currently, the parameter ``--optimize`` activates the opcode-based optimizer for the generated bytecode and the Yul optimizer for the Yul code generated internally, for example for ABI coder v2. One can use ``solc --ir-optimized --optimize`` to produce an @@ -27,7 +41,8 @@ optimized Yul IR for a Solidity source. Similarly, one can use ``solc --strict-a for a stand-alone Yul mode. .. note:: - The `peephole optimizer `_ is always + Some optimizer steps, such as, for example, the `peephole optimizer `_ + and the :ref:`unchecked loop increment optimizer ` are always enabled by default and can only be turned off via the :ref:`Standard JSON `. .. note:: @@ -1388,3 +1403,63 @@ into } The LiteralRematerialiser should be run before this step. + +Codegen-Based Optimizer Module +============================== + +Currently, the codegen-based optimizer module provides two optimizations. + +The first one, available in the legacy code generator, moves literals to the right side of +commutative binary operators, which helps exploit their associativity. + +The other one, available in the IR-based code generator, enables the use of unchecked arithmetic +when generating code for incrementing the counter variable of certain idiomatic ``for`` loops. +This avoids wasting gas by identifying some conditions that guarantee that the counter variable +cannot overflow. +This eliminates the need to use a verbose unchecked arithmetic block inside the loop body to +increment the counter variable. + +.. _unchecked-loop-optimizer: + +Unchecked Loop Increment +------------------------ + +Introduced in Solidity ``0.8.22``, the overflow check optimization step is concerned with identifying +the conditions under which the ``for`` loop counter can be safely incremented +without overflow checks. + +This optimization is **only** applied to ``for`` loops of the general form: + +.. code-block:: solidity + + for (uint i = X; i < Y; ++i) { + // variable i is not modified in the loop body + } + +The condition and the fact that the counter variable is only ever incremented +guarantee that it never overflows. +The precise requirements for the loop to be eligible for the optimization are as follows: + +- The loop condition is a comparison of the form ``i < Y``, for a local counter variable ``i`` + (called the "loop counter" hereon) and an expression ``Y``. +- The built-in operator ``<`` is necessarily used in the loop condition and is the only operator + that triggers the optimization. ``<=`` and the like are intentionally excluded. Additionally, + user-defined operators are **not** eligible. +- The loop expression is a prefix or postfix increment of the counter variable, i.e, ``i++`` or ``++i``. +- The loop counter is a local variable of a built-in integer type. +- The loop counter is **not** modified by the loop body or by the expression used as the loop condition. +- The comparison is performed on the same type as the loop counter, meaning that the type of the + right-hand-side expression is implicitly convertible to the type of the counter, such that the latter + is not implicitly widened before the comparison. + +To clarify the last condition, consider the following example: + +.. code-block:: solidity + + for (uint8 i = 0; i < uint16(1000); i++) { + // ... + } + +In this case, the counter ``i`` has its type implicitly converted from ``uint8`` +to ``uint16`` before the comparison and the condition is in fact never false, so +the overflow check for the increment cannot be removed. From 15afdc38aef5853e2ce1cda72371baeb12bad19f Mon Sep 17 00:00:00 2001 From: r0qs Date: Mon, 27 Nov 2023 14:30:45 -0300 Subject: [PATCH 021/189] Switch from yarn to pnpm for hardhat external tests --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4b793865d323..1570704bdc47 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1326,16 +1326,16 @@ jobs: name: Install dependencies command: | cd hardhat - yarn + pnpm install - run: name: Run hardhat-core test suite command: | HARDHAT_TESTS_SOLC_VERSION=$(scripts/get_version.sh) export HARDHAT_TESTS_SOLC_VERSION - # NOTE: This is expected to work without running `yarn build` first. + # NOTE: This is expected to work without running `pnpm build` first. cd hardhat/packages/hardhat-core - yarn test + pnpm test - matrix_notify_failure_unless_pr t_ext: From 435f38942192d077fd3b2666714590e9d2152fe2 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Tue, 28 Nov 2023 14:43:47 +0100 Subject: [PATCH 022/189] Fix docutils deprecated set_class method --- docs/ext/remix_code_links.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ext/remix_code_links.py b/docs/ext/remix_code_links.py index 31a5667689b8..55fc0ef5c26f 100644 --- a/docs/ext/remix_code_links.py +++ b/docs/ext/remix_code_links.py @@ -27,10 +27,10 @@ def remix_code_url(source_code, language, solidity_version): def build_remix_link_node(url): reference_node = docutils.nodes.reference('', 'open in Remix', internal=False, refuri=url, target='_blank') - reference_node.set_class('remix-link') + reference_node['classes'].append('remix-link') paragraph_node = docutils.nodes.paragraph() - paragraph_node.set_class('remix-link-container') + paragraph_node['classes'].append('remix-link-container') paragraph_node.append(reference_node) return paragraph_node From 1ad3890e64d6055fbcbaa4fc4ec15c198008d518 Mon Sep 17 00:00:00 2001 From: xiaolou86 Date: Wed, 11 Oct 2023 17:09:58 +0800 Subject: [PATCH 023/189] Fix typos --- test/compilationTests/corion/premium.sol | 6 +++--- test/compilationTests/corion/token.sol | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/compilationTests/corion/premium.sol b/test/compilationTests/corion/premium.sol index 8603b959006b..8dbe76212bd5 100644 --- a/test/compilationTests/corion/premium.sol +++ b/test/compilationTests/corion/premium.sol @@ -202,11 +202,11 @@ contract premium is module, safeMath { @success If the function was successful. */ if ( from != msg.sender ) { - (bool _success, uint256 _reamining, uint256 _nonce) = db.getAllowance(from, msg.sender); + (bool _success, uint256 _remaining, uint256 _nonce) = db.getAllowance(from, msg.sender); require( _success ); - _reamining = safeSub(_reamining, amount); + _remaining = safeSub(_remaining, amount); _nonce = safeAdd(_nonce, 1); - require( db.setAllowance(from, msg.sender, _reamining, _nonce) ); + require( db.setAllowance(from, msg.sender, _remaining, _nonce) ); emit AllowanceUsed(msg.sender, from, amount); } bytes memory _data; diff --git a/test/compilationTests/corion/token.sol b/test/compilationTests/corion/token.sol index 58c3e8cb4033..51c93f120be4 100644 --- a/test/compilationTests/corion/token.sol +++ b/test/compilationTests/corion/token.sol @@ -218,11 +218,11 @@ contract token is safeMath, module, announcementTypes { @success Was the Function successful? */ if ( from != msg.sender ) { - (bool _success, uint256 _reamining, uint256 _nonce) = db.getAllowance(from, msg.sender); + (bool _success, uint256 _remaining, uint256 _nonce) = db.getAllowance(from, msg.sender); require( _success ); - _reamining = safeSub(_reamining, amount); + _remaining = safeSub(_remaining, amount); _nonce = safeAdd(_nonce, 1); - require( db.setAllowance(from, msg.sender, _reamining, _nonce) ); + require( db.setAllowance(from, msg.sender, _remaining, _nonce) ); emit AllowanceUsed(msg.sender, from, amount); } bytes memory _data; From 4c628294b4dc935d43c093eecf09f423f54ed714 Mon Sep 17 00:00:00 2001 From: Vishwa Mehta Date: Wed, 29 Nov 2023 19:53:07 +0530 Subject: [PATCH 024/189] Update contributing.rst Updated breaking link to the team call on Jitsi. --- docs/contributing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 7f444871d546..ac4e2375ebdc 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -32,7 +32,7 @@ the team and contributors are working on, you can join our public team call: - Wednesdays at 3PM CET/CEST. -The call takes place on `Jitsi `_. +The call takes place on `Jitsi `_. How to Report Issues ==================== From 1247ff490b71fdbbc7503fba47536a1e5bc0f239 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Sat, 28 Oct 2023 18:31:49 -0300 Subject: [PATCH 025/189] Assert compiler outputs not generated when experimental Solidity is used --- libsolidity/interface/CompilerStack.cpp | 42 ++++++++++++++++++++++++- libsolidity/interface/CompilerStack.h | 2 ++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index eb0ed3550036..49e02c5ec7d8 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -86,6 +86,7 @@ #include +#include #include #include @@ -453,7 +454,7 @@ bool CompilerStack::analyze() try { - bool experimentalSolidity = !m_sourceOrder.empty() && m_sourceOrder.front()->ast->experimentalSolidity(); + bool experimentalSolidity = isExperimentalSolidity(); SyntaxChecker syntaxChecker(m_errorReporter, m_optimiserSettings.runYulOptimiser); for (Source const* source: m_sourceOrder) @@ -923,6 +924,8 @@ std::string const& CompilerStack::yulIR(std::string const& _contractName) const if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); + solAssert(!isExperimentalSolidity()); + return contract(_contractName).yulIR; } @@ -931,6 +934,8 @@ Json::Value const& CompilerStack::yulIRAst(std::string const& _contractName) con if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); + solAssert(!isExperimentalSolidity()); + return contract(_contractName).yulIRAst; } @@ -939,6 +944,8 @@ std::string const& CompilerStack::yulIROptimized(std::string const& _contractNam if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); + solAssert(!isExperimentalSolidity()); + return contract(_contractName).yulIROptimized; } @@ -947,6 +954,8 @@ Json::Value const& CompilerStack::yulIROptimizedAst(std::string const& _contract if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); + solAssert(!isExperimentalSolidity()); + return contract(_contractName).yulIROptimizedAst; } @@ -955,6 +964,8 @@ evmasm::LinkerObject const& CompilerStack::object(std::string const& _contractNa if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); + solAssert(!isExperimentalSolidity()); + return contract(_contractName).object; } @@ -963,6 +974,8 @@ evmasm::LinkerObject const& CompilerStack::runtimeObject(std::string const& _con if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); + solAssert(!isExperimentalSolidity()); + return contract(_contractName).runtimeObject; } @@ -972,6 +985,8 @@ std::string CompilerStack::assemblyString(std::string const& _contractName, Stri if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); + solAssert(!isExperimentalSolidity()); + Contract const& currentContract = contract(_contractName); if (currentContract.evmAssembly) return currentContract.evmAssembly->assemblyString(m_debugInfoSelection, _sourceCodes); @@ -985,6 +1000,8 @@ Json::Value CompilerStack::assemblyJSON(std::string const& _contractName) const if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); + solAssert(!isExperimentalSolidity()); + Contract const& currentContract = contract(_contractName); if (currentContract.evmAssembly) return currentContract.evmAssembly->assemblyJSON(sourceIndices()); @@ -1022,6 +1039,7 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const solThrow(CompilerError, "Analysis was not successful."); solAssert(_contract.contract, ""); + solAssert(!isExperimentalSolidity()); return _contract.abi.init([&]{ return ABI::generate(*_contract.contract); }); } @@ -1040,6 +1058,7 @@ Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const solThrow(CompilerError, "Analysis was not successful."); solAssert(_contract.contract, ""); + solAssert(!isExperimentalSolidity()); return _contract.storageLayout.init([&]{ return StorageLayout().generate(*_contract.contract); }); } @@ -1058,6 +1077,7 @@ Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const solThrow(CompilerError, "Analysis was not successful."); solAssert(_contract.contract, ""); + solAssert(!isExperimentalSolidity()); return _contract.userDocumentation.init([&]{ return Natspec::userDocumentation(*_contract.contract); }); } @@ -1077,6 +1097,8 @@ Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const solAssert(_contract.contract, ""); + solAssert(!isExperimentalSolidity()); + return _contract.devDocumentation.init([&]{ return Natspec::devDocumentation(*_contract.contract); }); } @@ -1085,6 +1107,8 @@ Json::Value CompilerStack::interfaceSymbols(std::string const& _contractName) co if (m_stackState < AnalysisSuccessful) solThrow(CompilerError, "Analysis was not successful."); + solAssert(!isExperimentalSolidity()); + Json::Value interfaceSymbols(Json::objectValue); // Always have a methods object interfaceSymbols["methods"] = Json::objectValue; @@ -1125,6 +1149,8 @@ std::string const& CompilerStack::metadata(Contract const& _contract) const solAssert(_contract.contract, ""); + solAssert(!isExperimentalSolidity()); + return _contract.metadata.init([&]{ return createMetadata(_contract, m_viaIR); }); } @@ -1145,6 +1171,8 @@ SourceUnit const& CompilerStack::ast(std::string const& _sourceName) const if (!source(_sourceName).ast) solThrow(CompilerError, "Parsing was not successful."); + solAssert(!isExperimentalSolidity()); + return *source(_sourceName).ast; } @@ -1867,6 +1895,8 @@ Json::Value CompilerStack::gasEstimates(std::string const& _contractName) const if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); + solAssert(!isExperimentalSolidity()); + if (!assemblyItems(_contractName) && !runtimeAssemblyItems(_contractName)) return Json::Value(); @@ -1938,3 +1968,13 @@ Json::Value CompilerStack::gasEstimates(std::string const& _contractName) const return output; } + +bool CompilerStack::isExperimentalSolidity() const +{ + return + !m_sourceOrder.empty() && + // TODO: Does it make sense to check for any source or just the front? + //m_sourceOrder.front()->ast->experimentalSolidity() + ranges::all_of(m_sourceOrder, [](auto const* _source) { return _source->ast->experimentalSolidity(); } ) + ; +} diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 929e7e54f49a..0ccb33219e54 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -348,6 +348,8 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac /// Changes the format of the metadata appended at the end of the bytecode. void setMetadataFormat(MetadataFormat _metadataFormat) { m_metadataFormat = _metadataFormat; } + bool isExperimentalSolidity() const; + static MetadataFormat defaultMetadataFormat() { return VersionIsRelease ? MetadataFormat::WithReleaseVersionTag : MetadataFormat::WithPrereleaseVersionTag; From 7006535c0d20116524808f0112bf13d7917f1536 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Wed, 29 Nov 2023 19:18:44 -0300 Subject: [PATCH 026/189] [FIXUP] Remove TODO comment --- libsolidity/interface/CompilerStack.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 49e02c5ec7d8..7a52a240d935 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -1973,8 +1973,6 @@ bool CompilerStack::isExperimentalSolidity() const { return !m_sourceOrder.empty() && - // TODO: Does it make sense to check for any source or just the front? - //m_sourceOrder.front()->ast->experimentalSolidity() ranges::all_of(m_sourceOrder, [](auto const* _source) { return _source->ast->experimentalSolidity(); } ) ; } From 9b09afefeaaf85f8277ec971ee54d2e4d7ebddc9 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Wed, 29 Nov 2023 18:10:09 -0300 Subject: [PATCH 027/189] [REFACTOR] Use solUnimplementedAssert instead of solAssert --- libsolidity/interface/CompilerStack.cpp | 32 ++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 7a52a240d935..d0c9587a5f65 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -924,7 +924,7 @@ std::string const& CompilerStack::yulIR(std::string const& _contractName) const if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); - solAssert(!isExperimentalSolidity()); + solUnimplementedAssert(!isExperimentalSolidity()); return contract(_contractName).yulIR; } @@ -934,7 +934,7 @@ Json::Value const& CompilerStack::yulIRAst(std::string const& _contractName) con if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); - solAssert(!isExperimentalSolidity()); + solUnimplementedAssert(!isExperimentalSolidity()); return contract(_contractName).yulIRAst; } @@ -944,7 +944,7 @@ std::string const& CompilerStack::yulIROptimized(std::string const& _contractNam if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); - solAssert(!isExperimentalSolidity()); + solUnimplementedAssert(!isExperimentalSolidity()); return contract(_contractName).yulIROptimized; } @@ -954,7 +954,7 @@ Json::Value const& CompilerStack::yulIROptimizedAst(std::string const& _contract if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); - solAssert(!isExperimentalSolidity()); + solUnimplementedAssert(!isExperimentalSolidity()); return contract(_contractName).yulIROptimizedAst; } @@ -964,7 +964,7 @@ evmasm::LinkerObject const& CompilerStack::object(std::string const& _contractNa if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); - solAssert(!isExperimentalSolidity()); + solUnimplementedAssert(!isExperimentalSolidity()); return contract(_contractName).object; } @@ -974,7 +974,7 @@ evmasm::LinkerObject const& CompilerStack::runtimeObject(std::string const& _con if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); - solAssert(!isExperimentalSolidity()); + solUnimplementedAssert(!isExperimentalSolidity()); return contract(_contractName).runtimeObject; } @@ -985,7 +985,7 @@ std::string CompilerStack::assemblyString(std::string const& _contractName, Stri if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); - solAssert(!isExperimentalSolidity()); + solUnimplementedAssert(!isExperimentalSolidity()); Contract const& currentContract = contract(_contractName); if (currentContract.evmAssembly) @@ -1000,7 +1000,7 @@ Json::Value CompilerStack::assemblyJSON(std::string const& _contractName) const if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); - solAssert(!isExperimentalSolidity()); + solUnimplementedAssert(!isExperimentalSolidity()); Contract const& currentContract = contract(_contractName); if (currentContract.evmAssembly) @@ -1039,7 +1039,7 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const solThrow(CompilerError, "Analysis was not successful."); solAssert(_contract.contract, ""); - solAssert(!isExperimentalSolidity()); + solUnimplementedAssert(!isExperimentalSolidity()); return _contract.abi.init([&]{ return ABI::generate(*_contract.contract); }); } @@ -1058,7 +1058,7 @@ Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const solThrow(CompilerError, "Analysis was not successful."); solAssert(_contract.contract, ""); - solAssert(!isExperimentalSolidity()); + solUnimplementedAssert(!isExperimentalSolidity()); return _contract.storageLayout.init([&]{ return StorageLayout().generate(*_contract.contract); }); } @@ -1077,7 +1077,7 @@ Json::Value const& CompilerStack::natspecUser(Contract const& _contract) const solThrow(CompilerError, "Analysis was not successful."); solAssert(_contract.contract, ""); - solAssert(!isExperimentalSolidity()); + solUnimplementedAssert(!isExperimentalSolidity()); return _contract.userDocumentation.init([&]{ return Natspec::userDocumentation(*_contract.contract); }); } @@ -1097,7 +1097,7 @@ Json::Value const& CompilerStack::natspecDev(Contract const& _contract) const solAssert(_contract.contract, ""); - solAssert(!isExperimentalSolidity()); + solUnimplementedAssert(!isExperimentalSolidity()); return _contract.devDocumentation.init([&]{ return Natspec::devDocumentation(*_contract.contract); }); } @@ -1107,7 +1107,7 @@ Json::Value CompilerStack::interfaceSymbols(std::string const& _contractName) co if (m_stackState < AnalysisSuccessful) solThrow(CompilerError, "Analysis was not successful."); - solAssert(!isExperimentalSolidity()); + solUnimplementedAssert(!isExperimentalSolidity()); Json::Value interfaceSymbols(Json::objectValue); // Always have a methods object @@ -1149,7 +1149,7 @@ std::string const& CompilerStack::metadata(Contract const& _contract) const solAssert(_contract.contract, ""); - solAssert(!isExperimentalSolidity()); + solUnimplementedAssert(!isExperimentalSolidity()); return _contract.metadata.init([&]{ return createMetadata(_contract, m_viaIR); }); } @@ -1171,7 +1171,7 @@ SourceUnit const& CompilerStack::ast(std::string const& _sourceName) const if (!source(_sourceName).ast) solThrow(CompilerError, "Parsing was not successful."); - solAssert(!isExperimentalSolidity()); + solUnimplementedAssert(!isExperimentalSolidity()); return *source(_sourceName).ast; } @@ -1895,7 +1895,7 @@ Json::Value CompilerStack::gasEstimates(std::string const& _contractName) const if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); - solAssert(!isExperimentalSolidity()); + solUnimplementedAssert(!isExperimentalSolidity()); if (!assemblyItems(_contractName) && !runtimeAssemblyItems(_contractName)) return Json::Value(); From fc65c0c6087dbc3564b8a11800140b56b4b3b593 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Wed, 29 Nov 2023 19:16:56 -0300 Subject: [PATCH 028/189] Assert import AST --- libsolidity/interface/CompilerStack.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index d0c9587a5f65..058151928b2a 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -422,6 +422,7 @@ void CompilerStack::importASTs(std::map const& _source std::map> reconstructedSources = ASTJsonImporter(m_evmVersion).jsonToSourceUnit(_sources); for (auto& src: reconstructedSources) { + solUnimplementedAssert(!src.second->experimentalSolidity()); std::string const& path = src.first; Source source; source.ast = src.second; From e43d1052df1046d545bfe934dc3e9cf80ec81f49 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 30 Nov 2023 15:15:30 +0100 Subject: [PATCH 029/189] Purge using namespace from test/libyul --- scripts/check_style.sh | 1 + test/libyul/Common.cpp | 27 ++- test/libyul/CompilabilityChecker.cpp | 32 ++-- test/libyul/ControlFlowGraphTest.cpp | 7 +- test/libyul/ControlFlowSideEffectsTest.cpp | 13 +- test/libyul/EVMCodeTransformTest.cpp | 11 +- test/libyul/FunctionSideEffects.cpp | 13 +- test/libyul/Inliner.cpp | 5 +- test/libyul/KnowledgeBaseTest.cpp | 9 +- test/libyul/Metrics.cpp | 5 +- test/libyul/ObjectCompilerTest.cpp | 9 +- test/libyul/ObjectParser.cpp | 26 +-- test/libyul/Parser.cpp | 183 ++++++++++----------- test/libyul/StackLayoutGeneratorTest.cpp | 7 +- test/libyul/StackShufflingTest.cpp | 41 +++-- test/libyul/SyntaxTest.cpp | 11 +- test/libyul/YulInterpreterTest.cpp | 13 +- test/libyul/YulOptimizerTest.cpp | 23 ++- test/libyul/YulOptimizerTestCommon.cpp | 17 +- 19 files changed, 218 insertions(+), 235 deletions(-) diff --git a/scripts/check_style.sh b/scripts/check_style.sh index 22ad325368d3..8ade99148f6f 100755 --- a/scripts/check_style.sh +++ b/scripts/check_style.sh @@ -46,6 +46,7 @@ NAMESPACE_STD_FREE_FILES=( test/libsolidity/analysis/* test/libsolidity/interface/* test/libsolidity/util/* + test/libyul/* ) ( diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp index f10414a9f7a1..10d7d12adddd 100644 --- a/test/libyul/Common.cpp +++ b/test/libyul/Common.cpp @@ -38,7 +38,6 @@ #include -using namespace std; using namespace solidity; using namespace solidity::yul; using namespace solidity::langutil; @@ -51,7 +50,7 @@ Dialect const& defaultDialect(bool _yul) } } -pair, shared_ptr> yul::test::parse(string const& _source, bool _yul) +std::pair, std::shared_ptr> yul::test::parse(std::string const& _source, bool _yul) { YulStack stack( solidity::test::CommonOptions::get().evmVersion(), @@ -67,21 +66,21 @@ pair, shared_ptr> yul::test::parse(strin return make_pair(stack.parserResult()->code, stack.parserResult()->analysisInfo); } -pair, shared_ptr> yul::test::parse( - string const& _source, +std::pair, std::shared_ptr> yul::test::parse( + std::string const& _source, Dialect const& _dialect, ErrorList& _errors ) { ErrorReporter errorReporter(_errors); CharStream stream(_source, ""); - shared_ptr scanner = make_shared(stream); - shared_ptr parserResult = yul::ObjectParser(errorReporter, _dialect).parse(scanner, false); + std::shared_ptr scanner = std::make_shared(stream); + std::shared_ptr parserResult = yul::ObjectParser(errorReporter, _dialect).parse(scanner, false); if (!parserResult) return {}; if (!parserResult->code || errorReporter.hasErrors()) return {}; - shared_ptr analysisInfo = make_shared(); + std::shared_ptr analysisInfo = std::make_shared(); AsmAnalyzer analyzer(*analysisInfo, errorReporter, _dialect, {}, parserResult->qualifiedDataNames()); // TODO this should be done recursively. if (!analyzer.analyze(*parserResult->code) || errorReporter.hasErrors()) @@ -89,20 +88,20 @@ pair, shared_ptr> yul::test::parse( return {std::move(parserResult), std::move(analysisInfo)}; } -yul::Block yul::test::disambiguate(string const& _source, bool _yul) +yul::Block yul::test::disambiguate(std::string const& _source, bool _yul) { auto result = parse(_source, _yul); return std::get(Disambiguator(defaultDialect(_yul), *result.second, {})(*result.first)); } -string yul::test::format(string const& _source, bool _yul) +std::string yul::test::format(std::string const& _source, bool _yul) { return yul::AsmPrinter()(*parse(_source, _yul).first); } namespace { -std::map const validDialects = { +std::map const validDialects = { { "evm", [](langutil::EVMVersion _evmVersion) -> yul::Dialect const& @@ -120,10 +119,10 @@ std::map const vali } }; -vector validDialectNames() + std::vector validDialectNames() { - vector names{size(validDialects), ""}; - transform(begin(validDialects), end(validDialects), names.begin(), [](auto const& dialect) { return dialect.first; }); + std::vector names{size(validDialects), ""}; + std::transform(begin(validDialects), end(validDialects), names.begin(), [](auto const& dialect) { return dialect.first; }); return names; } @@ -132,7 +131,7 @@ vector validDialectNames() yul::Dialect const& yul::test::dialect(std::string const& _name, langutil::EVMVersion _evmVersion) { if (!validDialects.count(_name)) - BOOST_THROW_EXCEPTION(runtime_error{ + BOOST_THROW_EXCEPTION(std::runtime_error{ "Invalid Dialect \"" + _name + "\". Valid dialects are " + diff --git a/test/libyul/CompilabilityChecker.cpp b/test/libyul/CompilabilityChecker.cpp index b27f7ab54740..27310cd898c2 100644 --- a/test/libyul/CompilabilityChecker.cpp +++ b/test/libyul/CompilabilityChecker.cpp @@ -27,22 +27,20 @@ #include -using namespace std; - namespace solidity::yul::test { namespace { -string check(string const& _input) +std::string check(std::string const& _input) { Object obj; std::tie(obj.code, obj.analysisInfo) = yul::test::parse(_input, false); BOOST_REQUIRE(obj.code); auto functions = CompilabilityChecker(EVMDialect::strictAssemblyForEVM(solidity::test::CommonOptions::get().evmVersion()), obj, true).stackDeficit; - string out; + std::string out; for (auto const& function: functions) - out += function.first.str() + ": " + to_string(function.second) + " "; + out += function.first.str() + ": " + std::to_string(function.second) + " "; return out; } } @@ -51,19 +49,19 @@ BOOST_AUTO_TEST_SUITE(CompilabilityChecker) BOOST_AUTO_TEST_CASE(smoke_test) { - string out = check("{}"); + std::string out = check("{}"); BOOST_CHECK_EQUAL(out, ""); } BOOST_AUTO_TEST_CASE(simple_function) { - string out = check("{ function f(a, b) -> x, y { x := a y := b } }"); + std::string out = check("{ function f(a, b) -> x, y { x := a y := b } }"); BOOST_CHECK_EQUAL(out, ""); } BOOST_AUTO_TEST_CASE(many_variables_few_uses) { - string out = check(R"({ + std::string out = check(R"({ function f(a, b) -> x, y { let r1 := 0 let r2 := 0 @@ -91,7 +89,7 @@ BOOST_AUTO_TEST_CASE(many_variables_few_uses) BOOST_AUTO_TEST_CASE(many_variables_many_uses) { - string out = check(R"({ + std::string out = check(R"({ function f(a, b) -> x, y { let r1 := 0 let r2 := 0 @@ -119,7 +117,7 @@ BOOST_AUTO_TEST_CASE(many_variables_many_uses) BOOST_AUTO_TEST_CASE(many_return_variables_unused_arguments) { - string out = check(R"({ + std::string out = check(R"({ function f(a, b) -> r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19 { } })"); @@ -128,7 +126,7 @@ BOOST_AUTO_TEST_CASE(many_return_variables_unused_arguments) BOOST_AUTO_TEST_CASE(many_return_variables_used_arguments) { - string out = check(R"({ + std::string out = check(R"({ function f(a, b) -> r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19 { r1 := 0 sstore(a, b) @@ -139,7 +137,7 @@ BOOST_AUTO_TEST_CASE(many_return_variables_used_arguments) BOOST_AUTO_TEST_CASE(multiple_functions_used_arguments) { - string out = check(R"({ + std::string out = check(R"({ function f(a, b) -> r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19 { r1 := 0 sstore(a, b) @@ -175,7 +173,7 @@ BOOST_AUTO_TEST_CASE(multiple_functions_used_arguments) BOOST_AUTO_TEST_CASE(multiple_functions_unused_arguments) { - string out = check(R"({ + std::string out = check(R"({ function f(a, b) -> r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19 { } function g(r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19) -> x, y { @@ -207,7 +205,7 @@ BOOST_AUTO_TEST_CASE(multiple_functions_unused_arguments) BOOST_AUTO_TEST_CASE(nested_used_arguments) { - string out = check(R"({ + std::string out = check(R"({ function h(x) { let r1 := 0 let r2 := 0 @@ -244,7 +242,7 @@ BOOST_AUTO_TEST_CASE(nested_used_arguments) BOOST_AUTO_TEST_CASE(nested_unused_arguments) { - string out = check(R"({ + std::string out = check(R"({ function h(x) { let r1 := 0 let r2 := 0 @@ -277,7 +275,7 @@ BOOST_AUTO_TEST_CASE(nested_unused_arguments) BOOST_AUTO_TEST_CASE(also_in_outer_block_used_arguments) { - string out = check(R"({ + std::string out = check(R"({ let x := 0 let r1 := 0 let r2 := 0 @@ -308,7 +306,7 @@ BOOST_AUTO_TEST_CASE(also_in_outer_block_used_arguments) BOOST_AUTO_TEST_CASE(also_in_outer_block_unused_arguments) { - string out = check(R"({ + std::string out = check(R"({ let x := 0 let r1 := 0 let r2 := 0 diff --git a/test/libyul/ControlFlowGraphTest.cpp b/test/libyul/ControlFlowGraphTest.cpp index 44fff4219fb4..9a0e6f092ff7 100644 --- a/test/libyul/ControlFlowGraphTest.cpp +++ b/test/libyul/ControlFlowGraphTest.cpp @@ -39,9 +39,8 @@ using namespace solidity::yul; using namespace solidity::yul::test; using namespace solidity::frontend; using namespace solidity::frontend::test; -using namespace std; -ControlFlowGraphTest::ControlFlowGraphTest(string const& _filename): +ControlFlowGraphTest::ControlFlowGraphTest(std::string const& _filename): TestCase(_filename) { m_source = m_reader.source(); @@ -195,13 +194,13 @@ class ControlFlowGraphPrinter std::list m_blocksToPrint; }; -TestCase::TestResult ControlFlowGraphTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) +TestCase::TestResult ControlFlowGraphTest::run(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted) { ErrorList errors; auto [object, analysisInfo] = parse(m_source, *m_dialect, errors); if (!object || !analysisInfo || Error::containsErrors(errors)) { - AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; + AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << std::endl; return TestResult::FatalError; } diff --git a/test/libyul/ControlFlowSideEffectsTest.cpp b/test/libyul/ControlFlowSideEffectsTest.cpp index ce6c18c3588f..6ac5a7b0ad59 100644 --- a/test/libyul/ControlFlowSideEffectsTest.cpp +++ b/test/libyul/ControlFlowSideEffectsTest.cpp @@ -27,7 +27,6 @@ #include #include -using namespace std; using namespace solidity; using namespace solidity::yul; using namespace solidity::yul::test; @@ -35,9 +34,9 @@ using namespace solidity::frontend::test; namespace { -string toString(ControlFlowSideEffects const& _sideEffects) +std::string toString(ControlFlowSideEffects const& _sideEffects) { - vector r; + std::vector r; if (_sideEffects.canTerminate) r.emplace_back("can terminate"); if (_sideEffects.canRevert) @@ -48,19 +47,19 @@ string toString(ControlFlowSideEffects const& _sideEffects) } } -ControlFlowSideEffectsTest::ControlFlowSideEffectsTest(string const& _filename): +ControlFlowSideEffectsTest::ControlFlowSideEffectsTest(std::string const& _filename): TestCase(_filename) { m_source = m_reader.source(); m_expectation = m_reader.simpleExpectations(); } -TestCase::TestResult ControlFlowSideEffectsTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) +TestCase::TestResult ControlFlowSideEffectsTest::run(std::ostream& _stream, std::string const& _linePrefix, bool _formatted) { Object obj; std::tie(obj.code, obj.analysisInfo) = yul::test::parse(m_source, false); if (!obj.code) - BOOST_THROW_EXCEPTION(runtime_error("Parsing input failed.")); + BOOST_THROW_EXCEPTION(std::runtime_error("Parsing input failed.")); ControlFlowSideEffectsCollector sideEffects( EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()), @@ -68,7 +67,7 @@ TestCase::TestResult ControlFlowSideEffectsTest::run(ostream& _stream, string co ); m_obtainedResult.clear(); forEach(*obj.code, [&](FunctionDefinition const& _fun) { - string effectStr = toString(sideEffects.functionSideEffects().at(&_fun)); + std::string effectStr = toString(sideEffects.functionSideEffects().at(&_fun)); m_obtainedResult += _fun.name.str() + (effectStr.empty() ? ":" : ": " + effectStr) + "\n"; }); diff --git a/test/libyul/EVMCodeTransformTest.cpp b/test/libyul/EVMCodeTransformTest.cpp index 257893ad5f70..4c2eecab05fe 100644 --- a/test/libyul/EVMCodeTransformTest.cpp +++ b/test/libyul/EVMCodeTransformTest.cpp @@ -38,9 +38,8 @@ using namespace solidity::yul; using namespace solidity::yul::test; using namespace solidity::frontend; using namespace solidity::frontend::test; -using namespace std; -EVMCodeTransformTest::EVMCodeTransformTest(string const& _filename): +EVMCodeTransformTest::EVMCodeTransformTest(std::string const& _filename): EVMVersionRestrictedTestCase(_filename) { m_source = m_reader.source(); @@ -48,21 +47,21 @@ EVMCodeTransformTest::EVMCodeTransformTest(string const& _filename): m_expectation = m_reader.simpleExpectations(); } -TestCase::TestResult EVMCodeTransformTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) +TestCase::TestResult EVMCodeTransformTest::run(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted) { solidity::frontend::OptimiserSettings settings = solidity::frontend::OptimiserSettings::none(); settings.runYulOptimiser = false; settings.optimizeStackAllocation = m_stackOpt; YulStack stack( EVMVersion{}, - nullopt, + std::nullopt, YulStack::Language::StrictAssembly, settings, DebugInfoSelection::All() ); if (!stack.parseAndAnalyze("", m_source)) { - AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; + AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << std::endl; SourceReferenceFormatter{_stream, stack, true, false} .printErrorInformation(stack.errors()); return TestResult::FatalError; @@ -75,7 +74,7 @@ TestCase::TestResult EVMCodeTransformTest::run(ostream& _stream, string const& _ adapter, EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}), m_stackOpt, - nullopt + std::nullopt ); std::ostringstream output; diff --git a/test/libyul/FunctionSideEffects.cpp b/test/libyul/FunctionSideEffects.cpp index dd960448209e..a57ec9807843 100644 --- a/test/libyul/FunctionSideEffects.cpp +++ b/test/libyul/FunctionSideEffects.cpp @@ -40,13 +40,12 @@ using namespace solidity::yul; using namespace solidity::yul::test; using namespace solidity::frontend; using namespace solidity::frontend::test; -using namespace std; namespace { -string toString(SideEffects const& _sideEffects) +std::string toString(SideEffects const& _sideEffects) { - vector ret; + std::vector ret; if (_sideEffects.movable) ret.emplace_back("movable"); if (_sideEffects.movableApartFromEffects) @@ -74,21 +73,21 @@ string toString(SideEffects const& _sideEffects) } } -FunctionSideEffects::FunctionSideEffects(string const& _filename): +FunctionSideEffects::FunctionSideEffects(std::string const& _filename): TestCase(_filename) { m_source = m_reader.source(); m_expectation = m_reader.simpleExpectations(); } -TestCase::TestResult FunctionSideEffects::run(ostream& _stream, string const& _linePrefix, bool _formatted) +TestCase::TestResult FunctionSideEffects::run(std::ostream& _stream, std::string const& _linePrefix, bool _formatted) { Object obj; std::tie(obj.code, obj.analysisInfo) = yul::test::parse(m_source, false); if (!obj.code) - BOOST_THROW_EXCEPTION(runtime_error("Parsing input failed.")); + BOOST_THROW_EXCEPTION(std::runtime_error("Parsing input failed.")); - map functionSideEffects = SideEffectsPropagator::sideEffects( + std::map functionSideEffects = SideEffectsPropagator::sideEffects( EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()), CallGraphGenerator::callGraph(*obj.code) ); diff --git a/test/libyul/Inliner.cpp b/test/libyul/Inliner.cpp index 75ae064e5467..6f622b41a4f4 100644 --- a/test/libyul/Inliner.cpp +++ b/test/libyul/Inliner.cpp @@ -33,7 +33,6 @@ #include -using namespace std; using namespace solidity; using namespace solidity::util; using namespace solidity::yul; @@ -41,14 +40,14 @@ using namespace solidity::yul::test; namespace { -string inlinableFunctions(string const& _source) +std::string inlinableFunctions(std::string const& _source) { auto ast = disambiguate(_source); InlinableExpressionFunctionFinder funFinder; funFinder(ast); - vector functionNames; + std::vector functionNames; for (auto const& f: funFinder.inlinableFunctions()) functionNames.emplace_back(f.first.str()); return boost::algorithm::join(functionNames, ","); diff --git a/test/libyul/KnowledgeBaseTest.cpp b/test/libyul/KnowledgeBaseTest.cpp index 7cb5a8ae3c05..b1dd0e6d19c5 100644 --- a/test/libyul/KnowledgeBaseTest.cpp +++ b/test/libyul/KnowledgeBaseTest.cpp @@ -33,7 +33,6 @@ #include -using namespace std; using namespace solidity::langutil; namespace solidity::yul::test @@ -42,10 +41,10 @@ namespace solidity::yul::test class KnowledgeBaseTest { protected: - KnowledgeBase constructKnowledgeBase(string const& _source) + KnowledgeBase constructKnowledgeBase(std::string const& _source) { ErrorList errorList; - shared_ptr analysisInfo; + std::shared_ptr analysisInfo; std::tie(m_object, analysisInfo) = yul::test::parse(_source, m_dialect, errorList); BOOST_REQUIRE(m_object && errorList.empty() && m_object->code); @@ -62,9 +61,9 @@ class KnowledgeBaseTest } EVMDialect m_dialect{EVMVersion{}, true}; - shared_ptr m_object; + std::shared_ptr m_object; SSAValueTracker m_ssaValues; - map m_values; + std::map m_values; }; BOOST_FIXTURE_TEST_SUITE(KnowledgeBase, KnowledgeBaseTest) diff --git a/test/libyul/Metrics.cpp b/test/libyul/Metrics.cpp index f8c5920f2acc..ac3153433f56 100644 --- a/test/libyul/Metrics.cpp +++ b/test/libyul/Metrics.cpp @@ -27,7 +27,6 @@ #include -using namespace std; using namespace solidity::langutil; namespace solidity::yul::test @@ -36,9 +35,9 @@ namespace solidity::yul::test namespace { -size_t codeSize(string const& _source, CodeWeights const _weights = {}) +size_t codeSize(std::string const& _source, CodeWeights const _weights = {}) { - shared_ptr ast = parse(_source, false).first; + std::shared_ptr ast = parse(_source, false).first; BOOST_REQUIRE(ast); return CodeSize::codeSize(*ast, _weights); } diff --git a/test/libyul/ObjectCompilerTest.cpp b/test/libyul/ObjectCompilerTest.cpp index 74a680b9b7c0..65ff18a768de 100644 --- a/test/libyul/ObjectCompilerTest.cpp +++ b/test/libyul/ObjectCompilerTest.cpp @@ -43,9 +43,8 @@ using namespace solidity::yul; using namespace solidity::yul::test; using namespace solidity::frontend; using namespace solidity::frontend::test; -using namespace std; -ObjectCompilerTest::ObjectCompilerTest(string const& _filename): +ObjectCompilerTest::ObjectCompilerTest(std::string const& _filename): TestCase(_filename) { m_source = m_reader.source(); @@ -62,18 +61,18 @@ ObjectCompilerTest::ObjectCompilerTest(string const& _filename): m_expectation = m_reader.simpleExpectations(); } -TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) +TestCase::TestResult ObjectCompilerTest::run(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted) { YulStack stack( EVMVersion(), - nullopt, + std::nullopt, YulStack::Language::StrictAssembly, OptimiserSettings::preset(m_optimisationPreset), DebugInfoSelection::All() ); if (!stack.parseAndAnalyze("source", m_source)) { - AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; + AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << std::endl; SourceReferenceFormatter{_stream, stack, true, false} .printErrorInformation(stack.errors()); return TestResult::FatalError; diff --git a/test/libyul/ObjectParser.cpp b/test/libyul/ObjectParser.cpp index e78a8b0eb627..d9623384a03e 100644 --- a/test/libyul/ObjectParser.cpp +++ b/test/libyul/ObjectParser.cpp @@ -43,9 +43,9 @@ #include #include -using namespace std; using namespace solidity::frontend; using namespace solidity::langutil; +using namespace std::string_literals; namespace solidity::yul::test { @@ -53,7 +53,7 @@ namespace solidity::yul::test namespace { -pair parse(string const& _source) +std::pair parse(std::string const& _source) { try { @@ -74,7 +74,7 @@ pair parse(string const& _source) return {false, {}}; } -optional parseAndReturnFirstError(string const& _source, bool _allowWarningsAndInfos = true) +std::optional parseAndReturnFirstError(std::string const& _source, bool _allowWarningsAndInfos = true) { bool success; ErrorList errors; @@ -99,12 +99,12 @@ optional parseAndReturnFirstError(string const& _source, bool _allowWarni return {}; } -bool successParse(string const& _source, bool _allowWarningsAndInfos = true) +bool successParse(std::string const& _source, bool _allowWarningsAndInfos = true) { return !parseAndReturnFirstError(_source, _allowWarningsAndInfos); } -Error expectError(string const& _source, bool _allowWarningsAndInfos = false) +Error expectError(std::string const& _source, bool _allowWarningsAndInfos = false) { auto error = parseAndReturnFirstError(_source, _allowWarningsAndInfos); @@ -112,18 +112,18 @@ Error expectError(string const& _source, bool _allowWarningsAndInfos = false) return *error; } -tuple, ErrorList> tryGetSourceLocationMapping(string _source) +std::tuple, ErrorList> tryGetSourceLocationMapping(std::string _source) { - vector lines; + std::vector lines; boost::split(lines, _source, boost::is_any_of("\n")); - string source = util::joinHumanReadablePrefixed(lines, "\n///") + "\n{}\n"; + std::string source = util::joinHumanReadablePrefixed(lines, "\n///") + "\n{}\n"; ErrorList errors; ErrorReporter reporter(errors); Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(EVMVersion::berlin()); ObjectParser objectParser{reporter, dialect}; CharStream stream(std::move(source), ""); - auto object = objectParser.parse(make_shared(stream), false); + auto object = objectParser.parse(std::make_shared(stream), false); BOOST_REQUIRE(object && object->debugData); return {object->debugData->sourceNames, std::move(errors)}; } @@ -147,9 +147,9 @@ BOOST_AUTO_TEST_CASE(empty_code) BOOST_AUTO_TEST_CASE(recursion_depth) { - string input; + std::string input; for (size_t i = 0; i < 20000; i++) - input += "object \"a" + to_string(i) + "\" { code {} "; + input += "object \"a" + std::to_string(i) + "\" { code {} "; for (size_t i = 0; i < 20000; i++) input += "}"; @@ -158,7 +158,7 @@ BOOST_AUTO_TEST_CASE(recursion_depth) BOOST_AUTO_TEST_CASE(to_string) { - string code = R"( + std::string code = R"( object "O" { code { let x := mload(0) if x { sstore(0, 1) } } object "i" { code {} data "j" "def" } @@ -166,7 +166,7 @@ BOOST_AUTO_TEST_CASE(to_string) data "k" hex"010203" } )"; - string expectation = R"(object "O" { + std::string expectation = R"(object "O" { code { let x := mload(0) if x { sstore(0, 1) } diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 511ed6c19095..c9b9ec0727f7 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -39,7 +39,6 @@ #include #include -using namespace std; using namespace solidity; using namespace solidity::util; using namespace solidity::langutil; @@ -53,14 +52,14 @@ namespace solidity::yul::test namespace { -shared_ptr parse(string const& _source, Dialect const& _dialect, ErrorReporter& errorReporter) +std::shared_ptr parse(std::string const& _source, Dialect const& _dialect, ErrorReporter& errorReporter) { try { auto stream = CharStream(_source, ""); - map> indicesToSourceNames; - indicesToSourceNames[0] = make_shared("source0"); - indicesToSourceNames[1] = make_shared("source1"); + std::map> indicesToSourceNames; + indicesToSourceNames[0] = std::make_shared("source0"); + indicesToSourceNames[1] = std::make_shared("source1"); auto parserResult = yul::Parser( errorReporter, @@ -85,7 +84,7 @@ shared_ptr parse(string const& _source, Dialect const& _dialect, ErrorRep return {}; } -std::optional parseAndReturnFirstError(string const& _source, Dialect const& _dialect, bool _allowWarningsAndInfos = true) +std::optional parseAndReturnFirstError(std::string const& _source, Dialect const& _dialect, bool _allowWarningsAndInfos = true) { ErrorList errors; ErrorReporter errorReporter(errors); @@ -145,7 +144,7 @@ BOOST_AUTO_TEST_CASE(builtins_analysis) { return _name == "builtin"_yulstring ? &f : nullptr; } - BuiltinFunction f{"builtin"_yulstring, vector(2), vector(3), {}, {}, false, {}}; + BuiltinFunction f{"builtin"_yulstring, std::vector(2), std::vector(3), {}, {}, false, {}}; }; SimpleDialect dialect; @@ -158,7 +157,7 @@ BOOST_AUTO_TEST_CASE(default_types_set) { ErrorList errorList; ErrorReporter reporter(errorList); - shared_ptr result = parse( + std::shared_ptr result = parse( "{" "let x:bool := true:bool " "let z:bool := true " @@ -212,7 +211,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_empty_block) "/// @src 0:234:543\n" "{}\n"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); CHECK_LOCATION(result->debugData->originLocation, "source0", 234, 543); } @@ -230,7 +229,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_with_children) "let y := add(1, 2)\n" "}\n"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); CHECK_LOCATION(result->debugData->originLocation, "source0", 234, 543); BOOST_REQUIRE_EQUAL(3, result->statements.size()); @@ -253,7 +252,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_different_sources) "let y := add(1, 2)\n" "}\n"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); CHECK_LOCATION(result->debugData->originLocation, "source0", 234, 543); BOOST_REQUIRE_EQUAL(3, result->statements.size()); @@ -275,7 +274,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_nested) "switch y case 0 {} default {}\n" "}\n"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); CHECK_LOCATION(result->debugData->originLocation, "source0", 234, 543); BOOST_REQUIRE_EQUAL(2, result->statements.size()); @@ -299,13 +298,13 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_block_switch_case) "}\n" "}\n"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); CHECK_LOCATION(result->debugData->originLocation, "source0", 234, 543); BOOST_REQUIRE_EQUAL(2, result->statements.size()); - BOOST_REQUIRE(holds_alternative(result->statements.at(1))); - auto const& switchStmt = get(result->statements.at(1)); + BOOST_REQUIRE(std::holds_alternative(result->statements.at(1))); + auto const& switchStmt = std::get(result->statements.at(1)); CHECK_LOCATION(switchStmt.debugData->originLocation, "source0", 343, 434); BOOST_REQUIRE_EQUAL(1, switchStmt.cases.size()); @@ -331,7 +330,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_inherit_into_outer_scope) "let y := add(1, 2)\n" "}\n"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); CHECK_LOCATION(result->debugData->originLocation, "source0", 1, 100); @@ -340,9 +339,9 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_inherit_into_outer_scope) CHECK_LOCATION(originLocationOf(result->statements.at(0)), "source0", 1, 100); // First child element must be a block itself with one statement. - BOOST_REQUIRE(holds_alternative(result->statements.at(0))); - BOOST_REQUIRE_EQUAL(get(result->statements.at(0)).statements.size(), 1); - CHECK_LOCATION(originLocationOf(get(result->statements.at(0)).statements.at(0)), "source0", 123, 432); + BOOST_REQUIRE(std::holds_alternative(result->statements.at(0))); + BOOST_REQUIRE_EQUAL(std::get(result->statements.at(0)).statements.size(), 1); + CHECK_LOCATION(originLocationOf(std::get(result->statements.at(0)).statements.at(0)), "source0", 123, 432); // The next two elements have an inherited source location from the prior inner scope. CHECK_LOCATION(originLocationOf(result->statements.at(1)), "source0", 123, 432); @@ -362,7 +361,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_assign_empty) "a := true:bool\n" "}\n"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); // should still parse BOOST_REQUIRE_EQUAL(2, result->statements.size()); CHECK_LOCATION(originLocationOf(result->statements.at(0)), "source0", 123, 432); @@ -383,7 +382,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_source_index) "\n" "}\n"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); // should still parse BOOST_REQUIRE(errorList.size() == 1); BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); @@ -403,13 +402,13 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_1) ":= true:bool\n" "}\n"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); BOOST_REQUIRE_EQUAL(1, result->statements.size()); CHECK_LOCATION(originLocationOf(result->statements.at(0)), "source0", 123, 432); - BOOST_REQUIRE(holds_alternative(result->statements.at(0))); - VariableDeclaration const& varDecl = get(result->statements.at(0)); + BOOST_REQUIRE(std::holds_alternative(result->statements.at(0))); + VariableDeclaration const& varDecl = std::get(result->statements.at(0)); CHECK_LOCATION(originLocationOf(*varDecl.value), "source0", 234, 2026); } @@ -426,18 +425,18 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_2) } )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); BOOST_REQUIRE_EQUAL(1, result->statements.size()); CHECK_LOCATION(result->debugData->originLocation, "source0", 0, 5); // `let x := add(1, ` - BOOST_REQUIRE(holds_alternative(result->statements.at(0))); - VariableDeclaration const& varDecl = get(result->statements.at(0)); + BOOST_REQUIRE(std::holds_alternative(result->statements.at(0))); + VariableDeclaration const& varDecl = std::get(result->statements.at(0)); CHECK_LOCATION(varDecl.debugData->originLocation, "source0", 0, 5); BOOST_REQUIRE(!!varDecl.value); - BOOST_REQUIRE(holds_alternative(*varDecl.value)); - FunctionCall const& call = get(*varDecl.value); + BOOST_REQUIRE(std::holds_alternative(*varDecl.value)); + FunctionCall const& call = std::get(*varDecl.value); CHECK_LOCATION(call.debugData->originLocation, "source1", 2, 3); // `2` @@ -460,26 +459,26 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_mixed_locations_3) } )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); BOOST_REQUIRE_EQUAL(2, result->statements.size()); CHECK_LOCATION(result->debugData->originLocation, "source1", 23, 45); - BOOST_REQUIRE(holds_alternative(result->statements.at(0))); - Block const& innerBlock = get(result->statements.at(0)); + BOOST_REQUIRE(std::holds_alternative(result->statements.at(0))); + Block const& innerBlock = std::get(result->statements.at(0)); CHECK_LOCATION(innerBlock.debugData->originLocation, "source1", 23, 45); BOOST_REQUIRE_EQUAL(1, innerBlock.statements.size()); - BOOST_REQUIRE(holds_alternative(result->statements.at(1))); - ExpressionStatement const& sstoreStmt = get(innerBlock.statements.at(0)); - BOOST_REQUIRE(holds_alternative(sstoreStmt.expression)); - FunctionCall const& sstoreCall = get(sstoreStmt.expression); + BOOST_REQUIRE(std::holds_alternative(result->statements.at(1))); + ExpressionStatement const& sstoreStmt = std::get(innerBlock.statements.at(0)); + BOOST_REQUIRE(std::holds_alternative(sstoreStmt.expression)); + FunctionCall const& sstoreCall = std::get(sstoreStmt.expression); CHECK_LOCATION(sstoreCall.debugData->originLocation, "source1", 23, 45); - BOOST_REQUIRE(holds_alternative(result->statements.at(1))); - ExpressionStatement mstoreStmt = get(result->statements.at(1)); - BOOST_REQUIRE(holds_alternative(mstoreStmt.expression)); - FunctionCall const& mstoreCall = get(mstoreStmt.expression); + BOOST_REQUIRE(std::holds_alternative(result->statements.at(1))); + ExpressionStatement mstoreStmt = std::get(result->statements.at(1)); + BOOST_REQUIRE(std::holds_alternative(mstoreStmt.expression)); + FunctionCall const& mstoreCall = std::get(mstoreStmt.expression); CHECK_LOCATION(mstoreCall.debugData->originLocation, "source0", 420, 680); } @@ -496,13 +495,13 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_comments_after_valid) } )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); BOOST_REQUIRE_EQUAL(1, result->statements.size()); CHECK_LOCATION(result->debugData->originLocation, "source1", 23, 45); - BOOST_REQUIRE(holds_alternative(result->statements.at(0))); - VariableDeclaration const& varDecl = get(result->statements.at(0)); + BOOST_REQUIRE(std::holds_alternative(result->statements.at(0))); + VariableDeclaration const& varDecl = std::get(result->statements.at(0)); CHECK_LOCATION(varDecl.debugData->originLocation, "source0", 420, 680); } @@ -515,7 +514,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_suffix) {} )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); BOOST_REQUIRE(errorList.size() == 1); BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); @@ -532,7 +531,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_invalid_prefix) {} )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); CHECK_LOCATION(result->debugData->originLocation, "", -1, -1); } @@ -546,7 +545,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_unspecified) {} )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); CHECK_LOCATION(result->debugData->originLocation, "", -1, -1); } @@ -560,7 +559,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_non_integer) {} )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); BOOST_REQUIRE(errorList.size() == 1); BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); @@ -577,7 +576,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_bad_integer) {} )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); BOOST_REQUIRE(errorList.size() == 1); BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); @@ -598,10 +597,10 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_ensure_last_match) } )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); - BOOST_REQUIRE(holds_alternative(result->statements.at(0))); - VariableDeclaration const& varDecl = get(result->statements.at(0)); + BOOST_REQUIRE(std::holds_alternative(result->statements.at(0))); + VariableDeclaration const& varDecl = std::get(result->statements.at(0)); // Ensure the latest @src per documentation-comment is used (0:30:40). CHECK_LOCATION(varDecl.debugData->originLocation, "source0", 30, 40); @@ -616,7 +615,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_no_whitespace) {} )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); BOOST_REQUIRE(errorList.size() == 1); BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); @@ -633,7 +632,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_separated_with_single_s {} )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); CHECK_LOCATION(result->debugData->originLocation, "source1", 333, 444); } @@ -644,7 +643,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_leading_trailing_whitespace) ErrorReporter reporter(errorList); auto const sourceText = "/// @src 0:111:222 \n{}"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); CHECK_LOCATION(result->debugData->originLocation, "source0", 111, 222); } @@ -661,10 +660,10 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_reference_original_sloc) } )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); - BOOST_REQUIRE(holds_alternative(result->statements.at(0))); - VariableDeclaration const& varDecl = get(result->statements.at(0)); + BOOST_REQUIRE(std::holds_alternative(result->statements.at(0))); + VariableDeclaration const& varDecl = std::get(result->statements.at(0)); // -1 points to original source code, which in this case is `"source0"` (which is also CHECK_LOCATION(varDecl.debugData->originLocation, "", 10, 20); @@ -683,19 +682,19 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets) } )~~~"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); BOOST_REQUIRE_EQUAL(result->statements.size(), 2); - BOOST_REQUIRE(holds_alternative(result->statements.at(0))); - VariableDeclaration const& varX = get(result->statements.at(0)); + BOOST_REQUIRE(std::holds_alternative(result->statements.at(0))); + VariableDeclaration const& varX = std::get(result->statements.at(0)); CHECK_LOCATION(varX.debugData->originLocation, "source0", 149, 156); - BOOST_REQUIRE(holds_alternative(result->statements.at(1))); - VariableDeclaration const& varY = get(result->statements.at(1)); + BOOST_REQUIRE(std::holds_alternative(result->statements.at(1))); + VariableDeclaration const& varY = std::get(result->statements.at(1)); BOOST_REQUIRE(!!varY.value); - BOOST_REQUIRE(holds_alternative(*varY.value)); - Literal const& literal128 = get(*varY.value); + BOOST_REQUIRE(std::holds_alternative(*varY.value)); + Literal const& literal128 = std::get(*varY.value); CHECK_LOCATION(literal128.debugData->originLocation, "source1", 96, 165); } @@ -708,7 +707,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_empty_snippet) {} )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); CHECK_LOCATION(result->debugData->originLocation, "source0", 111, 222); } @@ -722,7 +721,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_no_whitespace_befo {} )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); BOOST_REQUIRE(errorList.size() == 1); BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); @@ -739,7 +738,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_no_whitespace_afte {} )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); CHECK_LOCATION(result->debugData->originLocation, "source0", 111, 222); } @@ -753,7 +752,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_with_snippets_no_whites {} )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); CHECK_LOCATION(result->debugData->originLocation, "source1", 333, 444); } @@ -767,7 +766,7 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_two_locations_with_snippets_untermina {} )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); BOOST_REQUIRE(errorList.size() == 1); BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); @@ -788,19 +787,19 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_with_code_snippets_with_nested_locati } )~~~"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); BOOST_REQUIRE_EQUAL(result->statements.size(), 2); - BOOST_REQUIRE(holds_alternative(result->statements.at(0))); - VariableDeclaration const& varX = get(result->statements.at(0)); + BOOST_REQUIRE(std::holds_alternative(result->statements.at(0))); + VariableDeclaration const& varX = std::get(result->statements.at(0)); CHECK_LOCATION(varX.debugData->originLocation, "source0", 149, 156); - BOOST_REQUIRE(holds_alternative(result->statements.at(1))); - VariableDeclaration const& varY = get(result->statements.at(1)); + BOOST_REQUIRE(std::holds_alternative(result->statements.at(1))); + VariableDeclaration const& varY = std::get(result->statements.at(1)); BOOST_REQUIRE(!!varY.value); - BOOST_REQUIRE(holds_alternative(*varY.value)); - Literal const& literal128 = get(*varY.value); + BOOST_REQUIRE(std::holds_alternative(*varY.value)); + Literal const& literal128 = std::get(*varY.value); CHECK_LOCATION(literal128.debugData->originLocation, "source1", 96, 165); } @@ -817,13 +816,13 @@ BOOST_AUTO_TEST_CASE(astid) } )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); BOOST_CHECK(result->debugData->astID == int64_t(7)); - auto const& funDef = get(result->statements.at(0)); + auto const& funDef = std::get(result->statements.at(0)); BOOST_CHECK(funDef.debugData->astID == int64_t(2)); - BOOST_CHECK(funDef.parameters.at(0).debugData->astID == nullopt); - BOOST_CHECK(debugDataOf(result->statements.at(1))->astID == nullopt); + BOOST_CHECK(funDef.parameters.at(0).debugData->astID == std::nullopt); + BOOST_CHECK(debugDataOf(result->statements.at(1))->astID == std::nullopt); } BOOST_AUTO_TEST_CASE(astid_reset) @@ -839,13 +838,13 @@ BOOST_AUTO_TEST_CASE(astid_reset) } )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); BOOST_CHECK(result->debugData->astID == int64_t(7)); - auto const& funDef = get(result->statements.at(0)); + auto const& funDef = std::get(result->statements.at(0)); BOOST_CHECK(funDef.debugData->astID == int64_t(2)); - BOOST_CHECK(funDef.parameters.at(0).debugData->astID == nullopt); - BOOST_CHECK(debugDataOf(result->statements.at(1))->astID == nullopt); + BOOST_CHECK(funDef.parameters.at(0).debugData->astID == std::nullopt); + BOOST_CHECK(debugDataOf(result->statements.at(1))->astID == std::nullopt); } BOOST_AUTO_TEST_CASE(astid_multi) @@ -857,7 +856,7 @@ BOOST_AUTO_TEST_CASE(astid_multi) {} )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); BOOST_CHECK(result->debugData->astID == int64_t(8)); } @@ -871,7 +870,7 @@ BOOST_AUTO_TEST_CASE(astid_invalid) {} )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); BOOST_REQUIRE(errorList.size() == 1); BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); @@ -888,7 +887,7 @@ BOOST_AUTO_TEST_CASE(astid_too_large) {} )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); BOOST_REQUIRE(errorList.size() == 1); BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); @@ -904,7 +903,7 @@ BOOST_AUTO_TEST_CASE(astid_way_too_large) {} )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); BOOST_REQUIRE(errorList.size() == 1); BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); @@ -920,7 +919,7 @@ BOOST_AUTO_TEST_CASE(astid_not_fully_numeric) {} )"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result); BOOST_REQUIRE(errorList.size() == 1); BOOST_TEST(errorList[0]->type() == Error::Type::SyntaxError); @@ -942,12 +941,12 @@ BOOST_AUTO_TEST_CASE(customSourceLocations_multiple_src_tags_on_one_line) " let x := 123\n" "}\n"; EVMDialectTyped const& dialect = EVMDialectTyped::instance(EVMVersion{}); - shared_ptr result = parse(sourceText, dialect, reporter); + std::shared_ptr result = parse(sourceText, dialect, reporter); BOOST_REQUIRE(!!result && errorList.size() == 0); BOOST_REQUIRE_EQUAL(result->statements.size(), 1); - BOOST_REQUIRE(holds_alternative(result->statements.at(0))); - VariableDeclaration const& varX = get(result->statements.at(0)); + BOOST_REQUIRE(std::holds_alternative(result->statements.at(0))); + VariableDeclaration const& varX = std::get(result->statements.at(0)); CHECK_LOCATION(varX.debugData->originLocation, "source1", 4, 5); } diff --git a/test/libyul/StackLayoutGeneratorTest.cpp b/test/libyul/StackLayoutGeneratorTest.cpp index 70fda680e334..02b03ccea76d 100644 --- a/test/libyul/StackLayoutGeneratorTest.cpp +++ b/test/libyul/StackLayoutGeneratorTest.cpp @@ -43,9 +43,8 @@ using namespace solidity::yul; using namespace solidity::yul::test; using namespace solidity::frontend; using namespace solidity::frontend::test; -using namespace std; -StackLayoutGeneratorTest::StackLayoutGeneratorTest(string const& _filename): +StackLayoutGeneratorTest::StackLayoutGeneratorTest(std::string const& _filename): TestCase(_filename) { m_source = m_reader.source(); @@ -213,13 +212,13 @@ class StackLayoutPrinter std::list m_blocksToPrint; }; -TestCase::TestResult StackLayoutGeneratorTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) +TestCase::TestResult StackLayoutGeneratorTest::run(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted) { ErrorList errors; auto [object, analysisInfo] = parse(m_source, *m_dialect, errors); if (!object || !analysisInfo || Error::containsErrors(errors)) { - AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; + AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << std::endl; return TestResult::FatalError; } diff --git a/test/libyul/StackShufflingTest.cpp b/test/libyul/StackShufflingTest.cpp index 35130c11b122..7c85a931e2c8 100644 --- a/test/libyul/StackShufflingTest.cpp +++ b/test/libyul/StackShufflingTest.cpp @@ -25,9 +25,8 @@ using namespace solidity::util; using namespace solidity::langutil; using namespace solidity::yul; using namespace solidity::yul::test; -using namespace std; -bool StackShufflingTest::parse(string const& _source) +bool StackShufflingTest::parse(std::string const& _source) { CharStream stream(_source, ""); Scanner scanner(stream); @@ -48,14 +47,14 @@ bool StackShufflingTest::parse(string const& _source) while (scanner.currentToken() != Token::RBrack && scanner.currentToken() != Token::EOS) { - string literal = scanner.currentLiteral(); + std::string literal = scanner.currentLiteral(); if (literal == "RET") { scanner.next(); if (scanner.currentToken() == Token::LBrack) { scanner.next(); - string functionName = scanner.currentLiteral(); + std::string functionName = scanner.currentLiteral(); auto call = yul::FunctionCall{ {}, yul::Identifier{{}, YulString(functionName)}, {} }; @@ -77,7 +76,7 @@ bool StackShufflingTest::parse(string const& _source) { expectToken(Token::LBrack); scanner.next(); - string functionName = scanner.currentLiteral(); + std::string functionName = scanner.currentLiteral(); auto call = yul::FunctionCall{ {}, yul::Identifier{{}, YulString(functionName)}, {} }; @@ -90,7 +89,7 @@ bool StackShufflingTest::parse(string const& _source) }); expectToken(Token::RBrack); } - else if (literal.find("0x") != string::npos || scanner.currentToken() == Token::Number) + else if (literal.find("0x") != std::string::npos || scanner.currentToken() == Token::Number) { stack.emplace_back(LiteralSlot{u256(literal)}); } @@ -102,10 +101,10 @@ bool StackShufflingTest::parse(string const& _source) { expectToken(Token::LBrack); scanner.next(); // read number of ghost variables as ghostVariableId - string ghostVariableId = scanner.currentLiteral(); + std::string ghostVariableId = scanner.currentLiteral(); Scope::Variable ghostVar = Scope::Variable{""_yulstring, YulString(literal + "[" + ghostVariableId + "]")}; stack.emplace_back(VariableSlot{ - m_variables.insert(make_pair(ghostVar.name, ghostVar)).first->second + m_variables.insert(std::make_pair(ghostVar.name, ghostVar)).first->second }); expectToken(Token::RBrack); } @@ -129,50 +128,50 @@ bool StackShufflingTest::parse(string const& _source) return parseStack(m_targetStack); } -StackShufflingTest::StackShufflingTest(string const& _filename): +StackShufflingTest::StackShufflingTest(std::string const& _filename): TestCase(_filename) { m_source = m_reader.source(); m_expectation = m_reader.simpleExpectations(); } -TestCase::TestResult StackShufflingTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) +TestCase::TestResult StackShufflingTest::run(std::ostream& _stream, std::string const& _linePrefix, bool _formatted) { if (!parse(m_source)) { - AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; + AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << std::endl; return TestResult::FatalError; } - ostringstream output; + std::ostringstream output; createStackLayout( m_sourceStack, m_targetStack, [&](unsigned _swapDepth) // swap { - output << stackToString(m_sourceStack) << endl; - output << "SWAP" << _swapDepth << endl; + output << stackToString(m_sourceStack) << std::endl; + output << "SWAP" << _swapDepth << std::endl; }, [&](StackSlot const& _slot) // dupOrPush { - output << stackToString(m_sourceStack) << endl; + output << stackToString(m_sourceStack) << std::endl; if (canBeFreelyGenerated(_slot)) - output << "PUSH " << stackSlotToString(_slot) << endl; + output << "PUSH " << stackSlotToString(_slot) << std::endl; else { if (auto depth = util::findOffset(m_sourceStack | ranges::views::reverse, _slot)) - output << "DUP" << *depth + 1 << endl; + output << "DUP" << *depth + 1 << std::endl; else - BOOST_THROW_EXCEPTION(runtime_error("Invalid DUP operation.")); + BOOST_THROW_EXCEPTION(std::runtime_error("Invalid DUP operation.")); } }, [&](){ // pop - output << stackToString(m_sourceStack) << endl; - output << "POP" << endl; + output << stackToString(m_sourceStack) << std::endl; + output << "POP" << std::endl; } ); - output << stackToString(m_sourceStack) << endl; + output << stackToString(m_sourceStack) << std::endl; m_obtainedResult = output.str(); return checkResult(_stream, _linePrefix, _formatted); diff --git a/test/libyul/SyntaxTest.cpp b/test/libyul/SyntaxTest.cpp index 20aadcf19ab4..0201e047d847 100644 --- a/test/libyul/SyntaxTest.cpp +++ b/test/libyul/SyntaxTest.cpp @@ -30,7 +30,6 @@ #include -using namespace std; using namespace solidity; using namespace solidity::util; using namespace solidity::langutil; @@ -40,10 +39,10 @@ using namespace solidity::frontend::test; void SyntaxTest::parseAndAnalyze() { if (m_sources.sources.size() != 1) - BOOST_THROW_EXCEPTION(runtime_error{"Expected only one source for yul test."}); + BOOST_THROW_EXCEPTION(std::runtime_error{"Expected only one source for yul test."}); - string const& name = m_sources.sources.begin()->first; - string const& source = m_sources.sources.begin()->second; + std::string const& name = m_sources.sources.begin()->first; + std::string const& source = m_sources.sources.begin()->second; ErrorList errorList{}; soltestAssert(m_dialect, ""); @@ -72,9 +71,9 @@ void SyntaxTest::parseAndAnalyze() } -SyntaxTest::SyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion): +SyntaxTest::SyntaxTest(std::string const& _filename, langutil::EVMVersion _evmVersion): CommonSyntaxTest(_filename, _evmVersion) { - string dialectName = m_reader.stringSetting("dialect", "evmTyped"); + std::string dialectName = m_reader.stringSetting("dialect", "evmTyped"); m_dialect = &dialect(dialectName, solidity::test::CommonOptions::get().evmVersion()); } diff --git a/test/libyul/YulInterpreterTest.cpp b/test/libyul/YulInterpreterTest.cpp index a1ada928b801..eb6a447d1273 100644 --- a/test/libyul/YulInterpreterTest.cpp +++ b/test/libyul/YulInterpreterTest.cpp @@ -44,9 +44,8 @@ using namespace solidity::yul; using namespace solidity::yul::test; using namespace solidity::frontend; using namespace solidity::frontend::test; -using namespace std; -YulInterpreterTest::YulInterpreterTest(string const& _filename): +YulInterpreterTest::YulInterpreterTest(std::string const& _filename): EVMVersionRestrictedTestCase(_filename) { m_source = m_reader.source(); @@ -54,7 +53,7 @@ YulInterpreterTest::YulInterpreterTest(string const& _filename): m_simulateExternalCallsToSelf = m_reader.boolSetting("simulateExternalCall", false); } -TestCase::TestResult YulInterpreterTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) +TestCase::TestResult YulInterpreterTest::run(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted) { if (!parse(_stream, _linePrefix, _formatted)) return TestResult::FatalError; @@ -64,7 +63,7 @@ TestCase::TestResult YulInterpreterTest::run(ostream& _stream, string const& _li return checkResult(_stream, _linePrefix, _formatted); } -bool YulInterpreterTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) +bool YulInterpreterTest::parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted) { YulStack stack( solidity::test::CommonOptions::get().evmVersion(), @@ -81,14 +80,14 @@ bool YulInterpreterTest::parse(ostream& _stream, string const& _linePrefix, bool } else { - AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; + AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << std::endl; SourceReferenceFormatter{_stream, stack, true, false} .printErrorInformation(stack.errors()); return false; } } -string YulInterpreterTest::interpret() +std::string YulInterpreterTest::interpret() { InterpreterState state; state.maxTraceSize = 32; @@ -108,7 +107,7 @@ string YulInterpreterTest::interpret() { } - stringstream result; + std::stringstream result; state.dumpTraceAndState(result, false); return result.str(); } diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 6d7297c2c701..9fc6712db330 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -42,15 +42,14 @@ using namespace solidity::yul; using namespace solidity::yul::test; using namespace solidity::frontend; using namespace solidity::frontend::test; -using namespace std; -YulOptimizerTest::YulOptimizerTest(string const& _filename): +YulOptimizerTest::YulOptimizerTest(std::string const& _filename): EVMVersionRestrictedTestCase(_filename) { boost::filesystem::path path(_filename); if (path.empty() || std::next(path.begin()) == path.end() || std::next(std::next(path.begin())) == path.end()) - BOOST_THROW_EXCEPTION(runtime_error("Filename path has to contain a directory: \"" + _filename + "\".")); + BOOST_THROW_EXCEPTION(std::runtime_error("Filename path has to contain a directory: \"" + _filename + "\".")); m_optimizerStep = std::prev(std::prev(path.end()))->string(); m_source = m_reader.source(); @@ -61,7 +60,7 @@ YulOptimizerTest::YulOptimizerTest(string const& _filename): m_expectation = m_reader.simpleExpectations(); } -TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) +TestCase::TestResult YulOptimizerTest::run(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted) { std::tie(m_object, m_analysisInfo) = parse(_stream, _linePrefix, _formatted, m_source); if (!m_object) @@ -75,7 +74,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line if (!tester.runStep()) { - AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Invalid optimizer step: " << m_optimizerStep << endl; + AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Invalid optimizer step: " << m_optimizerStep << std::endl; return TestResult::FatalError; } @@ -86,7 +85,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line if (m_optimizerStep != "wordSizeTransform" && !std::get<0>(parse(_stream, _linePrefix, _formatted, printed))) { util::AnsiColorized(_stream, _formatted, {util::formatting::BOLD, util::formatting::CYAN}) - << _linePrefix << "Result after the optimiser:" << endl; + << _linePrefix << "Result after the optimiser:" << std::endl; printPrefixed(_stream, printed, _linePrefix + " "); return TestResult::FatalError; } @@ -97,20 +96,20 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line } std::pair, std::shared_ptr> YulOptimizerTest::parse( - ostream& _stream, - string const& _linePrefix, + std::ostream& _stream, + std::string const& _linePrefix, bool const _formatted, - string const& _source + std::string const& _source ) { ErrorList errors; soltestAssert(m_dialect, ""); - shared_ptr object; - shared_ptr analysisInfo; + std::shared_ptr object; + std::shared_ptr analysisInfo; std::tie(object, analysisInfo) = yul::test::parse(_source, *m_dialect, errors); if (!object || !analysisInfo || Error::containsErrors(errors)) { - AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; + AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << std::endl; CharStream charStream(_source, ""); SourceReferenceFormatter{_stream, SingletonCharStreamProvider(charStream), true, false} .printErrorInformation(errors); diff --git a/test/libyul/YulOptimizerTestCommon.cpp b/test/libyul/YulOptimizerTestCommon.cpp index 31ab55e96492..e529f1d95d32 100644 --- a/test/libyul/YulOptimizerTestCommon.cpp +++ b/test/libyul/YulOptimizerTestCommon.cpp @@ -72,10 +72,9 @@ using namespace solidity::langutil; using namespace solidity::yul; using namespace solidity::yul::test; using namespace solidity::frontend; -using namespace std; YulOptimizerTestCommon::YulOptimizerTestCommon( - shared_ptr _obj, + std::shared_ptr _obj, Dialect const& _dialect ) { @@ -349,7 +348,7 @@ YulOptimizerTestCommon::YulOptimizerTestCommon( // Mark all variables with a name starting with "$" for escalation to memory. struct FakeUnreachableGenerator: ASTWalker { - map> fakeUnreachables; + std::map> fakeUnreachables; using ASTWalker::operator(); void operator()(FunctionDefinition const& _function) override { @@ -388,7 +387,7 @@ YulOptimizerTestCommon::YulOptimizerTestCommon( }; } -void YulOptimizerTestCommon::setStep(string const& _optimizerStep) +void YulOptimizerTestCommon::setStep(std::string const& _optimizerStep) { m_optimizerStep = _optimizerStep; } @@ -407,7 +406,7 @@ bool YulOptimizerTestCommon::runStep() return true; } -string YulOptimizerTestCommon::randomOptimiserStep(unsigned _seed) +std::string YulOptimizerTestCommon::randomOptimiserStep(unsigned _seed) { std::mt19937 prng(_seed); std::uniform_int_distribution dist(1, m_namedSteps.size()); @@ -417,7 +416,7 @@ string YulOptimizerTestCommon::randomOptimiserStep(unsigned _seed) { if (count == idx) { - string optimiserStep = step.first; + std::string optimiserStep = step.first; // Do not fuzz mainFunction and wordSizeTransform // because they do not preserve yul code semantics. // Do not fuzz reasoning based simplifier because @@ -437,7 +436,7 @@ string YulOptimizerTestCommon::randomOptimiserStep(unsigned _seed) yulAssert(false, "Optimiser step selection failed."); } -shared_ptr YulOptimizerTestCommon::run() +std::shared_ptr YulOptimizerTestCommon::run() { return runStep() ? m_ast : nullptr; } @@ -451,8 +450,8 @@ void YulOptimizerTestCommon::disambiguate() void YulOptimizerTestCommon::updateContext() { - m_nameDispenser = make_unique(*m_dialect, *m_object->code, m_reservedIdentifiers); - m_context = make_unique(OptimiserStepContext{ + m_nameDispenser = std::make_unique(*m_dialect, *m_object->code, m_reservedIdentifiers); + m_context = std::make_unique(OptimiserStepContext{ *m_dialect, *m_nameDispenser, m_reservedIdentifiers, From 0022089d61479ec041ec329564b5dc12a5d9eaff Mon Sep 17 00:00:00 2001 From: r0qs Date: Mon, 27 Nov 2023 14:53:59 -0300 Subject: [PATCH 030/189] Introduce `Cancun` EVM version --- .circleci/soltest_all.sh | 2 +- Changelog.md | 2 +- liblangutil/EVMVersion.h | 6 ++++-- scripts/tests.sh | 2 +- solc/CommandLineParser.cpp | 2 +- test/EVMHost.cpp | 2 ++ 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.circleci/soltest_all.sh b/.circleci/soltest_all.sh index 4be427f192d2..1e941731dac3 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" -EVM_VALUES=(homestead byzantium constantinople petersburg istanbul berlin london paris shanghai) +EVM_VALUES=(homestead byzantium constantinople petersburg istanbul berlin london paris shanghai cancun) DEFAULT_EVM=shanghai [[ " ${EVM_VALUES[*]} " =~ $DEFAULT_EVM ]] OPTIMIZE_VALUES=(0 1) diff --git a/Changelog.md b/Changelog.md index 01a77ce17f7a..23515d444453 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,7 +4,7 @@ Language Features: Compiler Features: - + * EVM: Support for the EVM Version "Cancun". Bugfixes: diff --git a/liblangutil/EVMVersion.h b/liblangutil/EVMVersion.h index 9f0239624dba..b68e9546a1aa 100644 --- a/liblangutil/EVMVersion.h +++ b/liblangutil/EVMVersion.h @@ -59,10 +59,11 @@ class EVMVersion: static EVMVersion london() { return {Version::London}; } static EVMVersion paris() { return {Version::Paris}; } static EVMVersion shanghai() { return {Version::Shanghai}; } + static EVMVersion cancun() { return {Version::Cancun}; } static std::optional fromString(std::string const& _version) { - for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium(), constantinople(), petersburg(), istanbul(), berlin(), london(), paris(), shanghai()}) + for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium(), constantinople(), petersburg(), istanbul(), berlin(), london(), paris(), shanghai(), cancun()}) if (_version == v.name()) return v; return std::nullopt; @@ -86,6 +87,7 @@ class EVMVersion: case Version::London: return "london"; case Version::Paris: return "paris"; case Version::Shanghai: return "shanghai"; + case Version::Cancun: return "cancun"; } return "INVALID"; } @@ -109,7 +111,7 @@ class EVMVersion: bool canOverchargeGasForCall() const { return *this >= tangerineWhistle(); } private: - enum class Version { Homestead, TangerineWhistle, SpuriousDragon, Byzantium, Constantinople, Petersburg, Istanbul, Berlin, London, Paris, Shanghai }; + enum class Version { Homestead, TangerineWhistle, SpuriousDragon, Byzantium, Constantinople, Petersburg, Istanbul, Berlin, London, Paris, Shanghai, Cancun }; EVMVersion(Version _version): m_version(_version) {} diff --git a/scripts/tests.sh b/scripts/tests.sh index cb1766d9a1c6..9df40e385c1a 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -105,7 +105,7 @@ EVM_VERSIONS="homestead byzantium" if [ -z "$CI" ] then - EVM_VERSIONS+=" constantinople petersburg istanbul berlin london paris shanghai" + EVM_VERSIONS+=" constantinople petersburg istanbul berlin london paris shanghai cancun" fi # And then run the Solidity unit-tests in the matrix combination of optimizer / no optimizer diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 3ed1b7b634ca..6216af4a0180 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -605,7 +605,7 @@ General Information)").c_str(), g_strEVMVersion.c_str(), po::value()->value_name("version")->default_value(EVMVersion{}.name()), "Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, " - "byzantium, constantinople, petersburg, istanbul, berlin, london, paris or shanghai." + "byzantium, constantinople, petersburg, istanbul, berlin, london, paris, shanghai or cancun." ) ; if (!_forHelp) // Note: We intentionally keep this undocumented for now. diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 81005547d2de..2bc1da96525e 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -118,6 +118,8 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm): m_evmRevision = EVMC_PARIS; else if (_evmVersion == langutil::EVMVersion::shanghai()) m_evmRevision = EVMC_SHANGHAI; + else if (_evmVersion == langutil::EVMVersion::cancun()) + m_evmRevision = EVMC_CANCUN; else assertThrow(false, Exception, "Unsupported EVM version"); From 5fcc8c7e3f0702e03f646c8c5b6c72a3d0006aaf Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Fri, 1 Dec 2023 13:17:20 +0100 Subject: [PATCH 031/189] Purge using namespace from test/solc --- scripts/check_style.sh | 1 + test/solc/CommandLineInterface.cpp | 147 +++++++++---------- test/solc/CommandLineInterfaceAllowPaths.cpp | 33 ++--- test/solc/CommandLineParser.cpp | 67 +++++---- test/solc/Common.cpp | 21 ++- 5 files changed, 133 insertions(+), 136 deletions(-) diff --git a/scripts/check_style.sh b/scripts/check_style.sh index 8ade99148f6f..78b5a6efd19a 100755 --- a/scripts/check_style.sh +++ b/scripts/check_style.sh @@ -47,6 +47,7 @@ NAMESPACE_STD_FREE_FILES=( test/libsolidity/interface/* test/libsolidity/util/* test/libyul/* + test/solc/* ) ( diff --git a/test/solc/CommandLineInterface.cpp b/test/solc/CommandLineInterface.cpp index df57572a5349..7a4f625c1570 100644 --- a/test/solc/CommandLineInterface.cpp +++ b/test/solc/CommandLineInterface.cpp @@ -42,20 +42,19 @@ #include #include -using namespace std; using namespace solidity::frontend; using namespace solidity::test; using namespace solidity::util; using namespace solidity::langutil; -using PathSet = set; +using PathSet = std::set; #define TEST_CASE_NAME (boost::unit_test::framework::current_test_case().p_name) namespace { -ostream& operator<<(ostream& _out, vector const& _remappings) +std::ostream& operator<<(std::ostream& _out, std::vector const& _remappings) { static auto remappingToString = [](auto const& _remapping) { @@ -66,17 +65,17 @@ ostream& operator<<(ostream& _out, vector const& _rem return _out; } -ostream& operator<<(ostream& _out, map const& _map) +std::ostream& operator<<(std::ostream& _out, std::map const& _map) { - _out << "{" << endl; + _out << "{" << std::endl; for (auto const& [key, value]: _map) - _out << "" << key << ": " << value << "," << endl; + _out << "" << key << ": " << value << "," << std::endl; _out << "}"; return _out; } -ostream& operator<<(ostream& _out, PathSet const& _paths) +std::ostream& operator<<(std::ostream& _out, PathSet const& _paths) { static auto pathString = [](auto const& _path) { return _path.string(); }; @@ -93,15 +92,15 @@ namespace boost::test_tools::tt_detail // The recommended solution is to overload print_log_value<> struct and make it use our operator. template<> -struct print_log_value> +struct print_log_value> { - void operator()(std::ostream& _out, vector const& _value) { ::operator<<(_out, _value); } + void operator()(std::ostream& _out, std::vector const& _value) { ::operator<<(_out, _value); } }; template<> -struct print_log_value> +struct print_log_value> { - void operator()(std::ostream& _out, map const& _value) { ::operator<<(_out, _value); } + void operator()(std::ostream& _out, std::map const& _value) { ::operator<<(_out, _value); } }; template<> @@ -149,7 +148,7 @@ BOOST_AUTO_TEST_CASE(version) BOOST_AUTO_TEST_CASE(multiple_input_modes) { - array inputModeOptions = { + std::array inputModeOptions = { "--help", "--license", "--version", @@ -161,13 +160,13 @@ BOOST_AUTO_TEST_CASE(multiple_input_modes) "--import-ast", "--import-asm-json", }; - string expectedMessage = + std::string expectedMessage = "The following options are mutually exclusive: " "--help, --license, --version, --standard-json, --link, --assemble, --strict-assembly, --yul, --import-ast, --lsp, --import-asm-json. " "Select at most one."; - for (string const& mode1: inputModeOptions) - for (string const& mode2: inputModeOptions) + for (std::string const& mode1: inputModeOptions) + for (std::string const& mode2: inputModeOptions) if (mode1 != mode2) BOOST_CHECK_EXCEPTION( parseCommandLineAndReadInputFiles({"solc", mode1, mode2}), @@ -178,12 +177,12 @@ BOOST_AUTO_TEST_CASE(multiple_input_modes) BOOST_AUTO_TEST_CASE(no_import_callback_allowed_paths) { - array options = { + std::array options = { "--no-import-callback", "--allow-paths" }; - string expectedMessage = + std::string expectedMessage = "The following options are mutually exclusive: " "--no-import-callback, --allow-paths. " "Select at most one."; @@ -208,11 +207,11 @@ BOOST_AUTO_TEST_CASE(cli_input) soltestAssert(expectedDir1.is_absolute() || expectedDir1.root_path() == "/", ""); soltestAssert(expectedDir2.is_absolute() || expectedDir2.root_path() == "/", ""); - vector expectedRemappings = { + std::vector expectedRemappings = { {"", "a", "b/c/d"}, {"a", "b", "c/d/e/"}, }; - map expectedSources = { + std::map expectedSources = { {"", ""}, {(expectedDir1 / "input1.sol").generic_string(), ""}, {(expectedDir2 / "input2.sol").generic_string(), ""}, @@ -253,7 +252,7 @@ BOOST_AUTO_TEST_CASE(cli_ignore_missing_some_files_exist) soltestAssert(expectedDir1.is_absolute() || expectedDir1.root_path() == "/", ""); // NOTE: Allowed paths should not be added for skipped files. - map expectedSources = {{(expectedDir1 / "input1.sol").generic_string(), ""}}; + std::map expectedSources = {{(expectedDir1 / "input1.sol").generic_string(), ""}}; PathSet expectedAllowedPaths = {boost::filesystem::canonical(tempDir1)}; OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({ @@ -275,7 +274,7 @@ BOOST_AUTO_TEST_CASE(cli_ignore_missing_no_files_exist) { TemporaryDirectory tempDir(TEST_CASE_NAME); - string expectedMessage = + std::string expectedMessage = "Info: \"" + (tempDir.path() / "input1.sol").string() + "\" is not found. Skipping.\n" "Info: \"" + (tempDir.path() / "input2.sol").string() + "\" is not found. Skipping.\n" "Error: All specified input files either do not exist or are not regular files.\n"; @@ -295,7 +294,7 @@ BOOST_AUTO_TEST_CASE(cli_not_a_file) { TemporaryDirectory tempDir(TEST_CASE_NAME); - string expectedMessage = "\"" + tempDir.path().string() + "\" is not a valid file."; + std::string expectedMessage = "\"" + tempDir.path().string() + "\" is not a valid file."; BOOST_CHECK_EXCEPTION( parseCommandLineAndReadInputFiles({"solc", tempDir.path().string()}), @@ -352,7 +351,7 @@ BOOST_AUTO_TEST_CASE(standard_json_one_input_file) TemporaryDirectory tempDir(TEST_CASE_NAME); createFilesWithParentDirs({tempDir.path() / "input.json"}); - vector commandLine = {"solc", "--standard-json", (tempDir.path() / "input.json").string()}; + std::vector commandLine = {"solc", "--standard-json", (tempDir.path() / "input.json").string()}; OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine); BOOST_TEST(result.success); BOOST_TEST(result.stderrContent == ""); @@ -364,7 +363,7 @@ BOOST_AUTO_TEST_CASE(standard_json_one_input_file) BOOST_AUTO_TEST_CASE(standard_json_two_input_files) { - string expectedMessage = + std::string expectedMessage = "Too many input files for --standard-json.\n" "Please either specify a single file name or provide its content on standard input."; @@ -377,7 +376,7 @@ BOOST_AUTO_TEST_CASE(standard_json_two_input_files) BOOST_AUTO_TEST_CASE(standard_json_one_input_file_and_stdin) { - string expectedMessage = + std::string expectedMessage = "Too many input files for --standard-json.\n" "Please either specify a single file name or provide its content on standard input."; @@ -393,7 +392,7 @@ BOOST_AUTO_TEST_CASE(standard_json_ignore_missing) TemporaryDirectory tempDir(TEST_CASE_NAME); // This option is pretty much useless Standard JSON mode. - string expectedMessage = + std::string expectedMessage = "All specified input files either do not exist or are not regular files."; BOOST_CHECK_EXCEPTION( @@ -410,7 +409,7 @@ BOOST_AUTO_TEST_CASE(standard_json_ignore_missing) BOOST_AUTO_TEST_CASE(standard_json_remapping) { - string expectedMessage = + std::string expectedMessage = "Import remappings are not accepted on the command line in Standard JSON mode.\n" "Please put them under 'settings.remappings' in the JSON input."; @@ -437,7 +436,7 @@ BOOST_AUTO_TEST_CASE(cli_paths_to_source_unit_names_no_base_path) boost::filesystem::path expectedOtherDir = "/" / otherDirNoSymlinks.relative_path(); soltestAssert(expectedOtherDir.is_absolute() || expectedOtherDir.root_path() == "/", ""); - vector commandLine = { + std::vector commandLine = { "solc", "contract1.sol", // Relative path "c/d/contract2.sol", // Relative path with subdirectories @@ -454,7 +453,7 @@ BOOST_AUTO_TEST_CASE(cli_paths_to_source_unit_names_no_base_path) }; expectedOptions.modelChecker.initialize = true; - map expectedSources = { + std::map expectedSources = { {"contract1.sol", ""}, {"c/d/contract2.sol", ""}, {"contract3.sol", ""}, @@ -497,7 +496,7 @@ BOOST_AUTO_TEST_CASE(cli_paths_to_source_unit_names_base_path_same_as_work_dir) soltestAssert(expectedWorkDir.is_absolute() || expectedWorkDir.root_path() == "/", ""); soltestAssert(expectedOtherDir.is_absolute() || expectedOtherDir.root_path() == "/", ""); - vector commandLine = { + std::vector commandLine = { "solc", "--base-path=" + currentDirNoSymlinks.string(), "contract1.sol", // Relative path @@ -516,7 +515,7 @@ BOOST_AUTO_TEST_CASE(cli_paths_to_source_unit_names_base_path_same_as_work_dir) expectedOptions.input.basePath = currentDirNoSymlinks; expectedOptions.modelChecker.initialize = true; - map expectedSources = { + std::map expectedSources = { {"contract1.sol", ""}, {"c/d/contract2.sol", ""}, {"contract3.sol", ""}, @@ -566,7 +565,7 @@ BOOST_AUTO_TEST_CASE(cli_paths_to_source_unit_names_base_path_different_from_wor soltestAssert(expectedOtherDir.is_absolute() || expectedOtherDir.root_path() == "/", ""); soltestAssert(expectedBaseDir.is_absolute() || expectedBaseDir.root_path() == "/", ""); - vector commandLine = { + std::vector commandLine = { "solc", "--base-path=" + baseDirNoSymlinks.string(), "contract1.sol", // Relative path @@ -587,7 +586,7 @@ BOOST_AUTO_TEST_CASE(cli_paths_to_source_unit_names_base_path_different_from_wor expectedOptions.input.basePath = baseDirNoSymlinks; expectedOptions.modelChecker.initialize = true; - map expectedSources = { + std::map expectedSources = { {expectedWorkDir.generic_string() + "/contract1.sol", ""}, {expectedWorkDir.generic_string() + "/c/d/contract2.sol", ""}, {expectedCurrentDir.generic_string() + "/contract3.sol", ""}, @@ -632,7 +631,7 @@ BOOST_AUTO_TEST_CASE(cli_paths_to_source_unit_names_relative_base_path) soltestAssert(expectedWorkDir.is_absolute() || expectedWorkDir.root_path() == "/", ""); soltestAssert(expectedOtherDir.is_absolute() || expectedOtherDir.root_path() == "/", ""); - vector commandLine = { + std::vector commandLine = { "solc", "--base-path=base", "contract1.sol", // Relative path outside of base path @@ -655,7 +654,7 @@ BOOST_AUTO_TEST_CASE(cli_paths_to_source_unit_names_relative_base_path) expectedOptions.input.basePath = "base"; expectedOptions.modelChecker.initialize = true; - map expectedSources = { + std::map expectedSources = { {expectedWorkDir.generic_string() + "/contract1.sol", ""}, {"contract2.sol", ""}, {expectedWorkDir.generic_string() + "/contract3.sol", ""}, @@ -689,7 +688,7 @@ BOOST_AUTO_TEST_CASE(cli_paths_to_source_unit_names_normalization_and_weird_name TemporaryWorkingDirectory tempWorkDir(tempDir.path() / "x/y/z"); soltestAssert(tempDir.path().is_absolute(), ""); - string uncPath = "//" + tempDir.path().relative_path().generic_string(); + std::string uncPath = "//" + tempDir.path().relative_path().generic_string(); soltestAssert(FileReader::isUNCPath(uncPath), ""); boost::filesystem::path tempDirNoSymlinks = boost::filesystem::canonical(tempDir); @@ -697,7 +696,7 @@ BOOST_AUTO_TEST_CASE(cli_paths_to_source_unit_names_normalization_and_weird_name boost::filesystem::path expectedWorkDir = "/" / boost::filesystem::current_path().relative_path(); soltestAssert(expectedWorkDir.is_absolute() || expectedWorkDir.root_path() == "/", ""); - vector commandLine = { + std::vector commandLine = { "solc", #if !defined(_WIN32) @@ -785,7 +784,7 @@ BOOST_AUTO_TEST_CASE(cli_paths_to_source_unit_names_normalization_and_weird_name }; expectedOptions.modelChecker.initialize = true; - map expectedSources = { + std::map expectedSources = { #if !defined(_WIN32) {"file:/c/d/contract1.sol", ""}, {"file:/c/d/contract2.sol", ""}, @@ -865,7 +864,7 @@ BOOST_AUTO_TEST_CASE(cli_paths_to_source_unit_names_symlinks) boost::filesystem::path expectedWorkDir = "/" / boost::filesystem::current_path().relative_path(); soltestAssert(expectedWorkDir.is_absolute() || expectedWorkDir.root_path() == "/", ""); - vector commandLine = { + std::vector commandLine = { "solc", "--base-path=../r/sym/z/", @@ -885,7 +884,7 @@ BOOST_AUTO_TEST_CASE(cli_paths_to_source_unit_names_symlinks) expectedOptions.input.basePath = "../r/sym/z/"; expectedOptions.modelChecker.initialize = true; - map expectedSources = { + std::map expectedSources = { {"contract.sol", ""}, {(expectedWorkDir.parent_path() / "x/y/z/contract.sol").generic_string(), ""}, {"contract_symlink.sol", ""}, @@ -915,14 +914,14 @@ BOOST_AUTO_TEST_CASE(cli_paths_to_source_unit_names_base_path_and_stdin) boost::filesystem::path expectedWorkDir = "/" / boost::filesystem::current_path().relative_path(); - vector commandLine = {"solc", "--base-path=base", "-"}; + std::vector commandLine = {"solc", "--base-path=base", "-"}; CommandLineOptions expectedOptions; expectedOptions.input.addStdin = true; expectedOptions.input.basePath = "base"; expectedOptions.modelChecker.initialize = true; - map expectedSources = { + std::map expectedSources = { {"", ""}, }; FileReader::FileSystemPathSet expectedAllowedDirectories = {}; @@ -944,7 +943,7 @@ BOOST_AUTO_TEST_CASE(cli_include_paths) TemporaryDirectory tempDir({"base/", "include/", "lib/nested/"}, TEST_CASE_NAME); TemporaryWorkingDirectory tempWorkDir(tempDir); - string const mainContractSource = withPreamble( + std::string const mainContractSource = withPreamble( "import \"contract.sol\";\n" "import \"contract_via_callback.sol\";\n" "import \"include.sol\";\n" @@ -955,7 +954,7 @@ BOOST_AUTO_TEST_CASE(cli_include_paths) "import \"lib_via_callback.sol\";\n" ); - string const onlyPreamble = withPreamble(""); + std::string const onlyPreamble = withPreamble(""); createFilesWithParentDirs( { tempDir.path() / "base/contract.sol", @@ -974,7 +973,7 @@ BOOST_AUTO_TEST_CASE(cli_include_paths) boost::filesystem::path canonicalWorkDir = boost::filesystem::canonical(tempDir); boost::filesystem::path expectedWorkDir = "/" / canonicalWorkDir.relative_path(); - vector commandLine = { + std::vector commandLine = { "solc", "--no-color", "--base-path=base/", @@ -1005,7 +1004,7 @@ BOOST_AUTO_TEST_CASE(cli_include_paths) expectedOptions.formatting.coloredOutput = false; expectedOptions.modelChecker.initialize = true; - map expectedSources = { + std::map expectedSources = { {"main.sol", mainContractSource}, {"contract.sol", onlyPreamble}, {"contract_via_callback.sol", onlyPreamble}, @@ -1017,7 +1016,7 @@ BOOST_AUTO_TEST_CASE(cli_include_paths) {"lib_via_callback.sol", onlyPreamble}, }; - vector expectedIncludePaths = { + std::vector expectedIncludePaths = { expectedWorkDir / "include/", expectedWorkDir / "lib/nested", expectedWorkDir / "lib/", @@ -1030,11 +1029,11 @@ BOOST_AUTO_TEST_CASE(cli_include_paths) canonicalWorkDir / "lib", }; - string const expectedStdoutContent = "Compiler run successful. No contracts to compile.\n"; + std::string const expectedStdoutContent = "Compiler run successful. No contracts to compile.\n"; OptionsReaderAndMessages result = runCLI(commandLine, ""); BOOST_TEST(result.stderrContent == ""); - if (SemVerVersion{string(VersionString)}.isPrerelease()) + if (SemVerVersion{std::string(VersionString)}.isPrerelease()) BOOST_TEST(result.stdoutContent == ""); else BOOST_TEST(result.stdoutContent == expectedStdoutContent); @@ -1048,16 +1047,16 @@ BOOST_AUTO_TEST_CASE(cli_include_paths) BOOST_AUTO_TEST_CASE(cli_no_contracts_to_compile) { - string const contractSource = R"( + std::string const contractSource = R"( // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.0; enum Status { test } )"; - string const expectedStdoutContent = "Compiler run successful. No contracts to compile.\n"; + std::string const expectedStdoutContent = "Compiler run successful. No contracts to compile.\n"; OptionsReaderAndMessages result = runCLI({"solc", "-"}, contractSource); - if (SemVerVersion{string(VersionString)}.isPrerelease()) + if (SemVerVersion{std::string(VersionString)}.isPrerelease()) BOOST_TEST(result.stdoutContent == ""); else BOOST_TEST(result.stdoutContent == expectedStdoutContent); @@ -1066,17 +1065,17 @@ BOOST_AUTO_TEST_CASE(cli_no_contracts_to_compile) BOOST_AUTO_TEST_CASE(cli_no_output) { - string const contractSource = R"( + std::string const contractSource = R"( // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.0; abstract contract A { function B() public virtual returns(uint); })"; - string const expectedStdoutContent = "Compiler run successful. No output generated.\n"; + std::string const expectedStdoutContent = "Compiler run successful. No output generated.\n"; OptionsReaderAndMessages result = runCLI({"solc", "-"}, contractSource); - if (SemVerVersion{string(VersionString)}.isPrerelease()) + if (SemVerVersion{std::string(VersionString)}.isPrerelease()) BOOST_TEST(result.stdoutContent == ""); else BOOST_TEST(result.stdoutContent == expectedStdoutContent); @@ -1088,14 +1087,14 @@ BOOST_AUTO_TEST_CASE(standard_json_include_paths) TemporaryDirectory tempDir({"base/", "include/", "lib/nested/"}, TEST_CASE_NAME); TemporaryWorkingDirectory tempWorkDir(tempDir); - string const mainContractSource = withPreamble( + std::string const mainContractSource = withPreamble( "import 'contract_via_callback.sol';\n" "import 'include_via_callback.sol';\n" "import 'nested_via_callback.sol';\n" "import 'lib_via_callback.sol';\n" ); - string const standardJsonInput = R"( + std::string const standardJsonInput = R"( { "language": "Solidity", "sources": { @@ -1104,7 +1103,7 @@ BOOST_AUTO_TEST_CASE(standard_json_include_paths) } )"; - string const onlyPreamble = withPreamble(""); + std::string const onlyPreamble = withPreamble(""); createFilesWithParentDirs( { tempDir.path() / "base/contract_via_callback.sol", @@ -1117,7 +1116,7 @@ BOOST_AUTO_TEST_CASE(standard_json_include_paths) boost::filesystem::path expectedWorkDir = "/" / boost::filesystem::canonical(tempDir).relative_path(); - vector commandLine = { + std::vector commandLine = { "solc", "--base-path=base/", "--include-path=include/", @@ -1141,14 +1140,14 @@ BOOST_AUTO_TEST_CASE(standard_json_include_paths) // NOTE: Source code from Standard JSON does not end up in FileReader. This is not a problem // because FileReader is only used once to initialize the compiler stack and after that // its sources are irrelevant (even though the callback still stores everything it loads). - map expectedSources = { + std::map expectedSources = { {"contract_via_callback.sol", onlyPreamble}, {"include_via_callback.sol", onlyPreamble}, {"nested_via_callback.sol", onlyPreamble}, {"lib_via_callback.sol", onlyPreamble}, }; - vector expectedIncludePaths = { + std::vector expectedIncludePaths = { expectedWorkDir / "include/", expectedWorkDir / "lib/nested", expectedWorkDir / "lib/", @@ -1159,15 +1158,15 @@ BOOST_AUTO_TEST_CASE(standard_json_include_paths) OptionsReaderAndMessages result = runCLI(commandLine, standardJsonInput); Json::Value parsedStdout; - string jsonParsingErrors; + std::string jsonParsingErrors; BOOST_TEST(util::jsonParseStrict(result.stdoutContent, parsedStdout, &jsonParsingErrors)); BOOST_TEST(jsonParsingErrors == ""); for (Json::Value const& errorDict: parsedStdout["errors"]) // The error list might contain pre-release compiler warning BOOST_TEST(errorDict["severity"] != "error"); BOOST_TEST( - (parsedStdout["sources"].getMemberNames() | ranges::to) == - (expectedSources | ranges::views::keys | ranges::to) + set{"main.sol"} + (parsedStdout["sources"].getMemberNames() | ranges::to) == + (expectedSources | ranges::views::keys | ranges::to) + std::set{"main.sol"} ); BOOST_REQUIRE(result.success); @@ -1184,7 +1183,7 @@ BOOST_AUTO_TEST_CASE(cli_include_paths_empty_path) TemporaryWorkingDirectory tempWorkDir(tempDir); createFilesWithParentDirs({tempDir.path() / "base/main.sol"}); - string expectedMessage = "Empty values are not allowed in --include-path."; + std::string expectedMessage = "Empty values are not allowed in --include-path."; BOOST_CHECK_EXCEPTION( parseCommandLineAndReadInputFiles({ @@ -1205,7 +1204,7 @@ BOOST_AUTO_TEST_CASE(cli_include_paths_without_base_path) TemporaryWorkingDirectory tempWorkDir(tempDir); createFilesWithParentDirs({tempDir.path() / "contract.sol"}); - string expectedMessage = "--include-path option requires a non-empty base path."; + std::string expectedMessage = "--include-path option requires a non-empty base path."; BOOST_CHECK_EXCEPTION( parseCommandLineAndReadInputFiles({"solc", "--include-path", "include/", "contract.sol"}), @@ -1227,7 +1226,7 @@ BOOST_AUTO_TEST_CASE(cli_include_paths_should_detect_source_unit_name_collisions boost::filesystem::path expectedWorkDir = "/" / boost::filesystem::canonical(tempDir).relative_path(); - string expectedMessage = + std::string expectedMessage = "Source unit name collision detected. " "The specified values of base path and/or include paths would result in multiple " "input files being assigned the same source unit name:\n" @@ -1275,7 +1274,7 @@ BOOST_AUTO_TEST_CASE(cli_include_paths_should_detect_source_unit_name_collisions { // No conflict if files with the same name exist but only one is given to the compiler. - vector commandLine = { + std::vector commandLine = { "solc", "--base-path=dir3/", "--include-path=dir1/", @@ -1290,7 +1289,7 @@ BOOST_AUTO_TEST_CASE(cli_include_paths_should_detect_source_unit_name_collisions { // The same file specified multiple times is not a conflict. - vector commandLine = { + std::vector commandLine = { "solc", "--base-path=dir3/", "--include-path=dir1/", @@ -1314,7 +1313,7 @@ BOOST_AUTO_TEST_CASE(cli_include_paths_should_allow_duplicate_paths) boost::filesystem::path expectedWorkDir = "/" / boost::filesystem::canonical(tempDir).relative_path(); boost::filesystem::path expectedTempDir = "/" / tempDir.path().relative_path(); - vector commandLine = { + std::vector commandLine = { "solc", "--base-path=dir1/", "--include-path", "dir1", @@ -1330,7 +1329,7 @@ BOOST_AUTO_TEST_CASE(cli_include_paths_should_allow_duplicate_paths) }; // Duplicates do not affect the result but are not removed from the include path list. - vector expectedIncludePaths = { + std::vector expectedIncludePaths = { expectedWorkDir / "dir1", expectedWorkDir / "dir1", expectedWorkDir / "dir1/", @@ -1357,13 +1356,13 @@ BOOST_AUTO_TEST_CASE(cli_include_paths_ambiguous_import) TemporaryWorkingDirectory tempWorkDir(tempDir); // Ambiguous: both base/contract.sol and include/contract.sol match the import. - string const mainContractSource = withPreamble("import \"contract.sol\";"); + std::string const mainContractSource = withPreamble("import \"contract.sol\";"); createFilesWithParentDirs({"base/contract.sol", "include/contract.sol"}, withPreamble("")); boost::filesystem::path expectedWorkDir = "/" / boost::filesystem::canonical(tempDir).relative_path(); - vector commandLine = { + std::vector commandLine = { "solc", "--no-color", "--base-path=base/", @@ -1371,7 +1370,7 @@ BOOST_AUTO_TEST_CASE(cli_include_paths_ambiguous_import) "-", }; - string expectedMessage = + std::string expectedMessage = "Error: Source \"contract.sol\" not found: Ambiguous import. " "Multiple matching files found inside base path and/or include paths: \"" + (expectedWorkDir / "base/contract.sol").generic_string() + "\", \"" + diff --git a/test/solc/CommandLineInterfaceAllowPaths.cpp b/test/solc/CommandLineInterfaceAllowPaths.cpp index cd301819de1a..4fe603a4f182 100644 --- a/test/solc/CommandLineInterfaceAllowPaths.cpp +++ b/test/solc/CommandLineInterfaceAllowPaths.cpp @@ -35,7 +35,6 @@ #include #include -using namespace std; using namespace solidity::frontend; using namespace solidity::util; using namespace solidity::test; @@ -63,19 +62,19 @@ struct ImportCheck static ImportCheck const OK() { return {Result::OK, ""}; } static ImportCheck const FileNotFound() { return {Result::FileNotFound, ""}; } static ImportCheck const PathDisallowed() { return {Result::PathDisallowed, ""}; } - static ImportCheck const Unknown(const string& _message) { return {Result::Unknown, _message}; } + static ImportCheck const Unknown(const std::string& _message) { return {Result::Unknown, _message}; } Result result; std::string message; }; ImportCheck checkImport( - string const& _import, - vector const& _cliOptions + std::string const& _import, + std::vector const& _cliOptions ) { - soltestAssert(regex_match(_import, regex{R"(import '[^']*')"}), ""); - for (string const& option: _cliOptions) + soltestAssert(regex_match(_import, std::regex{R"(import '[^']*')"}), ""); + for (std::string const& option: _cliOptions) soltestAssert( boost::starts_with(option, "--base-path") || boost::starts_with(option, "--include-path") || @@ -84,7 +83,7 @@ ImportCheck checkImport( "" ); - vector commandLine = { + std::vector commandLine = { "solc", "-", "--no-color", @@ -92,7 +91,7 @@ ImportCheck checkImport( }; commandLine += _cliOptions; - string standardInputContent = + std::string standardInputContent = "// SPDX-License-Identifier: GPL-3.0\n" "pragma solidity >=0.0;\n" + _import + ";"; @@ -101,7 +100,7 @@ ImportCheck checkImport( if (cliResult.success) return ImportCheck::OK(); - static regex const sourceNotFoundErrorRegex{ + static std::regex const sourceNotFoundErrorRegex{ R"(^Error \(6275\): Source "[^"]+" not found: (.*)\.\n)" R"(\s*--> .*:\d+:\d+:\n)" R"(\s*\|\n)" @@ -109,15 +108,15 @@ ImportCheck checkImport( R"(\s*\| \^+\n\s*$)" }; - smatch submatches; + std::smatch submatches; if (!regex_match(cliResult.stderrContent, submatches, sourceNotFoundErrorRegex)) return ImportCheck::Unknown("Unexpected stderr content: '" + cliResult.stderrContent + "'"); - if (submatches[1] != "File not found" && !boost::starts_with(string(submatches[1]), "File outside of allowed directories")) + if (submatches[1] != "File not found" && !boost::starts_with(std::string(submatches[1]), "File outside of allowed directories")) return ImportCheck::Unknown("Unexpected error message: '" + cliResult.stderrContent + "'"); if (submatches[1] == "File not found") return ImportCheck::FileNotFound(); - else if (boost::starts_with(string(submatches[1]), "File outside of allowed directories")) + else if (boost::starts_with(std::string(submatches[1]), "File outside of allowed directories")) return ImportCheck::PathDisallowed(); else return ImportCheck::Unknown("Unexpected error message '" + submatches[1].str() + "'"); @@ -175,11 +174,11 @@ class AllowPathsFixture TemporaryWorkingDirectory m_tempWorkDir; boost::filesystem::path const m_codeDir; boost::filesystem::path const m_workDir; - string m_portablePrefix; + std::string m_portablePrefix; bool m_caseSensitiveFilesystem = true; }; -ostream& operator<<(ostream& _out, ImportCheck const& _value) +std::ostream& operator<<(std::ostream& _out, ImportCheck const& _value) { switch (_value.result) { @@ -216,7 +215,7 @@ BOOST_AUTO_TEST_SUITE(CommandLineInterfaceAllowPathsTest) BOOST_FIXTURE_TEST_CASE(allow_path_multiple_paths, AllowPathsFixture) { - string allowedPaths = + std::string allowedPaths = m_codeDir.generic_string() + "/a/b/X.sol," + m_codeDir.generic_string() + "/X/," + m_codeDir.generic_string() + "/z," + @@ -230,7 +229,7 @@ BOOST_FIXTURE_TEST_CASE(allow_path_multiple_paths, AllowPathsFixture) BOOST_FIXTURE_TEST_CASE(allow_path_should_work_with_various_path_forms, AllowPathsFixture) { - string import = "import '" + m_portablePrefix + "/a/b/c.sol'"; + std::string import = "import '" + m_portablePrefix + "/a/b/c.sol'"; // Without --allow-path BOOST_TEST(checkImport(import, {}) == ImportCheck::PathDisallowed()); @@ -343,7 +342,7 @@ BOOST_FIXTURE_TEST_CASE(allow_path_should_work_with_various_import_forms, AllowP // UNC paths in imports. // Unfortunately can't test it on Windows without having an existing UNC path. On Linux we can // at least rely on the fact that `//` works like `/`. - string uncImportPath = "/" + m_portablePrefix + "/a/b/c.sol"; + std::string uncImportPath = "/" + m_portablePrefix + "/a/b/c.sol"; soltestAssert(FileReader::isUNCPath(uncImportPath), ""); BOOST_TEST(checkImport("import '" + uncImportPath + "'", {"--allow-paths", "../code/a/b/c.sol"}) == ImportCheck::PathDisallowed()); #endif diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index 28ca2c48a1a0..e0592da5d174 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -38,7 +38,6 @@ #include #include -using namespace std; using namespace solidity::frontend; using namespace solidity::langutil; using namespace solidity::util; @@ -47,9 +46,9 @@ using namespace solidity::yul; namespace { -CommandLineOptions parseCommandLine(vector const& _commandLine) +CommandLineOptions parseCommandLine(std::vector const& _commandLine) { - vector argv = test::makeArgv(_commandLine); + std::vector argv = test::makeArgv(_commandLine); CommandLineParser cliParser; cliParser.parse(static_cast(_commandLine.size()), argv.data()); @@ -65,7 +64,7 @@ BOOST_AUTO_TEST_SUITE(CommandLineParserTest) BOOST_AUTO_TEST_CASE(no_options) { - vector commandLine = {"solc", "contract.sol"}; + std::vector commandLine = {"solc", "contract.sol"}; CommandLineOptions expectedOptions; expectedOptions.input.paths = {"contract.sol"}; @@ -79,7 +78,7 @@ BOOST_AUTO_TEST_CASE(no_options) BOOST_AUTO_TEST_CASE(help_license_version) { - map expectedModePerOption = { + std::map expectedModePerOption = { {"--help", InputMode::Help}, {"--license", InputMode::License}, {"--version", InputMode::Version}, @@ -100,7 +99,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) { for (InputMode inputMode: {InputMode::Compiler, InputMode::CompilerWithASTImport}) { - vector commandLine = { + std::vector commandLine = { "solc", "contract.sol", // Both modes do not care about file names, just about "/tmp/projects/token.sol", // their content. They also both support stdin. @@ -157,7 +156,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) }; if (inputMode == InputMode::CompilerWithASTImport) - commandLine += vector{ + commandLine += std::vector{ "--import-ast", }; @@ -234,7 +233,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) BOOST_AUTO_TEST_CASE(no_cbor_metadata) { - vector commandLine = {"solc", "--no-cbor-metadata", "contract.sol"}; + std::vector commandLine = {"solc", "--no-cbor-metadata", "contract.sol"}; CommandLineOptions parsedOptions = parseCommandLine(commandLine); bool assert = parsedOptions.metadata.format == CompilerStack::MetadataFormat::NoMetadata; @@ -263,13 +262,13 @@ BOOST_AUTO_TEST_CASE(no_import_callback) BOOST_AUTO_TEST_CASE(via_ir_options) { BOOST_TEST(!parseCommandLine({"solc", "contract.sol"}).output.viaIR); - for (string viaIrOption: {"--via-ir", "--experimental-via-ir"}) + for (std::string viaIrOption: {"--via-ir", "--experimental-via-ir"}) BOOST_TEST(parseCommandLine({"solc", viaIrOption, "contract.sol"}).output.viaIR); } BOOST_AUTO_TEST_CASE(assembly_mode_options) { - static vector, YulStack::Machine, YulStack::Language>> const allowedCombinations = { + static std::vector, YulStack::Machine, YulStack::Language>> const allowedCombinations = { {{"--machine=evm", "--yul-dialect=evm", "--assemble"}, YulStack::Machine::EVM, YulStack::Language::StrictAssembly}, {{"--machine=evm", "--yul-dialect=evm", "--yul"}, YulStack::Machine::EVM, YulStack::Language::StrictAssembly}, {{"--machine=evm", "--yul-dialect=evm", "--strict-assembly"}, YulStack::Machine::EVM, YulStack::Language::StrictAssembly}, @@ -283,7 +282,7 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) for (auto const& [assemblyOptions, expectedMachine, expectedLanguage]: allowedCombinations) { - vector commandLine = { + std::vector commandLine = { "solc", "contract.yul", "/tmp/projects/token.yul", @@ -317,7 +316,7 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) }; commandLine += assemblyOptions; if (expectedLanguage == YulStack::Language::StrictAssembly) - commandLine += vector{ + commandLine += std::vector{ "--optimize", "--optimize-runs=1000", "--yul-optimizations=agf", @@ -370,7 +369,7 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) BOOST_AUTO_TEST_CASE(standard_json_mode_options) { - vector commandLine = { + std::vector commandLine = { "solc", "input.json", "--standard-json", @@ -420,7 +419,7 @@ BOOST_AUTO_TEST_CASE(standard_json_mode_options) BOOST_AUTO_TEST_CASE(invalid_options_input_modes_combinations) { - map> invalidOptionInputModeCombinations = { + std::map> invalidOptionInputModeCombinations = { // TODO: This should eventually contain all options. {"--experimental-via-ir", {"--assemble", "--yul", "--strict-assembly", "--standard-json", "--link"}}, {"--via-ir", {"--assemble", "--yul", "--strict-assembly", "--standard-json", "--link"}}, @@ -439,16 +438,16 @@ BOOST_AUTO_TEST_CASE(invalid_options_input_modes_combinations) }; for (auto const& [optionName, inputModes]: invalidOptionInputModeCombinations) - for (string const& inputMode: inputModes) + for (std::string const& inputMode: inputModes) { - stringstream serr; + std::stringstream serr; size_t separatorPosition = optionName.find("="); - string optionNameWithoutValue = optionName.substr(0, separatorPosition); + std::string optionNameWithoutValue = optionName.substr(0, separatorPosition); soltestAssert(!optionNameWithoutValue.empty()); - vector commandLine = {"solc", optionName, "file", inputMode}; + std::vector commandLine = {"solc", optionName, "file", inputMode}; - string expectedMessage = "The following options are not supported in the current input mode: " + optionNameWithoutValue; + std::string expectedMessage = "The following options are not supported in the current input mode: " + optionNameWithoutValue; auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) { return _exception.what() == expectedMessage; }; BOOST_CHECK_EXCEPTION(parseCommandLine(commandLine), CommandLineValidationError, hasCorrectMessage); @@ -464,7 +463,7 @@ BOOST_AUTO_TEST_CASE(optimizer_flags) OptimiserSettings evmasmOnly = OptimiserSettings::standard(); evmasmOnly.runYulOptimiser = false; - map, OptimiserSettings> settingsMap = { + std::map, OptimiserSettings> settingsMap = { {{}, OptimiserSettings::minimal()}, {{"--optimize"}, OptimiserSettings::standard()}, {{"--no-optimize-yul"}, OptimiserSettings::minimal()}, @@ -473,7 +472,7 @@ BOOST_AUTO_TEST_CASE(optimizer_flags) {{"--optimize", "--optimize-yul"}, OptimiserSettings::standard()}, }; - map inputModeFlagMap = { + std::map inputModeFlagMap = { {InputMode::Compiler, ""}, {InputMode::CompilerWithASTImport, "--import-ast"}, {InputMode::Assembler, "--strict-assembly"}, @@ -482,7 +481,7 @@ BOOST_AUTO_TEST_CASE(optimizer_flags) for (auto const& [inputMode, inputModeFlag]: inputModeFlagMap) for (auto const& [optimizerFlags, expectedOptimizerSettings]: settingsMap) { - vector commandLine = {"solc", inputModeFlag, "file"}; + std::vector commandLine = {"solc", inputModeFlag, "file"}; commandLine += optimizerFlags; BOOST_CHECK(parseCommandLine(commandLine).optimiserSettings() == expectedOptimizerSettings); } @@ -497,7 +496,7 @@ BOOST_AUTO_TEST_CASE(default_optimiser_sequence) BOOST_AUTO_TEST_CASE(valid_optimiser_sequences) { - vector validSequenceInputs { + std::vector validSequenceInputs { ":", // Empty optimization sequence and empty cleanup sequence ":fDn", // Empty optimization sequence and specified cleanup sequence "dhfoDgvulfnTUtnIf:", // Specified optimization sequence and empty cleanup sequence @@ -508,7 +507,7 @@ BOOST_AUTO_TEST_CASE(valid_optimiser_sequences) "a[[a][[aa]aa[aa]][]]aaa[aa[aa[aa]]]a[a][a][a]a[a]" // Nested brackets }; - vector> const expectedParsedSequences { + std::vector> const expectedParsedSequences { {"", ""}, {"", "fDn"}, {"dhfoDgvulfnTUtnIf", ""}, @@ -532,7 +531,7 @@ BOOST_AUTO_TEST_CASE(valid_optimiser_sequences) BOOST_AUTO_TEST_CASE(invalid_optimiser_sequences) { - vector const invalidSequenceInputs { + std::vector const invalidSequenceInputs { "abcdefg{hijklmno}pqr[st]uvwxyz", // Invalid abbreviation "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[" @@ -550,7 +549,7 @@ BOOST_AUTO_TEST_CASE(invalid_optimiser_sequences) "dhfoDgvulfnTU:tnIf:fdN" // Too many cleanup sequence delimiters }; - vector const expectedErrorMessages { + std::vector const expectedErrorMessages { "'b' is not a valid step abbreviation", "Brackets nested too deep", "Unbalanced brackets", @@ -561,12 +560,12 @@ BOOST_AUTO_TEST_CASE(invalid_optimiser_sequences) BOOST_CHECK_EQUAL(invalidSequenceInputs.size(), expectedErrorMessages.size()); - string const baseExpectedErrorMessage = "Invalid optimizer step sequence in --yul-optimizations: "; + std::string const baseExpectedErrorMessage = "Invalid optimizer step sequence in --yul-optimizations: "; for (size_t i = 0; i < invalidSequenceInputs.size(); ++i) { - vector const commandLineOptions = {"solc", "contract.sol", "--optimize", "--yul-optimizations=" + invalidSequenceInputs[i]}; - string const expectedErrorMessage = baseExpectedErrorMessage + expectedErrorMessages[i]; + std::vector const commandLineOptions = {"solc", "contract.sol", "--optimize", "--yul-optimizations=" + invalidSequenceInputs[i]}; + std::string const expectedErrorMessage = baseExpectedErrorMessage + expectedErrorMessages[i]; auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) { return _exception.what() == expectedErrorMessage; }; BOOST_CHECK_EXCEPTION(parseCommandLine(commandLineOptions), CommandLineValidationError, hasCorrectMessage); } @@ -574,14 +573,14 @@ BOOST_AUTO_TEST_CASE(invalid_optimiser_sequences) BOOST_AUTO_TEST_CASE(valid_empty_optimizer_sequences_without_optimize) { - vector const validSequenceInputs { + std::vector const validSequenceInputs { " :", ": ", "\n : \n", ":" }; - vector> const expectedParsedSequences { + std::vector> const expectedParsedSequences { {" ", ""}, {"", " "}, {"\n ", " \n"}, @@ -601,9 +600,9 @@ BOOST_AUTO_TEST_CASE(valid_empty_optimizer_sequences_without_optimize) BOOST_AUTO_TEST_CASE(invalid_optimizer_sequence_without_optimize) { - string const invalidSequence{"u: "}; - string const expectedErrorMessage{"--yul-optimizations is invalid with a non-empty sequence if Yul optimizer is disabled."}; - vector commandLineOptions{"solc", "contract.sol", "--yul-optimizations=" + invalidSequence}; + std::string const invalidSequence{"u: "}; + std::string const expectedErrorMessage{"--yul-optimizations is invalid with a non-empty sequence if Yul optimizer is disabled."}; + std::vector commandLineOptions{"solc", "contract.sol", "--yul-optimizations=" + invalidSequence}; auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) { return _exception.what() == expectedErrorMessage; }; BOOST_CHECK_EXCEPTION(parseCommandLine(commandLineOptions), CommandLineValidationError, hasCorrectMessage); } diff --git a/test/solc/Common.cpp b/test/solc/Common.cpp index 24acd6b6c80d..6b6b69956f9d 100644 --- a/test/solc/Common.cpp +++ b/test/solc/Common.cpp @@ -23,13 +23,12 @@ #include -using namespace std; using namespace solidity::frontend; -vector test::makeArgv(vector const& _commandLine) +std::vector test::makeArgv(std::vector const& _commandLine) { size_t argc = _commandLine.size(); - vector argv(_commandLine.size() + 1); + std::vector argv(_commandLine.size() + 1); // C++ standard mandates argv[argc] to be NULL argv[argc] = nullptr; @@ -41,12 +40,12 @@ vector test::makeArgv(vector const& _commandLine) } test::OptionsReaderAndMessages test::parseCommandLineAndReadInputFiles( - vector const& _commandLine, - string const& _standardInputContent + std::vector const& _commandLine, + std::string const& _standardInputContent ) { - vector argv = makeArgv(_commandLine); - stringstream sin(_standardInputContent), sout, serr; + std::vector argv = makeArgv(_commandLine); + std::stringstream sin(_standardInputContent), sout, serr; CommandLineInterface cli(sin, sout, serr); bool success = cli.parseArguments(static_cast(_commandLine.size()), argv.data()); cli.readInputFiles(); @@ -62,12 +61,12 @@ test::OptionsReaderAndMessages test::parseCommandLineAndReadInputFiles( } test::OptionsReaderAndMessages test::runCLI( - vector const& _commandLine, - string const& _standardInputContent + std::vector const& _commandLine, + std::string const& _standardInputContent ) { - vector argv = makeArgv(_commandLine); - stringstream sin(_standardInputContent), sout, serr; + std::vector argv = makeArgv(_commandLine); + std::stringstream sin(_standardInputContent), sout, serr; CommandLineInterface cli(sin, sout, serr); bool success = cli.run(static_cast(_commandLine.size()), argv.data()); From abb0f081dc33eac65ee05ce6216f21ddcdc4e7da Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Fri, 1 Dec 2023 14:21:03 +0100 Subject: [PATCH 032/189] Purge using namespace from tools/yulInterpreter --- scripts/check_style.sh | 1 + .../EVMInstructionInterpreter.cpp | 21 ++++----- test/tools/yulInterpreter/Inspector.cpp | 38 +++++++-------- test/tools/yulInterpreter/Interpreter.cpp | 47 +++++++++---------- 4 files changed, 52 insertions(+), 55 deletions(-) diff --git a/scripts/check_style.sh b/scripts/check_style.sh index 78b5a6efd19a..00230c4a4af0 100755 --- a/scripts/check_style.sh +++ b/scripts/check_style.sh @@ -48,6 +48,7 @@ NAMESPACE_STD_FREE_FILES=( test/libsolidity/util/* test/libyul/* test/solc/* + test/tools/yulInterpreter/* ) ( diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index 132066f4ad31..79dbc554a292 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -34,7 +34,6 @@ #include -using namespace std; using namespace solidity; using namespace solidity::evmasm; using namespace solidity::yul; @@ -78,7 +77,7 @@ namespace solidity::yul::test /// @a _target at offset @a _targetOffset. Behaves as if @a _source would /// continue with an infinite sequence of zero bytes beyond its end. void copyZeroExtended( - map& _target, bytes const& _source, + std::map& _target, bytes const& _source, size_t _targetOffset, size_t _sourceOffset, size_t _size ) { @@ -92,7 +91,7 @@ using u512 = boost::multiprecision::number const& _arguments + std::vector const& _arguments ) { using namespace solidity::evmasm; @@ -457,18 +456,18 @@ u256 EVMInstructionInterpreter::eval( u256 EVMInstructionInterpreter::evalBuiltin( BuiltinFunctionForEVM const& _fun, - vector const& _arguments, - vector const& _evaluatedArguments + std::vector const& _arguments, + std::vector const& _evaluatedArguments ) { if (_fun.instruction) return eval(*_fun.instruction, _evaluatedArguments); - string fun = _fun.name.str(); + std::string fun = _fun.name.str(); // Evaluate datasize/offset/copy instructions if (fun == "datasize" || fun == "dataoffset") { - string arg = std::get(_arguments.at(0)).value.str(); + std::string arg = std::get(_arguments.at(0)).value.str(); if (arg.length() < 32) arg.resize(32, 0); if (fun == "datasize") @@ -492,7 +491,7 @@ u256 EVMInstructionInterpreter::evalBuiltin( m_state.memory, m_state.code, size_t(_evaluatedArguments.at(0)), - size_t(_evaluatedArguments.at(1) & numeric_limits::max()), + size_t(_evaluatedArguments.at(1) & std::numeric_limits::max()), size_t(_evaluatedArguments.at(2)) ); return 0; @@ -512,10 +511,10 @@ bool EVMInstructionInterpreter::accessMemory(u256 const& _offset, u256 const& _s else if (((_offset + _size) >= _offset) && ((_offset + _size + 0x1f) >= (_offset + _size))) { u256 newSize = (_offset + _size + 0x1f) & ~u256(0x1f); - m_state.msize = max(m_state.msize, newSize); + m_state.msize = std::max(m_state.msize, newSize); // We only record accesses to contiguous memory chunks that are at most s_maxRangeSize bytes // in size and at an offset of at most numeric_limits::max() - s_maxRangeSize - return _size <= s_maxRangeSize && _offset <= u256(numeric_limits::max() - s_maxRangeSize); + return _size <= s_maxRangeSize && _offset <= u256(std::numeric_limits::max() - s_maxRangeSize); } else m_state.msize = u256(-1); @@ -567,7 +566,7 @@ void EVMInstructionInterpreter::logTrace( { if (!(_writesToMemory && memWriteTracingDisabled())) { - string message = _pseudoInstruction + "("; + std::string message = _pseudoInstruction + "("; std::pair inputMemoryPtrModified = isInputMemoryPtrModified(_pseudoInstruction, _arguments); for (size_t i = 0; i < _arguments.size(); ++i) { diff --git a/test/tools/yulInterpreter/Inspector.cpp b/test/tools/yulInterpreter/Inspector.cpp index 3ded7367f61a..9c4701d00a12 100644 --- a/test/tools/yulInterpreter/Inspector.cpp +++ b/test/tools/yulInterpreter/Inspector.cpp @@ -28,19 +28,17 @@ using namespace solidity; using namespace solidity::yul; using namespace solidity::yul::test; -using namespace std; - namespace { void printVariable(YulString const& _name, u256 const& _value) { - cout << "\t" << _name.str() << " = " << _value.str(); + std::cout << "\t" << _name.str() << " = " << _value.str(); if (_value != 0) - cout << " (" << toCompactHexWithPrefix(_value) << ")"; + std::cout << " (" << toCompactHexWithPrefix(_value) << ")"; - cout << endl; + std::cout << std::endl; } } @@ -58,32 +56,32 @@ void InspectedInterpreter::run( InspectedInterpreter{_inspector, _state, _dialect, scope, _disableExternalCalls, _disableMemoryTrace}(_ast); } -Inspector::NodeAction Inspector::queryUser(DebugData const& _data, map const& _variables) +Inspector::NodeAction Inspector::queryUser(DebugData const& _data, std::map const& _variables) { if (m_stepMode == NodeAction::RunNode) { // Output instructions that are being skipped/run - cout << "Running " << currentSource(_data) << endl; + std::cout << "Running " << currentSource(_data) << std::endl; return NodeAction::StepThroughNode; } - string input; + std::string input; while (true) { // Output sourcecode about to run. - cout << "> " << currentSource(_data) << endl; + std::cout << "> " << currentSource(_data) << std::endl; // Ask user for action - cout << endl + std::cout << std::endl << "(s)tep/(n)ext/(i)nspect/(p)rint/all (v)ariables?" - << endl + << std::endl << "# "; - cout.flush(); + std::cout.flush(); - getline(cin, input); + std::getline(std::cin, input); boost::algorithm::trim(input); // Imitate GDB and repeat last cmd for empty string input. @@ -97,12 +95,12 @@ Inspector::NodeAction Inspector::queryUser(DebugData const& _data, map candidates; + std::vector candidates; bool found = false; for (auto &&[yulStr, val]: _variables) @@ -128,7 +126,7 @@ Inspector::NodeAction Inspector::queryUser(DebugData const& _data, map #include -using namespace std; using namespace solidity; using namespace solidity::yul; using namespace solidity::yul::test; using solidity::util::h256; -void InterpreterState::dumpStorage(ostream& _out) const +void InterpreterState::dumpStorage(std::ostream& _out) const { for (auto const& slot: storage) if (slot.second != h256{}) - _out << " " << slot.first.hex() << ": " << slot.second.hex() << endl; + _out << " " << slot.first.hex() << ": " << slot.second.hex() << std::endl; } -void InterpreterState::dumpTraceAndState(ostream& _out, bool _disableMemoryTrace) const +void InterpreterState::dumpTraceAndState(std::ostream& _out, bool _disableMemoryTrace) const { - _out << "Trace:" << endl; + _out << "Trace:" << std::endl; for (auto const& line: trace) - _out << " " << line << endl; + _out << " " << line << std::endl; if (!_disableMemoryTrace) { _out << "Memory dump:\n"; - map words; + std::map words; for (auto const& [offset, value]: memory) words[(offset / 0x20) * 0x20] |= u256(uint32_t(value)) << (256 - 8 - 8 * static_cast(offset % 0x20)); for (auto const& [offset, value]: words) if (value != 0) - _out << " " << std::uppercase << std::hex << std::setw(4) << offset << ": " << h256(value).hex() << endl; + _out << " " << std::uppercase << std::hex << std::setw(4) << offset << ": " << h256(value).hex() << std::endl; } - _out << "Storage dump:" << endl; + _out << "Storage dump:" << std::endl; dumpStorage(_out); if (!calldata.empty()) @@ -94,7 +93,7 @@ void InterpreterState::dumpTraceAndState(ostream& _out, bool _disableMemoryTrace static_cast(calldata[offset]); } - _out << endl; + _out << std::endl; } } @@ -118,7 +117,7 @@ void Interpreter::operator()(ExpressionStatement const& _expressionStatement) void Interpreter::operator()(Assignment const& _assignment) { solAssert(_assignment.value, ""); - vector values = evaluateMulti(*_assignment.value); + std::vector values = evaluateMulti(*_assignment.value); solAssert(values.size() == _assignment.variableNames.size(), ""); for (size_t i = 0; i < values.size(); ++i) { @@ -130,7 +129,7 @@ void Interpreter::operator()(Assignment const& _assignment) void Interpreter::operator()(VariableDeclaration const& _declaration) { - vector values(_declaration.variables.size(), 0); + std::vector values(_declaration.variables.size(), 0); if (_declaration.value) values = evaluateMulti(*_declaration.value); @@ -223,7 +222,7 @@ void Interpreter::operator()(Block const& _block) enterScope(_block); // Register functions. for (auto const& statement: _block.statements) - if (holds_alternative(statement)) + if (std::holds_alternative(statement)) { FunctionDefinition const& funDef = std::get(statement); m_scope->names.emplace(funDef.name, &funDef); @@ -247,7 +246,7 @@ u256 Interpreter::evaluate(Expression const& _expression) return ev.value(); } -vector Interpreter::evaluateMulti(Expression const& _expression) +std::vector Interpreter::evaluateMulti(Expression const& _expression) { ExpressionEvaluator ev(m_state, m_dialect, *m_scope, m_variables, m_disableExternalCalls, m_disableMemoryTrace); ev.visit(_expression); @@ -257,7 +256,7 @@ vector Interpreter::evaluateMulti(Expression const& _expression) void Interpreter::enterScope(Block const& _block) { if (!m_scope->subScopes.count(&_block)) - m_scope->subScopes[&_block] = make_unique(Scope{ + m_scope->subScopes[&_block] = std::make_unique(Scope{ {}, {}, m_scope @@ -302,7 +301,7 @@ void ExpressionEvaluator::operator()(Identifier const& _identifier) void ExpressionEvaluator::operator()(FunctionCall const& _funCall) { - vector> const* literalArguments = nullptr; + std::vector> const* literalArguments = nullptr; if (BuiltinFunction const* builtin = m_dialect.builtin(_funCall.functionName.name)) if (!builtin->literalArguments.empty()) literalArguments = &builtin->literalArguments; @@ -337,14 +336,14 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall) FunctionDefinition const* fun = scope->names.at(_funCall.functionName.name); yulAssert(fun, "Function not found."); yulAssert(m_values.size() == fun->parameters.size(), ""); - map variables; + std::map variables; for (size_t i = 0; i < fun->parameters.size(); ++i) variables[fun->parameters.at(i).name] = m_values.at(i); for (size_t i = 0; i < fun->returnVariables.size(); ++i) variables[fun->returnVariables.at(i).name] = 0; m_state.controlFlowState = ControlFlowState::Default; - unique_ptr interpreter = makeInterpreterCopy(std::move(variables)); + std::unique_ptr interpreter = makeInterpreterCopy(std::move(variables)); (*interpreter)(fun->body); m_state.controlFlowState = ControlFlowState::Default; @@ -366,12 +365,12 @@ void ExpressionEvaluator::setValue(u256 _value) } void ExpressionEvaluator::evaluateArgs( - vector const& _expr, - vector> const* _literalArguments + std::vector const& _expr, + std::vector> const* _literalArguments ) { incrementStep(); - vector values; + std::vector values; size_t i = 0; /// Function arguments are evaluated in reverse. for (auto const& expr: _expr | ranges::views::reverse) @@ -380,13 +379,13 @@ void ExpressionEvaluator::evaluateArgs( visit(expr); else { - string literal = std::get(expr).value.str(); + std::string literal = std::get(expr).value.str(); try { m_values = {u256(literal)}; } - catch (exception&) + catch (std::exception&) { m_values = {u256(0)}; } @@ -455,7 +454,7 @@ void ExpressionEvaluator::runExternalCall(evmasm::Instruction _instruction) yulAssert(tmpState.numInstance < 1024, "Detected more than 1024 recursive calls, aborting..."); // Create new interpreter for the called contract - unique_ptr newInterpreter = makeInterpreterNew(tmpState, tmpScope); + std::unique_ptr newInterpreter = makeInterpreterNew(tmpState, tmpScope); Scope* abstractRootScope = &m_scope; Scope* fileScope = nullptr; From 502f978f6cc4875fe0a42cfc7e167a78b85d0c66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 10 Nov 2023 20:54:22 +0100 Subject: [PATCH 033/189] ReleaseChecklist: New solc-js publication process using a CI-built tarball --- ReleaseChecklist.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index db8bd426b66d..167417a95d1a 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -29,6 +29,7 @@ At least a day before the release: At least a day before the release: - [ ] Create a draft PR to sort the changelog. - [ ] Create draft PRs to bump version in ``solidity`` and ``solc-js``. + **Note**: The ``solc-js`` PR won't pass CI checks yet because it depends on the soljson binary from ``solc-bin``. - [ ] Create a draft of the release on github. - [ ] Create a draft PR to update soliditylang.org. - [ ] Create drafts of blog posts. @@ -94,9 +95,11 @@ At least a day before the release: ### 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. - - [ ] Run ``npm run build:tarball`` in the updated ``solc-js`` repository to create ``solc-.tgz``. Inspect the tarball to ensure that it contains an up to date compiler binary. - - [ ] Run ``npm run publish:tarball`` to publish the newly created tarball. - [ ] 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. + 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 - [ ] Make sure the documentation for the new release has been published successfully. From 787a5c22857001e59e9589714a3597048f737a01 Mon Sep 17 00:00:00 2001 From: r0qs Date: Mon, 4 Dec 2023 18:20:34 -0300 Subject: [PATCH 034/189] Use pnpm instead of forge install for prb-math external test --- .circleci/config.yml | 8 +++++++- scripts/externalTests/runners/foundry.py | 7 +++++-- test/externalTests/prb-math.py | 18 +++++++++++++++++- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1570704bdc47..db505fc4a568 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -737,7 +737,7 @@ defaults: name: t_native_test_ext_prb_math project: prb-math binary_type: native - image: cimg/rust:1.70 + image: cimg/rust:1.74.0-node - job_native_test_ext_elementfi: &job_native_test_ext_elementfi <<: *requires_b_ubu_static @@ -1373,6 +1373,12 @@ jobs: - attach_workspace: at: /tmp/workspace - install_foundry + - run: + name: Ensure pnpm is installed if npm is present + command: | + if command -v npm &> /dev/null; then + sudo npm install -g pnpm + fi - install_python3: packages: requests - run: diff --git a/scripts/externalTests/runners/foundry.py b/scripts/externalTests/runners/foundry.py index 0b171250f77e..3d35f1ac1d90 100644 --- a/scripts/externalTests/runners/foundry.py +++ b/scripts/externalTests/runners/foundry.py @@ -69,8 +69,7 @@ def profile_section(profile_fields: dict) -> str: yul = {yul} """).format(**profile_fields) - @BaseRunner.enter_test_dir - def configure(self): + def setup_presets_profiles(self): """Configure forge tests profiles""" profiles = [] @@ -93,6 +92,10 @@ def configure(self): for profile in profiles: f.write(profile) + @BaseRunner.enter_test_dir + def configure(self): + """Install project dependencies""" + self.setup_presets_profiles() run_forge_command("forge install", self.env) @BaseRunner.enter_test_dir diff --git a/test/externalTests/prb-math.py b/test/externalTests/prb-math.py index 0a4ce8ff9565..e57688ec2e38 100755 --- a/test/externalTests/prb-math.py +++ b/test/externalTests/prb-math.py @@ -20,7 +20,9 @@ # ------------------------------------------------------------------------------ import sys +import subprocess from pathlib import Path +from shutil import which # Our scripts/ is not a proper Python package so we need to modify PYTHONPATH to import from it # pragma pylint: disable=import-error,wrong-import-position @@ -32,6 +34,20 @@ from runners.foundry import FoundryRunner from test_helpers import SettingsPreset +class PRBMathRunner(FoundryRunner): + def configure(self): + if which("pnpm") is None: + raise RuntimeError("pnpm not found.") + + self.setup_presets_profiles() + # Starting from version v4.0.2, the default installation method for PRBMath dependencies + # has transitioned from Foundry to Node.js. + subprocess.run( + ["pnpm", "install"], + env=self.env, + check=True + ) + test_config = TestConfig( name="PRBMath", repo_url="https://github.com/PaulRBerg/prb-math.git", @@ -50,4 +66,4 @@ ], ) -sys.exit(run_test(FoundryRunner(argv=sys.argv[1:], config=test_config))) +sys.exit(run_test(PRBMathRunner(argv=sys.argv[1:], config=test_config))) From 8f051dd1d998056a524eefa11f8505b6fc510c2b Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Fri, 8 Dec 2023 12:58:17 -0300 Subject: [PATCH 035/189] Fix import-ast bug when inline assembly had empty let var declaration. --- Changelog.md | 1 + libyul/AsmJsonImporter.cpp | 5 +- .../empty_let_variable_declaration.json | 108 ++++++++++++++++++ .../empty_let_variable_declaration.sol | 8 ++ ...ty_let_variable_declaration_parseOnly.json | 92 +++++++++++++++ 5 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 test/libsolidity/ASTJSON/assembly/empty_let_variable_declaration.json create mode 100644 test/libsolidity/ASTJSON/assembly/empty_let_variable_declaration.sol create mode 100644 test/libsolidity/ASTJSON/assembly/empty_let_variable_declaration_parseOnly.json diff --git a/Changelog.md b/Changelog.md index 01a77ce17f7a..ef08d1f17785 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Compiler Features: Bugfixes: + * AST import: Fix bug when importing inline assembly with empty ``let`` variable declaration. ### 0.8.23 (2023-11-08) diff --git a/libyul/AsmJsonImporter.cpp b/libyul/AsmJsonImporter.cpp index ea8faa8792ee..190dcda21d05 100644 --- a/libyul/AsmJsonImporter.cpp +++ b/libyul/AsmJsonImporter.cpp @@ -255,7 +255,10 @@ VariableDeclaration AsmJsonImporter::createVariableDeclaration(Json::Value const auto varDec = createAsmNode(_node); for (auto const& var: member(_node, "variables")) varDec.variables.emplace_back(createTypedName(var)); - varDec.value = std::make_unique(createExpression(member(_node, "value"))); + + if (_node.isMember("value")) + varDec.value = std::make_unique(createExpression(member(_node, "value"))); + return varDec; } diff --git a/test/libsolidity/ASTJSON/assembly/empty_let_variable_declaration.json b/test/libsolidity/ASTJSON/assembly/empty_let_variable_declaration.json new file mode 100644 index 000000000000..9d64279da848 --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/empty_let_variable_declaration.json @@ -0,0 +1,108 @@ +{ + "absolutePath": "a", + "exportedSymbols": + { + "C": + [ + 6 + ] + }, + "id": 7, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "canonicalName": "C", + "contractDependencies": [], + "contractKind": "contract", + "fullyImplemented": true, + "id": 6, + "linearizedBaseContracts": + [ + 6 + ], + "name": "C", + "nameLocation": "9:1:1", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 4, + "nodeType": "Block", + "src": "39:30:1", + "statements": + [ + { + "AST": + { + "nativeSrc": "52:14:1", + "nodeType": "YulBlock", + "src": "52:14:1", + "statements": + [ + { + "nativeSrc": "57:5:1", + "nodeType": "YulVariableDeclaration", + "src": "57:5:1", + "variables": + [ + { + "name": "x", + "nativeSrc": "61:1:1", + "nodeType": "YulTypedName", + "src": "61:1:1", + "type": "" + } + ] + } + ] + }, + "evmVersion": %EVMVERSION%, + "externalReferences": [], + "id": 3, + "nodeType": "InlineAssembly", + "src": "43:23:1" + } + ] + }, + "functionSelector": "26121ff0", + "id": 5, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nameLocation": "23:1:1", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "24:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "39:0:1" + }, + "scope": 6, + "src": "14:55:1", + "stateMutability": "pure", + "virtual": false, + "visibility": "public" + } + ], + "scope": 7, + "src": "0:71:1", + "usedErrors": [], + "usedEvents": [] + } + ], + "src": "0:72:1" +} diff --git a/test/libsolidity/ASTJSON/assembly/empty_let_variable_declaration.sol b/test/libsolidity/ASTJSON/assembly/empty_let_variable_declaration.sol new file mode 100644 index 000000000000..5b396dd44156 --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/empty_let_variable_declaration.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure { + assembly { + let x + } + } +} +// ---- diff --git a/test/libsolidity/ASTJSON/assembly/empty_let_variable_declaration_parseOnly.json b/test/libsolidity/ASTJSON/assembly/empty_let_variable_declaration_parseOnly.json new file mode 100644 index 000000000000..5f12f280ba9b --- /dev/null +++ b/test/libsolidity/ASTJSON/assembly/empty_let_variable_declaration_parseOnly.json @@ -0,0 +1,92 @@ +{ + "absolutePath": "a", + "id": 7, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 6, + "name": "C", + "nameLocation": "9:1:1", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 4, + "nodeType": "Block", + "src": "39:30:1", + "statements": + [ + { + "AST": + { + "nativeSrc": "52:14:1", + "nodeType": "YulBlock", + "src": "52:14:1", + "statements": + [ + { + "nativeSrc": "57:5:1", + "nodeType": "YulVariableDeclaration", + "src": "57:5:1", + "variables": + [ + { + "name": "x", + "nativeSrc": "61:1:1", + "nodeType": "YulTypedName", + "src": "61:1:1", + "type": "" + } + ] + } + ] + }, + "evmVersion": %EVMVERSION%, + "externalReferences": [], + "id": 3, + "nodeType": "InlineAssembly", + "src": "43:23:1" + } + ] + }, + "id": 5, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nameLocation": "23:1:1", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "24:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "39:0:1" + }, + "src": "14:55:1", + "stateMutability": "pure", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:71:1", + "usedErrors": [], + "usedEvents": [] + } + ], + "src": "0:72:1" +} From 66cb0aee9d43d78f53020d78e23a9e06d2759540 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Mon, 11 Dec 2023 14:14:11 +0100 Subject: [PATCH 036/189] Fix argument expansion in command_available --- scripts/common.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/common.sh b/scripts/common.sh index da8726f3fa0d..39c1226d1145 100644 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -307,9 +307,9 @@ function split_on_empty_lines_into_numbered_files function command_available { local program="$1" - local parameters=${*:2} - if ! "${program}" "${parameters}" > /dev/null 2>&1 + local parameters=( "${@:2}" ) + if ! "${program}" "${parameters[@]}" > /dev/null 2>&1 then - fail "'${program}' not found or not executed successfully with parameter(s) '${parameters}'. aborting." + fail "'${program}' not found or not executed successfully with parameter(s) '${parameters[*]}'. aborting." fi } From 38d992a1535ca9b179c38fadb6be31fd08ab0dad Mon Sep 17 00:00:00 2001 From: Qi He Date: Tue, 1 Aug 2023 16:32:50 +0800 Subject: [PATCH 037/189] docs: fix incomplete member of address in cheatsheet.rst --- docs/cheatsheet.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index a60d3b50f526..2f0c1a251f4d 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -46,6 +46,12 @@ Members of ``address`` - ``
.balance`` (``uint256``): balance of the :ref:`address` in Wei - ``
.code`` (``bytes memory``): code at the :ref:`address` (can be empty) - ``
.codehash`` (``bytes32``): the codehash of the :ref:`address` +- ``
.call(bytes memory) returns (bool, bytes memory)``: issue low-level ``CALL`` with the given payload, + returns success condition and return data +- ``
.delegatecall(bytes memory) returns (bool, bytes memory)``: issue low-level ``DELEGATECALL`` with the given payload, + returns success condition and return data +- ``
.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 From 7d6e6ee6209f9fae8eecddb21707ba677e18cedc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 13 Dec 2023 16:30:37 +0100 Subject: [PATCH 038/189] prepare_report.py: Don't ignore CLI exit code --- scripts/bytecodecompare/prepare_report.py | 7 +++-- scripts/common/cmdline_helpers.py | 2 +- .../test_bytecodecompare_prepare_report.py | 27 ++++++++++--------- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/scripts/bytecodecompare/prepare_report.py b/scripts/bytecodecompare/prepare_report.py index 4cf24bb8aa40..f286a5d060ad 100755 --- a/scripts/bytecodecompare/prepare_report.py +++ b/scripts/bytecodecompare/prepare_report.py @@ -183,7 +183,10 @@ def parse_standard_json_output(source_file_name: Path, standard_json_output: str return file_report -def parse_cli_output(source_file_name: Path, cli_output: str) -> FileReport: +def parse_cli_output(source_file_name: Path, cli_output: str, exit_code: int) -> FileReport: + if exit_code != 0: + return FileReport(file_name=source_file_name, contract_reports=None) + # re.split() returns a list containing the text between pattern occurrences but also inserts the # content of matched groups in between. It also never omits the empty elements so the number of # list items is predictable (3 per match + the text before the first match) @@ -345,7 +348,7 @@ def run_compiler( check=exit_on_error, ) - return parse_cli_output(Path(source_file_name), process.stdout) + return parse_cli_output(Path(source_file_name), process.stdout, process.returncode) def generate_report( diff --git a/scripts/common/cmdline_helpers.py b/scripts/common/cmdline_helpers.py index 1f322be1fcf1..ede9f9385549 100644 --- a/scripts/common/cmdline_helpers.py +++ b/scripts/common/cmdline_helpers.py @@ -48,7 +48,7 @@ def solc_bin_report(solc_binary: str, input_files: List[Path], via_ir: bool) -> (['--via-ir'] if via_ir else []), encoding='utf8', ) - return parse_cli_output('', output) + return parse_cli_output('', output, 0) def save_bytecode(bytecode_path: Path, reports: FileReport, contract: Optional[str] = None): diff --git a/test/scripts/test_bytecodecompare_prepare_report.py b/test/scripts/test_bytecodecompare_prepare_report.py index b8a1791d652c..d73b1f7141bf 100644 --- a/test/scripts/test_bytecodecompare_prepare_report.py +++ b/test/scripts/test_bytecodecompare_prepare_report.py @@ -487,19 +487,19 @@ def test_parse_cli_output(self): ] ) - report = parse_cli_output(Path('syntaxTests/scoping/library_inherited2.sol'), LIBRARY_INHERITED2_SOL_CLI_OUTPUT) + report = parse_cli_output(Path('syntaxTests/scoping/library_inherited2.sol'), LIBRARY_INHERITED2_SOL_CLI_OUTPUT, 0) self.assertEqual(report, expected_report) def test_parse_cli_output_should_report_error_on_compiler_errors(self): expected_report = FileReport(file_name=Path('syntaxTests/pragma/unknown_pragma.sol'), contract_reports=None) - report = parse_cli_output(Path('syntaxTests/pragma/unknown_pragma.sol'), UNKNOWN_PRAGMA_SOL_CLI_OUTPUT) + report = parse_cli_output(Path('syntaxTests/pragma/unknown_pragma.sol'), UNKNOWN_PRAGMA_SOL_CLI_OUTPUT, 0) self.assertEqual(report, expected_report) def test_parse_cli_output_should_report_error_on_empty_output(self): expected_report = FileReport(file_name=Path('file.sol'), contract_reports=None) - self.assertEqual(parse_cli_output(Path('file.sol'), ''), expected_report) + self.assertEqual(parse_cli_output(Path('file.sol'), '', 0), expected_report) def test_parse_cli_output_should_report_missing_bytecode_and_metadata(self): compiler_output = dedent("""\ @@ -541,22 +541,25 @@ def test_parse_cli_output_should_report_missing_bytecode_and_metadata(self): ] ) - self.assertEqual(parse_cli_output(Path('syntaxTests/scoping/library_inherited2.sol'), compiler_output), expected_report) + self.assertEqual( + parse_cli_output(Path('syntaxTests/scoping/library_inherited2.sol'), compiler_output, 0), + expected_report + ) def test_parse_cli_output_should_report_error_on_unimplemented_feature_error(self): expected_report = FileReport(file_name=Path('file.sol'), contract_reports=None) - self.assertEqual(parse_cli_output(Path('file.sol'), UNIMPLEMENTED_FEATURE_CLI_OUTPUT), expected_report) + self.assertEqual(parse_cli_output(Path('file.sol'), UNIMPLEMENTED_FEATURE_CLI_OUTPUT, 0), expected_report) def test_parse_cli_output_should_report_error_on_stack_too_deep_error(self): expected_report = FileReport(file_name=Path('file.sol'), contract_reports=None) - self.assertEqual(parse_cli_output(Path('file.sol'), STACK_TOO_DEEP_CLI_OUTPUT), expected_report) + self.assertEqual(parse_cli_output(Path('file.sol'), STACK_TOO_DEEP_CLI_OUTPUT, 0), expected_report) def test_parse_cli_output_should_report_error_on_code_generation_error(self): expected_report = FileReport(file_name=Path('file.sol'), contract_reports=None) - self.assertEqual(parse_cli_output(Path('file.sol'), CODE_GENERATION_ERROR_CLI_OUTPUT), expected_report) + self.assertEqual(parse_cli_output(Path('file.sol'), CODE_GENERATION_ERROR_CLI_OUTPUT, 0), expected_report) def test_parse_cli_output_should_handle_output_from_solc_0_4_0(self): expected_report = FileReport( @@ -571,7 +574,7 @@ def test_parse_cli_output_should_handle_output_from_solc_0_4_0(self): ] ) - self.assertEqual(parse_cli_output(Path('contract.sol'), SOLC_0_4_0_CLI_OUTPUT), expected_report) + self.assertEqual(parse_cli_output(Path('contract.sol'), SOLC_0_4_0_CLI_OUTPUT, 0), expected_report) def test_parse_cli_output_should_handle_output_from_solc_0_4_8(self): expected_report = FileReport( @@ -588,7 +591,7 @@ def test_parse_cli_output_should_handle_output_from_solc_0_4_8(self): ] ) - self.assertEqual(parse_cli_output(Path('contract.sol'), SOLC_0_4_8_CLI_OUTPUT), expected_report) + self.assertEqual(parse_cli_output(Path('contract.sol'), SOLC_0_4_8_CLI_OUTPUT, 0), expected_report) def test_parse_cli_output_should_handle_leading_and_trailing_spaces(self): compiler_output = ( @@ -606,7 +609,7 @@ def test_parse_cli_output_should_handle_leading_and_trailing_spaces(self): ] ) - self.assertEqual(parse_cli_output(Path('contract.sol'), compiler_output), expected_report) + self.assertEqual(parse_cli_output(Path('contract.sol'), compiler_output, 0), expected_report) def test_parse_cli_output_should_handle_empty_bytecode_and_metadata_lines(self): compiler_output = dedent("""\ @@ -640,7 +643,7 @@ def test_parse_cli_output_should_handle_empty_bytecode_and_metadata_lines(self): ] ) - self.assertEqual(parse_cli_output(Path('contract.sol'), compiler_output), expected_report) + self.assertEqual(parse_cli_output(Path('contract.sol'), compiler_output, 0), expected_report) def test_parse_cli_output_should_handle_link_references_in_bytecode(self): compiler_output = dedent("""\ @@ -662,4 +665,4 @@ def test_parse_cli_output_should_handle_link_references_in_bytecode(self): ) # pragma pylint: enable=line-too-long - self.assertEqual(parse_cli_output(Path('contract.sol'), compiler_output), expected_report) + self.assertEqual(parse_cli_output(Path('contract.sol'), compiler_output, 0), expected_report) From 646b34250b3c56cee792be2deec18ba1e8b952dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 13 Dec 2023 16:51:35 +0100 Subject: [PATCH 039/189] prepare_report.py: Always exit on error from Standard JSON interface --- scripts/bytecodecompare/prepare_report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/bytecodecompare/prepare_report.py b/scripts/bytecodecompare/prepare_report.py index f286a5d060ad..8be9ae045cca 100755 --- a/scripts/bytecodecompare/prepare_report.py +++ b/scripts/bytecodecompare/prepare_report.py @@ -315,7 +315,7 @@ def run_compiler( input=compiler_input, encoding='utf8', capture_output=True, - check=exit_on_error, + check=True, ) return parse_standard_json_output(Path(source_file_name), process.stdout) From 194b114664c7daebc2ff68af3c573272f5d28913 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 6 Jun 2023 11:18:46 +0200 Subject: [PATCH 040/189] Add basic type inference infrastructure for experimental Solidity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kamil Śliwak Co-authored-by: Matheus Aguiar Co-authored-by: Nikola Matic --- .circleci/osx_install_dependencies.sh | 1 + liblangutil/Scanner.cpp | 2 +- liblangutil/Token.h | 29 +- libsolidity/CMakeLists.txt | 28 + libsolidity/analysis/NameAndTypeResolver.cpp | 6 +- libsolidity/analysis/NameAndTypeResolver.h | 5 +- libsolidity/analysis/ReferencesResolver.cpp | 93 +- libsolidity/analysis/ReferencesResolver.h | 6 +- libsolidity/analysis/SyntaxChecker.cpp | 14 +- libsolidity/analysis/SyntaxChecker.h | 2 + libsolidity/ast/AST.cpp | 12 + libsolidity/ast/AST.h | 190 ++- libsolidity/ast/ASTAnnotations.h | 10 + libsolidity/ast/ASTForward.h | 9 + libsolidity/ast/ASTJsonExporter.cpp | 9 + libsolidity/ast/ASTJsonExporter.h | 1 + libsolidity/ast/ASTVisitor.h | 32 + libsolidity/ast/AST_accept.h | 118 ++ .../experimental/analysis/Analysis.cpp | 190 ++- libsolidity/experimental/analysis/Analysis.h | 76 +- .../experimental/analysis/DebugWarner.cpp | 50 + .../experimental/analysis/DebugWarner.h | 42 + .../analysis/FunctionDependencyAnalysis.cpp | 72 + .../analysis/FunctionDependencyAnalysis.h | 57 + .../analysis/SyntaxRestrictor.cpp | 113 ++ .../experimental/analysis/SyntaxRestrictor.h | 67 + .../analysis/TypeClassRegistration.cpp | 62 + .../analysis/TypeClassRegistration.h | 58 + .../experimental/analysis/TypeInference.cpp | 1211 +++++++++++++++++ .../experimental/analysis/TypeInference.h | 124 ++ .../analysis/TypeRegistration.cpp | 211 +++ .../experimental/analysis/TypeRegistration.h | 67 + .../experimental/ast/FunctionCallGraph.h | 40 + libsolidity/experimental/ast/Type.cpp | 62 + libsolidity/experimental/ast/Type.h | 154 +++ libsolidity/experimental/ast/TypeSystem.cpp | 347 +++++ libsolidity/experimental/ast/TypeSystem.h | 184 +++ .../experimental/ast/TypeSystemHelper.cpp | 404 ++++++ .../experimental/ast/TypeSystemHelper.h | 60 + libsolidity/experimental/codegen/Common.cpp | 73 + libsolidity/experimental/codegen/Common.h | 41 + .../codegen/IRGenerationContext.h | 55 + .../experimental/codegen/IRGenerator.cpp | 160 +++ .../experimental/codegen/IRGenerator.h | 72 + .../codegen/IRGeneratorForStatements.cpp | 393 ++++++ .../codegen/IRGeneratorForStatements.h | 71 + libsolidity/interface/CompilerStack.cpp | 89 +- libsolidity/interface/CompilerStack.h | 10 + libsolidity/parsing/Parser.cpp | 317 ++++- libsolidity/parsing/Parser.h | 19 +- libstdlib/src/stub.sol | 3 +- scripts/ASTImportTest.sh | 9 +- scripts/check_style.sh | 1 + scripts/common.sh | 11 + scripts/error_codes.py | 109 +- test/CMakeLists.txt | 2 + test/InteractiveTests.h | 42 +- test/cmdlineTests/~soljson_via_fuzzer/test.sh | 4 +- test/libsolidity/Assembly.cpp | 2 +- .../FunctionDependencyGraphTest.cpp | 59 + .../libsolidity/FunctionDependencyGraphTest.h | 50 + .../SolidityExpressionCompiler.cpp | 2 +- test/libsolidity/SyntaxTest.cpp | 2 +- .../callgraph.sol | 60 + .../callgraph_no_leaves.sol | 66 + .../semanticTests/experimental/stub.sol | 97 ++ .../semanticTests/experimental/type_class.sol | 67 + .../builtin/builtin_type_definition.sol | 92 ++ .../builtin_type_definition_duplicate.sol | 17 + .../builtin_type_definition_unknown.sol | 8 + .../import}/experimental_solidity.sol | 2 +- ...al_solidity_multisource_not_all_enable.sol | 0 .../experimental_solidity_out_of_order_1.sol | 0 .../experimental_solidity_out_of_order_2.sol | 0 ...xperimental_solidity_wrong_evm_version.sol | 0 .../{ => import}/parsing_stdlib_import_1.sol | 3 +- .../{ => import}/parsing_stdlib_import_2.sol | 3 +- .../{ => import}/parsing_stdlib_import_3.sol | 4 +- .../{ => import}/parsing_stdlib_import_4.sol | 3 +- ...parsing_stdlib_import_without_pragma_1.sol | 0 ...parsing_stdlib_import_without_pragma_2.sol | 0 .../inference/experimental_keywords.sol | 4 + .../import_and_call_stdlib_function.sol | 25 + .../inference/invalid_type_referenced.sol | 13 + ...onomorphic_function_call_type_mismatch.sol | 16 + .../inference/polymorphic_function_call.sol | 53 + ...morphic_function_call_let_polymorphism.sol | 19 + ...olymorphic_function_call_type_mismatch.sol | 16 + .../inference/polymorphic_type.sol | 47 + .../polymorphic_type_abs_and_rep.sol | 69 + ...rphic_type_instantiation_and_operators.sol | 168 +++ test/stopAfterParseTests.sh | 2 +- test/tools/CMakeLists.txt | 1 + 93 files changed, 6536 insertions(+), 133 deletions(-) create mode 100644 libsolidity/experimental/analysis/DebugWarner.cpp create mode 100644 libsolidity/experimental/analysis/DebugWarner.h create mode 100644 libsolidity/experimental/analysis/FunctionDependencyAnalysis.cpp create mode 100644 libsolidity/experimental/analysis/FunctionDependencyAnalysis.h create mode 100644 libsolidity/experimental/analysis/SyntaxRestrictor.cpp create mode 100644 libsolidity/experimental/analysis/SyntaxRestrictor.h create mode 100644 libsolidity/experimental/analysis/TypeClassRegistration.cpp create mode 100644 libsolidity/experimental/analysis/TypeClassRegistration.h create mode 100644 libsolidity/experimental/analysis/TypeInference.cpp create mode 100644 libsolidity/experimental/analysis/TypeInference.h create mode 100644 libsolidity/experimental/analysis/TypeRegistration.cpp create mode 100644 libsolidity/experimental/analysis/TypeRegistration.h create mode 100644 libsolidity/experimental/ast/FunctionCallGraph.h create mode 100644 libsolidity/experimental/ast/Type.cpp create mode 100644 libsolidity/experimental/ast/Type.h create mode 100644 libsolidity/experimental/ast/TypeSystem.cpp create mode 100644 libsolidity/experimental/ast/TypeSystem.h create mode 100644 libsolidity/experimental/ast/TypeSystemHelper.cpp create mode 100644 libsolidity/experimental/ast/TypeSystemHelper.h create mode 100644 libsolidity/experimental/codegen/Common.cpp create mode 100644 libsolidity/experimental/codegen/Common.h create mode 100644 libsolidity/experimental/codegen/IRGenerationContext.h create mode 100644 libsolidity/experimental/codegen/IRGenerator.cpp create mode 100644 libsolidity/experimental/codegen/IRGenerator.h create mode 100644 libsolidity/experimental/codegen/IRGeneratorForStatements.cpp create mode 100644 libsolidity/experimental/codegen/IRGeneratorForStatements.h create mode 100644 test/libsolidity/FunctionDependencyGraphTest.cpp create mode 100644 test/libsolidity/FunctionDependencyGraphTest.h create mode 100644 test/libsolidity/functionDependencyGraphTests/callgraph.sol create mode 100644 test/libsolidity/functionDependencyGraphTests/callgraph_no_leaves.sol create mode 100644 test/libsolidity/semanticTests/experimental/stub.sol create mode 100644 test/libsolidity/semanticTests/experimental/type_class.sol create mode 100644 test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition.sol create mode 100644 test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_duplicate.sol create mode 100644 test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_unknown.sol rename test/libsolidity/syntaxTests/{pragma => experimental/import}/experimental_solidity.sol (70%) rename test/libsolidity/syntaxTests/{pragma => experimental/import}/experimental_solidity_multisource_not_all_enable.sol (100%) rename test/libsolidity/syntaxTests/{pragma => experimental/import}/experimental_solidity_out_of_order_1.sol (100%) rename test/libsolidity/syntaxTests/{pragma => experimental/import}/experimental_solidity_out_of_order_2.sol (100%) rename test/libsolidity/syntaxTests/{pragma => experimental/import}/experimental_solidity_wrong_evm_version.sol (100%) rename test/libsolidity/syntaxTests/experimental/{ => import}/parsing_stdlib_import_1.sol (76%) rename test/libsolidity/syntaxTests/experimental/{ => import}/parsing_stdlib_import_2.sol (77%) rename test/libsolidity/syntaxTests/experimental/{ => import}/parsing_stdlib_import_3.sol (70%) rename test/libsolidity/syntaxTests/experimental/{ => import}/parsing_stdlib_import_4.sol (77%) rename test/libsolidity/syntaxTests/experimental/{ => import}/parsing_stdlib_import_without_pragma_1.sol (100%) rename test/libsolidity/syntaxTests/experimental/{ => import}/parsing_stdlib_import_without_pragma_2.sol (100%) create mode 100644 test/libsolidity/syntaxTests/experimental/inference/experimental_keywords.sol create mode 100644 test/libsolidity/syntaxTests/experimental/inference/import_and_call_stdlib_function.sol create mode 100644 test/libsolidity/syntaxTests/experimental/inference/invalid_type_referenced.sol create mode 100644 test/libsolidity/syntaxTests/experimental/inference/monomorphic_function_call_type_mismatch.sol create mode 100644 test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol create mode 100644 test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call_let_polymorphism.sol create mode 100644 test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call_type_mismatch.sol create mode 100644 test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol create mode 100644 test/libsolidity/syntaxTests/experimental/inference/polymorphic_type_abs_and_rep.sol create mode 100644 test/libsolidity/syntaxTests/experimental/inference/polymorphic_type_instantiation_and_operators.sol diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh index 8e795b1dc833..61ac4a106610 100755 --- a/.circleci/osx_install_dependencies.sh +++ b/.circleci/osx_install_dependencies.sh @@ -59,6 +59,7 @@ then brew install wget brew install coreutils brew install diffutils + brew install grep ./scripts/install_obsolete_jsoncpp_1_7_4.sh # z3 diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp index 3db3f29d5d23..806efa311668 100644 --- a/liblangutil/Scanner.cpp +++ b/liblangutil/Scanner.cpp @@ -665,7 +665,7 @@ void Scanner::scanToken() case '.': // . Number advance(); - if (isDecimalDigit(m_char)) + if (m_kind != ScannerKind::ExperimentalSolidity && isDecimalDigit(m_char)) token = scanNumber('.'); else token = Token::Period; diff --git a/liblangutil/Token.h b/liblangutil/Token.h index d79c7f193e64..98de0b228d04 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -269,7 +269,15 @@ namespace solidity::langutil T(Leave, "leave", 0) \ \ T(NonExperimentalEnd, nullptr, 0) /* used as non-experimental enum end marker */ \ + /* Experimental Solidity specific keywords. */ \ + K(Class, "class", 0) \ + K(Instantiation, "instantiation", 0) \ + K(Integer, "Integer", 0) \ + K(Itself, "itself", 0) \ + K(StaticAssert, "static_assert", 0) \ + K(Builtin, "__builtin", 0) \ T(ExperimentalEnd, nullptr, 0) /* used as experimental enum end marker */ \ + \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ \ @@ -292,7 +300,7 @@ namespace TokenTraits constexpr size_t count() { return static_cast(Token::NUM_TOKENS); } // Predicates - constexpr bool isElementaryTypeName(Token tok) { return Token::Int <= tok && tok < Token::TypesEnd; } + constexpr bool isElementaryTypeName(Token _token) { return Token::Int <= _token && _token < Token::TypesEnd; } constexpr bool isAssignmentOp(Token tok) { return Token::Assign <= tok && tok <= Token::AssignMod; } constexpr bool isBinaryOp(Token op) { return Token::Comma <= op && op <= Token::Exp; } constexpr bool isCommutativeOp(Token op) { return op == Token::BitOr || op == Token::BitXor || op == Token::BitAnd || @@ -325,6 +333,16 @@ namespace TokenTraits tok == Token::TrueLiteral || tok == Token::FalseLiteral || tok == Token::HexStringLiteral || tok == Token::Hex; } + constexpr bool isBuiltinTypeClassName(Token _token) + { + return + _token == Token::Integer || + (isBinaryOp(_token) && _token != Token::Comma) || + isCompareOp(_token) || + isUnaryOp(_token) || + (isAssignmentOp(_token) && _token != Token::Assign); + } + constexpr bool isExperimentalSolidityKeyword(Token token) { return @@ -345,17 +363,16 @@ namespace TokenTraits token == Token::While || token == Token::For || token == Token::Continue || - token == Token::Break; - // TODO: see isExperimentalSolidityKeyword below - // || (token > Token::NonExperimentalEnd && token < Token::ExperimentalEnd); + token == Token::Break || + (token > Token::NonExperimentalEnd && token< Token::ExperimentalEnd); } - constexpr bool isExperimentalSolidityOnlyKeyword(Token) + constexpr bool isExperimentalSolidityOnlyKeyword(Token _token) { // TODO: use token > Token::NonExperimentalEnd && token < Token::ExperimentalEnd // as soon as other experimental tokens are added. For now the comparison generates // a warning from clang because it is always false. - return false; + return _token > Token::NonExperimentalEnd && _token < Token::ExperimentalEnd; } bool isYulKeyword(std::string const& _literal); diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 0a8c90f36bbb..3db857241f28 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -184,6 +184,34 @@ set(sources parsing/Parser.cpp parsing/Parser.h parsing/Token.h + experimental/analysis/Analysis.cpp + experimental/analysis/Analysis.h + experimental/analysis/DebugWarner.cpp + experimental/analysis/DebugWarner.h + experimental/analysis/FunctionDependencyAnalysis.cpp + experimental/analysis/FunctionDependencyAnalysis.h + experimental/analysis/TypeClassRegistration.cpp + experimental/analysis/TypeClassRegistration.h + experimental/analysis/TypeInference.cpp + experimental/analysis/TypeInference.h + experimental/analysis/TypeRegistration.cpp + experimental/analysis/TypeRegistration.h + experimental/analysis/SyntaxRestrictor.cpp + experimental/analysis/SyntaxRestrictor.h + experimental/ast/FunctionCallGraph.h + experimental/ast/Type.cpp + experimental/ast/Type.h + experimental/ast/TypeSystem.cpp + experimental/ast/TypeSystem.h + experimental/ast/TypeSystemHelper.cpp + experimental/ast/TypeSystemHelper.h + experimental/codegen/Common.h + experimental/codegen/Common.cpp + experimental/codegen/IRGenerationContext.h + experimental/codegen/IRGenerator.cpp + experimental/codegen/IRGenerator.h + experimental/codegen/IRGeneratorForStatements.cpp + experimental/codegen/IRGeneratorForStatements.h ) add_library(solidity ${sources}) diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index d6ff63dbf34f..94d0568118b9 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -38,11 +38,13 @@ namespace solidity::frontend NameAndTypeResolver::NameAndTypeResolver( GlobalContext& _globalContext, langutil::EVMVersion _evmVersion, - ErrorReporter& _errorReporter + ErrorReporter& _errorReporter, + bool _experimentalSolidity ): m_evmVersion(_evmVersion), m_errorReporter(_errorReporter), - m_globalContext(_globalContext) + m_globalContext(_globalContext), + m_experimentalSolidity(_experimentalSolidity) { m_scopes[nullptr] = std::make_shared(); for (Declaration const* declaration: _globalContext.declarations()) diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 6ba591e8ad16..a6cf5ed76096 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -59,7 +59,8 @@ class NameAndTypeResolver NameAndTypeResolver( GlobalContext& _globalContext, langutil::EVMVersion _evmVersion, - langutil::ErrorReporter& _errorReporter + langutil::ErrorReporter& _errorReporter, + bool _experimentalSolidity ); /// Registers all declarations found in the AST node, usually a source unit. /// @returns false in case of error. @@ -107,6 +108,7 @@ class NameAndTypeResolver /// Sets the current scope. void setScope(ASTNode const* _node); + bool experimentalSolidity() const { return m_experimentalSolidity; } private: /// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors. bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true); @@ -132,6 +134,7 @@ class NameAndTypeResolver DeclarationContainer* m_currentScope = nullptr; langutil::ErrorReporter& m_errorReporter; GlobalContext& m_globalContext; + bool m_experimentalSolidity = false; }; /** diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index e680021331a2..fd7def71459a 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -112,6 +112,21 @@ bool ReferencesResolver::visit(VariableDeclaration const& _varDecl) if (_varDecl.documentation()) resolveInheritDoc(*_varDecl.documentation(), _varDecl.annotation()); + if (m_resolver.experimentalSolidity()) + { + solAssert(!_varDecl.hasTypeName()); + if (_varDecl.typeExpression()) + { + ScopedSaveAndRestore typeContext{m_typeContext, true}; + _varDecl.typeExpression()->accept(*this); + } + if (_varDecl.overrides()) + _varDecl.overrides()->accept(*this); + if (_varDecl.value()) + _varDecl.value()->accept(*this); + return false; + } + return true; } @@ -120,6 +135,8 @@ bool ReferencesResolver::visit(Identifier const& _identifier) auto declarations = m_resolver.nameFromCurrentScope(_identifier.name()); if (declarations.empty()) { + if (m_resolver.experimentalSolidity() && m_typeContext) + return false; std::string suggestions = m_resolver.similarNameSuggestions(_identifier.name()); std::string errorMessage = "Undeclared identifier."; if (!suggestions.empty()) @@ -140,7 +157,7 @@ bool ReferencesResolver::visit(Identifier const& _identifier) bool ReferencesResolver::visit(FunctionDefinition const& _functionDefinition) { - m_returnParameters.push_back(_functionDefinition.returnParameterList().get()); + m_functionDefinitions.push_back(&_functionDefinition); if (_functionDefinition.documentation()) resolveInheritDoc(*_functionDefinition.documentation(), _functionDefinition.annotation()); @@ -150,13 +167,13 @@ bool ReferencesResolver::visit(FunctionDefinition const& _functionDefinition) void ReferencesResolver::endVisit(FunctionDefinition const&) { - solAssert(!m_returnParameters.empty(), ""); - m_returnParameters.pop_back(); + solAssert(!m_functionDefinitions.empty(), ""); + m_functionDefinitions.pop_back(); } bool ReferencesResolver::visit(ModifierDefinition const& _modifierDefinition) { - m_returnParameters.push_back(nullptr); + m_functionDefinitions.push_back(nullptr); if (_modifierDefinition.documentation()) resolveInheritDoc(*_modifierDefinition.documentation(), _modifierDefinition.annotation()); @@ -166,8 +183,8 @@ bool ReferencesResolver::visit(ModifierDefinition const& _modifierDefinition) void ReferencesResolver::endVisit(ModifierDefinition const&) { - solAssert(!m_returnParameters.empty(), ""); - m_returnParameters.pop_back(); + solAssert(!m_functionDefinitions.empty(), ""); + m_functionDefinitions.pop_back(); } void ReferencesResolver::endVisit(IdentifierPath const& _path) @@ -227,11 +244,30 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) bool ReferencesResolver::visit(Return const& _return) { - solAssert(!m_returnParameters.empty(), ""); - _return.annotation().functionReturnParameters = m_returnParameters.back(); + solAssert(!m_functionDefinitions.empty(), ""); + _return.annotation().function = m_functionDefinitions.back(); + _return.annotation().functionReturnParameters = m_functionDefinitions.back() ? m_functionDefinitions.back()->returnParameterList().get() : nullptr; return true; } +bool ReferencesResolver::visit(BinaryOperation const& _binaryOperation) +{ + if (m_resolver.experimentalSolidity()) + { + _binaryOperation.leftExpression().accept(*this); + if (_binaryOperation.getOperator() == Token::Colon) + { + ScopedSaveAndRestore typeContext(m_typeContext, !m_typeContext); + _binaryOperation.rightExpression().accept(*this); + } + else + _binaryOperation.rightExpression().accept(*this); + return false; + } + else + return ASTConstVisitor::visit(_binaryOperation); +} + void ReferencesResolver::operator()(yul::FunctionDefinition const& _function) { solAssert(nativeLocationOf(_function) == originLocationOf(_function), ""); @@ -252,6 +288,47 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) { solAssert(nativeLocationOf(_identifier) == originLocationOf(_identifier), ""); + if (m_resolver.experimentalSolidity()) + { + std::vector splitName; + boost::split(splitName, _identifier.name.str(), boost::is_any_of(".")); + solAssert(!splitName.empty()); + if (splitName.size() > 2) + { + m_errorReporter.declarationError( + 4955_error, + nativeLocationOf(_identifier), + "Unsupported identifier in inline assembly." + ); + return; + } + std::string name = splitName.front(); + auto declarations = m_resolver.nameFromCurrentScope(name); + switch (declarations.size()) + { + case 0: + if (splitName.size() > 1) + m_errorReporter.declarationError( + 7531_error, + nativeLocationOf(_identifier), + "Unsupported identifier in inline assembly." + ); + break; + case 1: + m_yulAnnotation->externalReferences[&_identifier].declaration = declarations.front(); + m_yulAnnotation->externalReferences[&_identifier].suffix = splitName.size() > 1 ? splitName.back() : ""; + break; + default: + m_errorReporter.declarationError( + 5387_error, + nativeLocationOf(_identifier), + "Multiple matching identifiers. Resolving overloaded identifiers is not supported." + ); + break; + } + return; + } + static std::set suffixes{"slot", "offset", "length", "address", "selector"}; std::string suffix; for (std::string const& s: suffixes) diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index 512a681b456b..13752e9c9345 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -85,6 +85,7 @@ class ReferencesResolver: private ASTConstVisitor, private yul::ASTWalker bool visit(InlineAssembly const& _inlineAssembly) override; bool visit(Return const& _return) override; bool visit(UsingForDirective const& _usingFor) override; + bool visit(BinaryOperation const& _binaryOperation) override; void operator()(yul::FunctionDefinition const& _function) override; void operator()(yul::Identifier const& _identifier) override; @@ -98,12 +99,13 @@ class ReferencesResolver: private ASTConstVisitor, private yul::ASTWalker langutil::ErrorReporter& m_errorReporter; NameAndTypeResolver& m_resolver; langutil::EVMVersion m_evmVersion; - /// Stack of return parameters. - std::vector m_returnParameters; + /// Stack of function definitions. + std::vector m_functionDefinitions; bool const m_resolveInsideCode; InlineAssemblyAnnotation* m_yulAnnotation = nullptr; bool m_yulInsideFunction = false; + bool m_typeContext = false; }; } diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index b2dd73cc244c..ac23ca36a8f4 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -443,7 +443,9 @@ bool SyntaxChecker::visit(UsingForDirective const& _usingFor) bool SyntaxChecker::visit(FunctionDefinition const& _function) { - solAssert(_function.isFree() == (m_currentContractKind == std::nullopt), ""); + if (m_sourceUnit && m_sourceUnit->experimentalSolidity()) + // Handled in experimental::SyntaxRestrictor instead. + return true; if (!_function.isFree() && !_function.isConstructor() && _function.noVisibilitySpecified()) { @@ -498,3 +500,13 @@ bool SyntaxChecker::visit(StructDefinition const& _struct) return true; } + +bool SyntaxChecker::visitNode(ASTNode const& _node) +{ + if (_node.experimentalSolidityOnly()) + { + solAssert(m_sourceUnit); + solAssert(m_sourceUnit->experimentalSolidity()); + } + return ASTConstVisitor::visitNode(_node); +} diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index f221df09fcac..69bda229f701 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -97,6 +97,8 @@ class SyntaxChecker: private ASTConstVisitor bool visit(StructDefinition const& _struct) override; bool visit(Literal const& _literal) override; + bool visitNode(ASTNode const&) override; + langutil::ErrorReporter& m_errorReporter; bool m_useYulOptimizer = false; diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index c0bf9009186d..bae16967595b 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -1057,3 +1057,15 @@ TryCatchClause const* TryStatement::errorClause() const { TryCatchClause const* TryStatement::fallbackClause() const { return findClause(m_clauses); } + +/// Experimental Solidity nodes +/// @{ +TypeClassDefinitionAnnotation& TypeClassDefinition::annotation() const +{ + return initAnnotation(); +} +TypeDeclarationAnnotation& TypeDefinition::annotation() const +{ + return initAnnotation(); +} +/// @} diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index ca23b0d61975..b9cc2118ef54 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -125,6 +125,8 @@ class ASTNode bool operator!=(ASTNode const& _other) const { return !operator==(_other); } ///@} + virtual bool experimentalSolidityOnly() const { return false; } + protected: size_t const m_id = 0; @@ -960,7 +962,8 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen ASTPointer const& _parameters, std::vector> _modifiers, ASTPointer const& _returnParameters, - ASTPointer const& _body + ASTPointer const& _body, + ASTPointer const& _experimentalReturnExpression = {} ): CallableDeclaration(_id, _location, _name, _nameLocation, _visibility, _parameters, _isVirtual, _overrides, _returnParameters), StructurallyDocumented(_documentation), @@ -969,10 +972,12 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen m_free(_free), m_kind(_kind), m_functionModifiers(std::move(_modifiers)), - m_body(_body) + m_body(_body), + m_experimentalReturnExpression(_experimentalReturnExpression) { solAssert(_kind == Token::Constructor || _kind == Token::Function || _kind == Token::Fallback || _kind == Token::Receive, ""); solAssert(isOrdinary() == !name().empty(), ""); + // TODO: assert _returnParameters implies non-experimental _experimentalReturnExpression implies experimental } void accept(ASTVisitor& _visitor) override; @@ -1030,12 +1035,15 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen ContractDefinition const* _searchStart = nullptr ) const override; + Expression const* experimentalReturnExpression() const { return m_experimentalReturnExpression.get(); } + private: StateMutability m_stateMutability; bool m_free; Token const m_kind; std::vector> m_functionModifiers; ASTPointer m_body; + ASTPointer m_experimentalReturnExpression; }; /** @@ -1070,7 +1078,8 @@ class VariableDeclaration: public Declaration, public StructurallyDocumented bool _isIndexed = false, Mutability _mutability = Mutability::Mutable, ASTPointer _overrides = nullptr, - Location _referenceLocation = Location::Unspecified + Location _referenceLocation = Location::Unspecified, + ASTPointer _typeExpression = {} ): Declaration(_id, _location, _name, std::move(_nameLocation), _visibility), StructurallyDocumented(std::move(_documentation)), @@ -1079,15 +1088,18 @@ class VariableDeclaration: public Declaration, public StructurallyDocumented m_isIndexed(_isIndexed), m_mutability(_mutability), m_overrides(std::move(_overrides)), - m_location(_referenceLocation) + m_location(_referenceLocation), + m_typeExpression(std::move(_typeExpression)) { - solAssert(m_typeName, ""); + // TODO: consider still asserting unless we are in experimental solidity. + // solAssert(m_typeName, ""); solAssert(!m_typeExpression, ""); } void accept(ASTVisitor& _visitor) override; void accept(ASTConstVisitor& _visitor) const override; + bool hasTypeName() const { return m_typeName != nullptr; } TypeName const& typeName() const { return *m_typeName; } ASTPointer const& value() const { return m_value; } @@ -1142,6 +1154,7 @@ class VariableDeclaration: public Declaration, public StructurallyDocumented /// @returns null when it is not accessible as a function. FunctionTypePointer functionType(bool /*_internal*/) const override; + ASTPointer const& typeExpression() const { return m_typeExpression; } VariableDeclarationAnnotation& annotation() const override; protected: @@ -1157,6 +1170,7 @@ class VariableDeclaration: public Declaration, public StructurallyDocumented Mutability m_mutability = Mutability::Mutable; ASTPointer m_overrides; ///< Contains the override specifier node Location m_location = Location::Unspecified; ///< Location of the variable if it is of reference type. + ASTPointer m_typeExpression; }; /** @@ -2138,7 +2152,8 @@ class BinaryOperation: public Expression ): Expression(_id, _location), m_left(std::move(_left)), m_operator(_operator), m_right(std::move(_right)) { - solAssert(TokenTraits::isBinaryOp(_operator) || TokenTraits::isCompareOp(_operator), ""); + // TODO: assert against colon for non-experimental solidity + solAssert(TokenTraits::isBinaryOp(_operator) || TokenTraits::isCompareOp(_operator) || _operator == Token::Colon || _operator == Token::RightArrow, ""); } void accept(ASTVisitor& _visitor) override; void accept(ASTConstVisitor& _visitor) const override; @@ -2449,4 +2464,167 @@ class Literal: public PrimaryExpression /// @} +/// Experimental Solidity nodes +/// @{ +class TypeClassDefinition: public Declaration, public StructurallyDocumented, public ScopeOpener +{ +public: + TypeClassDefinition( + int64_t _id, + SourceLocation const& _location, + ASTPointer _typeVariable, + ASTPointer const& _name, + SourceLocation _nameLocation, + ASTPointer const& _documentation, + std::vector> _subNodes + ): + Declaration(_id, _location, _name, std::move(_nameLocation)), + StructurallyDocumented(_documentation), + m_typeVariable(std::move(_typeVariable)), + m_subNodes(std::move(_subNodes)) + {} + + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + + VariableDeclaration const& typeVariable() const { return *m_typeVariable; } + std::vector> const& subNodes() const { return m_subNodes; } + + TypeClassDefinitionAnnotation& annotation() const override; + + Type const* type() const override { solAssert(false, "Requested type of experimental solidity node."); } + + bool experimentalSolidityOnly() const override { return true; } + +private: + ASTPointer m_typeVariable; + std::vector> m_subNodes; +}; + +class TypeClassInstantiation: public ASTNode, public ScopeOpener +{ +public: + TypeClassInstantiation( + int64_t _id, + SourceLocation const& _location, + ASTPointer _typeConstructor, + ASTPointer _argumentSorts, + ASTPointer _class, + std::vector> _subNodes + ): + ASTNode(_id, _location), + m_typeConstructor(std::move(_typeConstructor)), + m_argumentSorts(std::move(_argumentSorts)), + m_class(std::move(_class)), + m_subNodes(std::move(_subNodes)) + {} + + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + + TypeName const& typeConstructor() const { return *m_typeConstructor; } + ParameterList const* argumentSorts() const { return m_argumentSorts.get(); } + TypeClassName const& typeClass() const { return *m_class; } + std::vector> const& subNodes() const { return m_subNodes; } + + bool experimentalSolidityOnly() const override { return true; } + +private: + ASTPointer m_typeConstructor; + ASTPointer m_argumentSorts; + ASTPointer m_class; + std::vector> m_subNodes; +}; + +class TypeDefinition: public Declaration, public ScopeOpener +{ +public: + TypeDefinition( + int64_t _id, + SourceLocation const& _location, + ASTPointer _name, + SourceLocation _nameLocation, + ASTPointer _arguments, + ASTPointer _typeExpression + ): + Declaration(_id, _location, _name, std::move(_nameLocation), Visibility::Default), + m_arguments(std::move(_arguments)), + m_typeExpression(std::move(_typeExpression)) + { + } + + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + + Type const* type() const override { return nullptr; } + + TypeDeclarationAnnotation& annotation() const override; + + ParameterList const* arguments() const { return m_arguments.get(); } + Expression const* typeExpression() const { return m_typeExpression.get(); } + + bool experimentalSolidityOnly() const override { return true; } + +private: + ASTPointer m_arguments; + ASTPointer m_typeExpression; +}; + +class TypeClassName: public ASTNode +{ +public: + TypeClassName( + int64_t _id, + SourceLocation const& _location, + std::variant> _name + ): + ASTNode(_id, _location), + m_name(std::move(_name)) + { + if (Token const* token = std::get_if(&_name)) + solAssert(TokenTraits::isBuiltinTypeClassName(*token)); + } + + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + + bool experimentalSolidityOnly() const override { return true; } + + std::variant> name() const { return m_name; } + +private: + std::variant> m_name; +}; + +class Builtin: public Expression +{ +public: + Builtin( + int64_t _id, + SourceLocation _location, + ASTPointer _nameParameter, + SourceLocation _nameParameterLocation + ): + Expression(_id, std::move(_location)), + m_nameParameter(std::move(_nameParameter)), + m_nameParameterLocation(std::move(_nameParameterLocation)) + { + solAssert(m_nameParameter); + } + + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + + bool experimentalSolidityOnly() const override { return true; } + + ASTString const& nameParameter() const { return *m_nameParameter; } + SourceLocation const& nameParameterLocation() const { return m_nameParameterLocation; } + +private: + ASTPointer m_nameParameter; + SourceLocation m_nameParameterLocation; +}; + +/// @} + } diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 9a19907117eb..e68614f464c0 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -245,6 +245,8 @@ struct ReturnAnnotation: StatementAnnotation { /// Reference to the return parameters of the function. ParameterList const* functionReturnParameters = nullptr; + /// Reference to the function containing the return statement. + FunctionDefinition const* function = nullptr; }; struct TypeNameAnnotation: ASTAnnotation @@ -342,4 +344,12 @@ struct FunctionCallAnnotation: ExpressionAnnotation bool tryCall = false; }; +/// Experimental Solidity annotations. +/// Used to integrate with name and type resolution. +/// @{ +struct TypeClassDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation +{ +}; +/// @} + } diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index 79e6a4ecf217..f7c3d60237d7 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -99,6 +99,15 @@ class ElementaryTypeNameExpression; class Literal; class StructuredDocumentation; +/// Experimental Solidity nodes +/// @{ +class TypeClassDefinition; +class TypeClassInstantiation; +class TypeClassName; +class TypeDefinition; +class Builtin; +/// @} + class VariableScope; template diff --git a/libsolidity/ast/ASTJsonExporter.cpp b/libsolidity/ast/ASTJsonExporter.cpp index 98a368d1958f..c54a2d013ad0 100644 --- a/libsolidity/ast/ASTJsonExporter.cpp +++ b/libsolidity/ast/ASTJsonExporter.cpp @@ -1038,6 +1038,15 @@ void ASTJsonExporter::endVisit(EventDefinition const&) m_inEvent = false; } +bool ASTJsonExporter::visitNode(ASTNode const& _node) +{ + solAssert(false, _node.experimentalSolidityOnly() ? + "Attempt to export an AST of experimental solidity." : + "Attempt to export an AST that contains unexpected nodes." + ); + return false; +} + std::string ASTJsonExporter::location(VariableDeclaration::Location _location) { switch (_location) diff --git a/libsolidity/ast/ASTJsonExporter.h b/libsolidity/ast/ASTJsonExporter.h index 71d15ce0a5f2..ff3b94fa28de 100644 --- a/libsolidity/ast/ASTJsonExporter.h +++ b/libsolidity/ast/ASTJsonExporter.h @@ -130,6 +130,7 @@ class ASTJsonExporter: public ASTConstVisitor void endVisit(EventDefinition const&) override; + bool visitNode(ASTNode const& _node) override; private: void setJsonNode( ASTNode const& _node, diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index ef03e6339322..f9196d6b95c8 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -109,6 +109,14 @@ class ASTVisitor virtual bool visit(ElementaryTypeNameExpression& _node) { return visitNode(_node); } virtual bool visit(Literal& _node) { return visitNode(_node); } virtual bool visit(StructuredDocumentation& _node) { return visitNode(_node); } + /// Experimental Solidity nodes + /// @{ + virtual bool visit(TypeClassDefinition& _node) { return visitNode(_node); } + virtual bool visit(TypeClassInstantiation& _node) { return visitNode(_node); } + virtual bool visit(TypeDefinition& _node) { return visitNode(_node); } + virtual bool visit(TypeClassName& _node) { return visitNode(_node); } + virtual bool visit(Builtin& _node) { return visitNode(_node); } + /// @} virtual void endVisit(SourceUnit& _node) { endVisitNode(_node); } virtual void endVisit(PragmaDirective& _node) { endVisitNode(_node); } @@ -165,6 +173,14 @@ class ASTVisitor virtual void endVisit(ElementaryTypeNameExpression& _node) { endVisitNode(_node); } virtual void endVisit(Literal& _node) { endVisitNode(_node); } virtual void endVisit(StructuredDocumentation& _node) { endVisitNode(_node); } + /// Experimental Solidity nodes + /// @{ + virtual void endVisit(TypeClassDefinition& _node) { endVisitNode(_node); } + virtual void endVisit(TypeClassInstantiation& _node) { endVisitNode(_node); } + virtual void endVisit(TypeDefinition& _node) { endVisitNode(_node); } + virtual void endVisit(TypeClassName& _node) { endVisitNode(_node); } + virtual void endVisit(Builtin& _node) { endVisitNode(_node); } + /// @} protected: /// Generic function called by default for each node, to be overridden by derived classes @@ -243,6 +259,14 @@ class ASTConstVisitor virtual bool visit(ElementaryTypeNameExpression const& _node) { return visitNode(_node); } virtual bool visit(Literal const& _node) { return visitNode(_node); } virtual bool visit(StructuredDocumentation const& _node) { return visitNode(_node); } + /// Experimental Solidity nodes + /// @{ + virtual bool visit(TypeClassDefinition const& _node) { return visitNode(_node); } + virtual bool visit(TypeClassInstantiation const& _node) { return visitNode(_node); } + virtual bool visit(TypeDefinition const& _node) { return visitNode(_node); } + virtual bool visit(TypeClassName const& _node) { return visitNode(_node); } + virtual bool visit(Builtin const& _node) { return visitNode(_node); } + /// @} virtual void endVisit(SourceUnit const& _node) { endVisitNode(_node); } virtual void endVisit(PragmaDirective const& _node) { endVisitNode(_node); } @@ -299,6 +323,14 @@ class ASTConstVisitor virtual void endVisit(ElementaryTypeNameExpression const& _node) { endVisitNode(_node); } virtual void endVisit(Literal const& _node) { endVisitNode(_node); } virtual void endVisit(StructuredDocumentation const& _node) { endVisitNode(_node); } + /// Experimental Solidity nodes + /// @{ + virtual void endVisit(TypeClassDefinition const& _node) { endVisitNode(_node); } + virtual void endVisit(TypeClassInstantiation const& _node) { endVisitNode(_node); } + virtual void endVisit(TypeDefinition const& _node) { endVisitNode(_node); } + virtual void endVisit(TypeClassName const& _node) { endVisitNode(_node); } + virtual void endVisit(Builtin const& _node) { endVisitNode(_node); } + /// @} protected: /// Generic function called by default for each node, to be overridden by derived classes diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 78dc0e5dddda..be9de64db7a2 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -265,6 +265,8 @@ void FunctionDefinition::accept(ASTVisitor& _visitor) m_parameters->accept(_visitor); if (m_returnParameters) m_returnParameters->accept(_visitor); + if (m_experimentalReturnExpression) + m_experimentalReturnExpression->accept(_visitor); listAccept(m_functionModifiers, _visitor); if (m_body) m_body->accept(_visitor); @@ -283,6 +285,8 @@ void FunctionDefinition::accept(ASTConstVisitor& _visitor) const m_parameters->accept(_visitor); if (m_returnParameters) m_returnParameters->accept(_visitor); + if (m_experimentalReturnExpression) + m_experimentalReturnExpression->accept(_visitor); listAccept(m_functionModifiers, _visitor); if (m_body) m_body->accept(_visitor); @@ -296,6 +300,8 @@ void VariableDeclaration::accept(ASTVisitor& _visitor) { if (m_typeName) m_typeName->accept(_visitor); + if (m_typeExpression) + m_typeExpression->accept(_visitor); if (m_overrides) m_overrides->accept(_visitor); if (m_value) @@ -310,6 +316,8 @@ void VariableDeclaration::accept(ASTConstVisitor& _visitor) const { if (m_typeName) m_typeName->accept(_visitor); + if (m_typeExpression) + m_typeExpression->accept(_visitor); if (m_overrides) m_overrides->accept(_visitor); if (m_value) @@ -1024,4 +1032,114 @@ void Literal::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +/// Experimental Solidity nodes +/// @{ +void TypeClassDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_typeVariable->accept(_visitor); + if (m_documentation) + m_documentation->accept(_visitor); + listAccept(m_subNodes, _visitor); + } + _visitor.endVisit(*this); +} + +void TypeClassDefinition::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_typeVariable->accept(_visitor); + if (m_documentation) + m_documentation->accept(_visitor); + listAccept(m_subNodes, _visitor); + } + _visitor.endVisit(*this); +} + +void TypeClassInstantiation::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_typeConstructor->accept(_visitor); + if (m_argumentSorts) + m_argumentSorts->accept(_visitor); + m_class->accept(_visitor); + listAccept(m_subNodes, _visitor); + } + _visitor.endVisit(*this); +} + +void TypeClassInstantiation::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_typeConstructor->accept(_visitor); + if (m_argumentSorts) + m_argumentSorts->accept(_visitor); + m_class->accept(_visitor); + listAccept(m_subNodes, _visitor); + } + _visitor.endVisit(*this); +} + +void TypeDefinition::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + if (m_arguments) + m_arguments->accept(_visitor); + if (m_typeExpression) + m_typeExpression->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void TypeDefinition::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + if (m_arguments) + m_arguments->accept(_visitor); + if (m_typeExpression) + m_typeExpression->accept(_visitor); + } + _visitor.endVisit(*this); +} + + +void TypeClassName::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + if (auto* path = std::get_if>(&m_name)) + (*path)->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void TypeClassName::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + if (auto* path = std::get_if>(&m_name)) + (*path)->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void Builtin::accept(ASTVisitor& _visitor) +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} + +void Builtin::accept(ASTConstVisitor& _visitor) const +{ + _visitor.visit(*this); + _visitor.endVisit(*this); +} +/// @} + } diff --git a/libsolidity/experimental/analysis/Analysis.cpp b/libsolidity/experimental/analysis/Analysis.cpp index 672aa4f87958..441700675f5f 100644 --- a/libsolidity/experimental/analysis/Analysis.cpp +++ b/libsolidity/experimental/analysis/Analysis.cpp @@ -16,19 +16,189 @@ */ // SPDX-License-Identifier: GPL-3.0 #include - -#include +#include +#include +#include +#include +#include +#include using namespace solidity::langutil; using namespace solidity::frontend::experimental; -bool Analysis::check(std::vector> const&) +// TODO: creating all of them for all nodes up front may be wasteful, we should improve the mechanism. +struct Analysis::AnnotationContainer +{ + TypeClassRegistration::Annotation typeClassRegistrationAnnotation; + TypeRegistration::Annotation typeRegistrationAnnotation; + TypeInference::Annotation typeInferenceAnnotation; +}; + +struct Analysis::GlobalAnnotationContainer +{ + FunctionDependencyAnalysis::GlobalAnnotation functionDependencyGraphAnnotation; + TypeClassRegistration::GlobalAnnotation typeClassRegistrationAnnotation; + TypeRegistration::GlobalAnnotation typeRegistrationAnnotation; + TypeInference::GlobalAnnotation typeInferenceAnnotation; +}; + +template<> +TypeClassRegistration::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher::get(ASTNode const& _node) +{ + return analysis.annotationContainer(_node).typeClassRegistrationAnnotation; +} + +template<> +TypeClassRegistration::GlobalAnnotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get() const +{ + return analysis.annotationContainer().typeClassRegistrationAnnotation; +} + +template<> +TypeClassRegistration::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher::get() +{ + return analysis.annotationContainer().typeClassRegistrationAnnotation; +} + +template<> +TypeClassRegistration::Annotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get(ASTNode const& _node) const +{ + return analysis.annotationContainer(_node).typeClassRegistrationAnnotation; +} + +template<> +FunctionDependencyAnalysis::GlobalAnnotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get() const +{ + return analysis.annotationContainer().functionDependencyGraphAnnotation; +} + +template<> +FunctionDependencyAnalysis::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher::get() +{ + return analysis.annotationContainer().functionDependencyGraphAnnotation; +} + +template<> +TypeRegistration::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher::get(ASTNode const& _node) +{ + return analysis.annotationContainer(_node).typeRegistrationAnnotation; +} + +template<> +TypeRegistration::GlobalAnnotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get() const +{ + return analysis.annotationContainer().typeRegistrationAnnotation; +} + +template<> +TypeRegistration::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher::get() +{ + return analysis.annotationContainer().typeRegistrationAnnotation; +} + +template<> +TypeRegistration::Annotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get(ASTNode const& _node) const { - m_errorReporter.error( - 6547_error, - Error::Type::UnimplementedFeatureError, - SourceLocation{}, - "Experimental Analysis is not implemented yet." - ); - return false; + return analysis.annotationContainer(_node).typeRegistrationAnnotation; +} + +template<> +TypeInference::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher::get(ASTNode const& _node) +{ + return analysis.annotationContainer(_node).typeInferenceAnnotation; +} + +template<> +TypeInference::Annotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get(ASTNode const& _node) const +{ + return analysis.annotationContainer(_node).typeInferenceAnnotation; +} + +template<> +TypeInference::GlobalAnnotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher::get() const +{ + return analysis.annotationContainer().typeInferenceAnnotation; +} + +template<> +TypeInference::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher::get() +{ + return analysis.annotationContainer().typeInferenceAnnotation; +} + +Analysis::AnnotationContainer& Analysis::annotationContainer(ASTNode const& _node) +{ + solAssert(_node.id() > 0); + size_t id = static_cast(_node.id()); + solAssert(id <= m_maxAstId); + return m_annotations[id]; +} + +Analysis::AnnotationContainer const& Analysis::annotationContainer(ASTNode const& _node) const +{ + solAssert(_node.id() > 0); + size_t id = static_cast(_node.id()); + solAssert(id <= m_maxAstId); + return m_annotations[id]; +} + +Analysis::Analysis(langutil::ErrorReporter& _errorReporter, uint64_t _maxAstId): + m_errorReporter(_errorReporter), + m_maxAstId(_maxAstId), + m_annotations(std::make_unique(static_cast(_maxAstId + 1))), + m_globalAnnotation(std::make_unique()) +{ +} + +Analysis::~Analysis() +{} + +template +std::tuple...> makeIndexTuple(std::index_sequence) { + return std::make_tuple( std::integral_constant{}...); +} + +bool Analysis::check(std::vector> const& _sourceUnits) +{ + using AnalysisSteps = std::tuple< + SyntaxRestrictor, + TypeClassRegistration, + TypeRegistration, + // TODO move after step introduced in https://github.com/ethereum/solidity/pull/14578, but before TypeInference + FunctionDependencyAnalysis, + TypeInference, + DebugWarner + >; + + return std::apply([&](auto... _indexTuple) { + return ([&](auto&& _step) { + for (auto source: _sourceUnits) + if (!_step.analyze(*source)) + return false; + return true; + }(std::tuple_element_t{*this}) && ...); + }, makeIndexTuple(std::make_index_sequence>{})); + +/* + { + SyntaxRestrictor syntaxRestrictor{*this}; + for (auto source: _sourceUnits) + if (!syntaxRestrictor.analyze(*source)) + return false; + } + + { + TypeRegistration typeRegistration{*this}; + for (auto source: _sourceUnits) + if (!typeRegistration.analyze(*source)) + return false; + } + { + TypeInference typeInference{*this}; + for (auto source: _sourceUnits) + if (!typeInference.analyze(*source)) + return false; + } + return true; + */ } diff --git a/libsolidity/experimental/analysis/Analysis.h b/libsolidity/experimental/analysis/Analysis.h index 729ff2b93d33..dde3a24efb91 100644 --- a/libsolidity/experimental/analysis/Analysis.h +++ b/libsolidity/experimental/analysis/Analysis.h @@ -17,12 +17,16 @@ // SPDX-License-Identifier: GPL-3.0 #pragma once -#include +#include + +#include #include +#include namespace solidity::frontend { class SourceUnit; +class ASTNode; } namespace solidity::langutil @@ -33,17 +37,79 @@ class ErrorReporter; namespace solidity::frontend::experimental { +class TypeSystem; + +class Analysis; + +namespace detail +{ +template +struct AnnotationFetcher +{ + Analysis& analysis; + typename Step::Annotation& get(ASTNode const& _node); + typename Step::GlobalAnnotation& get(); +}; +template +struct ConstAnnotationFetcher +{ + Analysis const& analysis; + typename Step::Annotation const& get(ASTNode const& _node) const; + typename Step::GlobalAnnotation const& get() const; +}; +} + class Analysis { -public: - Analysis(langutil::ErrorReporter& _errorReporter): - m_errorReporter(_errorReporter) - {} + struct AnnotationContainer; + struct GlobalAnnotationContainer; +public: + Analysis(langutil::ErrorReporter& _errorReporter, uint64_t _maxAstId); + Analysis(Analysis const&) = delete; + ~Analysis(); + Analysis const& operator=(Analysis const&) = delete; bool check(std::vector> const& _sourceUnits); + langutil::ErrorReporter& errorReporter() { return m_errorReporter; } + uint64_t maxAstId() const { return m_maxAstId; } + TypeSystem& typeSystem() { return m_typeSystem; } + TypeSystem const& typeSystem() const { return m_typeSystem; } + + template + typename Step::Annotation& annotation(ASTNode const& _node) + { + return detail::AnnotationFetcher{*this}.get(_node); + } + + template + typename Step::Annotation const& annotation(ASTNode const& _node) const + { + return detail::ConstAnnotationFetcher{*this}.get(_node); + } + + template + typename Step::GlobalAnnotation& annotation() + { + return detail::AnnotationFetcher{*this}.get(); + } + + template + typename Step::GlobalAnnotation const& annotation() const + { + return detail::ConstAnnotationFetcher{*this}.get(); + } + + AnnotationContainer& annotationContainer(ASTNode const& _node); + AnnotationContainer const& annotationContainer(ASTNode const& _node) const; + GlobalAnnotationContainer& annotationContainer() { return *m_globalAnnotation; } + GlobalAnnotationContainer const& annotationContainer() const { return *m_globalAnnotation; } private: langutil::ErrorReporter& m_errorReporter; + TypeSystem m_typeSystem; + uint64_t m_maxAstId = 0; + std::unique_ptr m_annotations; + std::unique_ptr m_globalAnnotation; }; } diff --git a/libsolidity/experimental/analysis/DebugWarner.cpp b/libsolidity/experimental/analysis/DebugWarner.cpp new file mode 100644 index 000000000000..2b5c23b9bed0 --- /dev/null +++ b/libsolidity/experimental/analysis/DebugWarner.cpp @@ -0,0 +1,50 @@ +/* + 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 + +using namespace solidity::frontend; +using namespace solidity::frontend::experimental; +using namespace solidity::langutil; + +DebugWarner::DebugWarner(Analysis& _analysis): m_analysis(_analysis), m_errorReporter(_analysis.errorReporter()) +{} + +bool DebugWarner::analyze(ASTNode const& _astRoot) +{ + _astRoot.accept(*this); + return !Error::containsErrors(m_errorReporter.errors()); +} + +bool DebugWarner::visitNode(ASTNode const& _node) +{ + std::optional const& inferredType = m_analysis.annotation(_node).type; + if (inferredType.has_value()) + m_errorReporter.info( + 4164_error, + _node.location(), + "Inferred type: " + TypeEnvironmentHelpers{m_analysis.typeSystem().env()}.typeToString(*inferredType) + ); + return true; +} diff --git a/libsolidity/experimental/analysis/DebugWarner.h b/libsolidity/experimental/analysis/DebugWarner.h new file mode 100644 index 000000000000..a7b5ac6d017b --- /dev/null +++ b/libsolidity/experimental/analysis/DebugWarner.h @@ -0,0 +1,42 @@ +/* + 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::frontend::experimental +{ +class Analysis; + +class DebugWarner: public ASTConstVisitor +{ +public: + DebugWarner(Analysis& _analysis); + + bool analyze(ASTNode const& _astRoot); + +private: + bool visitNode(ASTNode const& _node) override; + + Analysis& m_analysis; + langutil::ErrorReporter& m_errorReporter; +}; + +} diff --git a/libsolidity/experimental/analysis/FunctionDependencyAnalysis.cpp b/libsolidity/experimental/analysis/FunctionDependencyAnalysis.cpp new file mode 100644 index 000000000000..332f7620c9b6 --- /dev/null +++ b/libsolidity/experimental/analysis/FunctionDependencyAnalysis.cpp @@ -0,0 +1,72 @@ +/* + 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 + +using namespace solidity::frontend::experimental; +using namespace solidity::util; + +FunctionDependencyAnalysis::FunctionDependencyAnalysis(Analysis& _analysis): + m_analysis(_analysis), + m_errorReporter(_analysis.errorReporter()) +{ +} + +bool FunctionDependencyAnalysis::analyze(SourceUnit const& _sourceUnit) +{ + _sourceUnit.accept(*this); + return !m_errorReporter.hasErrors(); +} + +bool FunctionDependencyAnalysis::visit(FunctionDefinition const& _functionDefinition) +{ + solAssert(!m_currentFunction); + m_currentFunction = &_functionDefinition; + // Insert a function definition pointer that maps to an empty set; the pointed to set will later be + // populated in ``endVisit(Identifier const& _identifier)`` if ``m_currentFunction`` references another. + auto [_, inserted] = annotation().functionCallGraph.edges.try_emplace( + m_currentFunction, std::set>{} + ); + solAssert(inserted); + return true; +} + +void FunctionDependencyAnalysis::endVisit(FunctionDefinition const&) +{ + m_currentFunction = nullptr; +} + +void FunctionDependencyAnalysis::endVisit(Identifier const& _identifier) +{ + auto const* callee = dynamic_cast(_identifier.annotation().referencedDeclaration); + // Check that the identifier is within a function body and is a function, and add it to the graph + // as an ``m_currentFunction`` -> ``callee`` edge. + if (m_currentFunction && callee) + addEdge(m_currentFunction, callee); +} + +void FunctionDependencyAnalysis::addEdge(FunctionDefinition const* _caller, FunctionDefinition const* _callee) +{ + annotation().functionCallGraph.edges[_caller].insert(_callee); +} + +FunctionDependencyAnalysis::GlobalAnnotation& FunctionDependencyAnalysis::annotation() +{ + return m_analysis.annotation(); +} diff --git a/libsolidity/experimental/analysis/FunctionDependencyAnalysis.h b/libsolidity/experimental/analysis/FunctionDependencyAnalysis.h new file mode 100644 index 000000000000..acd3f1b213fa --- /dev/null +++ b/libsolidity/experimental/analysis/FunctionDependencyAnalysis.h @@ -0,0 +1,57 @@ +/* + 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::frontend::experimental +{ + +class Analysis; + +class FunctionDependencyAnalysis: private ASTConstVisitor +{ +public: + FunctionDependencyAnalysis(Analysis& _analysis); + bool analyze(SourceUnit const& _sourceUnit); + + struct Annotation {}; + struct GlobalAnnotation + { + FunctionDependencyGraph functionCallGraph; + }; + +private: + bool visit(FunctionDefinition const& _functionDefinition) override; + void endVisit(FunctionDefinition const&) override; + void endVisit(Identifier const& _identifier) override; + void addEdge(FunctionDefinition const* _caller, FunctionDefinition const* _callee); + GlobalAnnotation& annotation(); + + Analysis& m_analysis; + langutil::ErrorReporter& m_errorReporter; + FunctionDefinition const* m_currentFunction = nullptr; +}; + +} diff --git a/libsolidity/experimental/analysis/SyntaxRestrictor.cpp b/libsolidity/experimental/analysis/SyntaxRestrictor.cpp new file mode 100644 index 000000000000..e93fddcf50ef --- /dev/null +++ b/libsolidity/experimental/analysis/SyntaxRestrictor.cpp @@ -0,0 +1,113 @@ +/* + 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::frontend; +using namespace solidity::frontend::experimental; +using namespace solidity::langutil; + +SyntaxRestrictor::SyntaxRestrictor(Analysis& _analysis): m_errorReporter(_analysis.errorReporter()) +{} + +bool SyntaxRestrictor::analyze(ASTNode const& _astRoot) +{ + _astRoot.accept(*this); + return !Error::containsErrors(m_errorReporter.errors()); +} + +bool SyntaxRestrictor::visitNode(ASTNode const& _node) +{ + if (!_node.experimentalSolidityOnly()) + m_errorReporter.syntaxError(9282_error, _node.location(), "Unsupported AST node."); + return false; +} + +bool SyntaxRestrictor::visit(ContractDefinition const& _contractDefinition) +{ + if (_contractDefinition.contractKind() != ContractKind::Contract) + m_errorReporter.syntaxError(9159_error, _contractDefinition.location(), "Only contracts are supported."); + if (!_contractDefinition.baseContracts().empty()) + m_errorReporter.syntaxError(5731_error, _contractDefinition.location(), "Inheritance unsupported."); + return true; +} + +bool SyntaxRestrictor::visit(FunctionDefinition const& _functionDefinition) +{ + if (!_functionDefinition.isImplemented()) + m_errorReporter.syntaxError(1741_error, _functionDefinition.location(), "Functions must be implemented."); + if (!_functionDefinition.modifiers().empty()) + m_errorReporter.syntaxError(9988_error, _functionDefinition.location(), "Function may not have modifiers."); + if (_functionDefinition.overrides()) + m_errorReporter.syntaxError(5044_error, _functionDefinition.location(), "Function may not have override specifiers."); + solAssert(!_functionDefinition.returnParameterList()); + if (_functionDefinition.isFree()) + { + if (_functionDefinition.stateMutability() != StateMutability::NonPayable) + m_errorReporter.syntaxError(5714_error, _functionDefinition.location(), "Free functions may not have a mutability."); + } + else + { + if (_functionDefinition.isFallback()) + { + if (_functionDefinition.visibility() != Visibility::External) + m_errorReporter.syntaxError(7341_error, _functionDefinition.location(), "Fallback function must be external."); + } + else + m_errorReporter.syntaxError(4496_error, _functionDefinition.location(), "Only fallback functions are supported in contracts."); + } + + return true; +} + +bool SyntaxRestrictor::visit(VariableDeclarationStatement const& _variableDeclarationStatement) +{ + if (_variableDeclarationStatement.declarations().size() == 1) + { + if (!_variableDeclarationStatement.declarations().front()) + m_errorReporter.syntaxError(9658_error, _variableDeclarationStatement.initialValue()->location(), "Variable declaration has to declare a single variable."); + } + else + m_errorReporter.syntaxError(3520_error, _variableDeclarationStatement.initialValue()->location(), "Variable declarations can only declare a single variable."); + return true; +} + +bool SyntaxRestrictor::visit(VariableDeclaration const& _variableDeclaration) +{ + if (_variableDeclaration.value()) + m_errorReporter.syntaxError(1801_error, _variableDeclaration.value()->location(), "Variable declarations with initial value not supported."); + if (_variableDeclaration.isStateVariable()) + m_errorReporter.syntaxError(6388_error, _variableDeclaration.location(), "State variables are not supported."); + if (!_variableDeclaration.isLocalVariable()) + m_errorReporter.syntaxError(8953_error, _variableDeclaration.location(), "Only local variables are supported."); + if (_variableDeclaration.mutability() != VariableDeclaration::Mutability::Mutable) + m_errorReporter.syntaxError(2934_error, _variableDeclaration.location(), "Only mutable variables are supported."); + if (_variableDeclaration.isIndexed()) + m_errorReporter.syntaxError(9603_error, _variableDeclaration.location(), "Indexed variables are not supported."); + if (!_variableDeclaration.noVisibilitySpecified()) + m_errorReporter.syntaxError(8809_error, _variableDeclaration.location(), "Variables with visibility not supported."); + if (_variableDeclaration.overrides()) + m_errorReporter.syntaxError(6175_error, _variableDeclaration.location(), "Variables with override specifier not supported."); + if (_variableDeclaration.referenceLocation() != VariableDeclaration::Location::Unspecified) + m_errorReporter.syntaxError(5360_error, _variableDeclaration.location(), "Variables with reference location not supported."); + return true; +} diff --git a/libsolidity/experimental/analysis/SyntaxRestrictor.h b/libsolidity/experimental/analysis/SyntaxRestrictor.h new file mode 100644 index 000000000000..86d2d1b496e3 --- /dev/null +++ b/libsolidity/experimental/analysis/SyntaxRestrictor.h @@ -0,0 +1,67 @@ +/* + 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::frontend::experimental +{ +class Analysis; + +class SyntaxRestrictor: public ASTConstVisitor +{ +public: + SyntaxRestrictor(Analysis& _analysis); + + bool analyze(ASTNode const& _astRoot); + +private: + /// Default visit will reject all AST nodes that are not explicitly allowed. + bool visitNode(ASTNode const& _node) override; + + bool visit(SourceUnit const&) override { return true; } + bool visit(PragmaDirective const&) override { return true; } + bool visit(ImportDirective const&) override { return true; } + bool visit(ContractDefinition const& _contractDefinition) override; + bool visit(FunctionDefinition const& _functionDefinition) override; + bool visit(ExpressionStatement const&) override { return true; } + bool visit(FunctionCall const&) override { return true; } + bool visit(Assignment const&) override { return true; } + bool visit(Block const&) override { return true; } + bool visit(InlineAssembly const&) override { return true; } + bool visit(Identifier const&) override { return true; } + bool visit(IdentifierPath const&) override { return true; } + bool visit(IfStatement const&) override { return true; } + bool visit(VariableDeclarationStatement const&) override; + bool visit(VariableDeclaration const&) override; + bool visit(ElementaryTypeName const&) override { return true; } + bool visit(ParameterList const&) override { return true; } + bool visit(Return const&) override { return true; } + bool visit(MemberAccess const&) override { return true; } + bool visit(BinaryOperation const&) override { return true; } + bool visit(ElementaryTypeNameExpression const&) override { return true; } + bool visit(TupleExpression const&) override { return true; } + bool visit(Literal const&) override { return true; } + + langutil::ErrorReporter& m_errorReporter; +}; + +} diff --git a/libsolidity/experimental/analysis/TypeClassRegistration.cpp b/libsolidity/experimental/analysis/TypeClassRegistration.cpp new file mode 100644 index 000000000000..fe70a44d1e27 --- /dev/null +++ b/libsolidity/experimental/analysis/TypeClassRegistration.cpp @@ -0,0 +1,62 @@ +/* + 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::frontend::experimental; +using namespace solidity::langutil; + +TypeClassRegistration::TypeClassRegistration(Analysis& _analysis): + m_analysis(_analysis), + m_errorReporter(_analysis.errorReporter()), + m_typeSystem(_analysis.typeSystem()) +{ +} + +bool TypeClassRegistration::analyze(SourceUnit const& _sourceUnit) +{ + _sourceUnit.accept(*this); + return !m_errorReporter.hasErrors(); +} + +bool TypeClassRegistration::visit(TypeClassDefinition const& _typeClassDefinition) +{ + std::variant typeClassOrError = m_typeSystem.declareTypeClass( + _typeClassDefinition.name(), + &_typeClassDefinition + ); + + m_analysis.annotation(_typeClassDefinition).typeClass = std::visit( + util::GenericVisitor{ + [](TypeClass _class) -> TypeClass { return _class; }, + [&](std::string _error) -> TypeClass { + m_errorReporter.fatalTypeError(4767_error, _typeClassDefinition.location(), _error); + util::unreachable(); + } + }, + typeClassOrError + ); + + return true; +} diff --git a/libsolidity/experimental/analysis/TypeClassRegistration.h b/libsolidity/experimental/analysis/TypeClassRegistration.h new file mode 100644 index 000000000000..c0c1c3549bba --- /dev/null +++ b/libsolidity/experimental/analysis/TypeClassRegistration.h @@ -0,0 +1,58 @@ +/* + 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::langutil +{ +class ErrorReporter; +} + +namespace solidity::frontend::experimental +{ + +class Analysis; +class TypeSystem; + +class TypeClassRegistration: public ASTConstVisitor +{ +public: + struct Annotation + { + // Type classes. + std::optional typeClass; + }; + struct GlobalAnnotation + { + }; + + TypeClassRegistration(Analysis& _analysis); + + bool analyze(SourceUnit const& _sourceUnit); + +private: + bool visit(TypeClassDefinition const& _typeClassDefinition) override; + + Analysis& m_analysis; + langutil::ErrorReporter& m_errorReporter; + TypeSystem& m_typeSystem; +}; + +} diff --git a/libsolidity/experimental/analysis/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp new file mode 100644 index 000000000000..a13d6ebc9403 --- /dev/null +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -0,0 +1,1211 @@ +/* + 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 + +using namespace solidity; +using namespace solidity::frontend; +using namespace solidity::frontend::experimental; +using namespace solidity::langutil; + +TypeInference::TypeInference(Analysis& _analysis): + m_analysis(_analysis), + m_errorReporter(_analysis.errorReporter()), + m_typeSystem(_analysis.typeSystem()), + m_env(&m_typeSystem.env()), + m_voidType(m_typeSystem.type(PrimitiveType::Void, {})), + m_wordType(m_typeSystem.type(PrimitiveType::Word, {})), + m_integerType(m_typeSystem.type(PrimitiveType::Integer, {})), + m_unitType(m_typeSystem.type(PrimitiveType::Unit, {})), + m_boolType(m_typeSystem.type(PrimitiveType::Bool, {})) +{ + TypeSystemHelpers helper{m_typeSystem}; + + auto declareBuiltinClass = [&](std::string _name, BuiltinClass _class) -> TypeClass { + auto result = m_typeSystem.declareTypeClass(_name, nullptr); + if (auto error = std::get_if(&result)) + solAssert(!error, *error); + TypeClass declaredClass = std::get(result); + // TODO: validation? + solAssert(annotation().builtinClassesByName.emplace(_name, _class).second); + return annotation().builtinClasses.emplace(_class, declaredClass).first->second; + }; + + auto registeredTypeClass = [&](BuiltinClass _builtinClass) -> TypeClass { + return annotation().builtinClasses.at(_builtinClass); + }; + + auto defineConversion = [&](BuiltinClass _builtinClass, PrimitiveType _fromType, std::string _functionName) { + annotation().typeClassFunctions[registeredTypeClass(_builtinClass)] = {{ + std::move(_functionName), + helper.functionType( + m_typeSystem.type(_fromType, {}), + m_typeSystem.typeClassInfo(registeredTypeClass(_builtinClass)).typeVariable + ), + }}; + }; + + auto defineBinaryMonoidalOperator = [&](BuiltinClass _builtinClass, Token _token, std::string _functionName) { + Type typeVar = m_typeSystem.typeClassInfo(registeredTypeClass(_builtinClass)).typeVariable; + annotation().operators.emplace(_token, std::make_tuple(registeredTypeClass(_builtinClass), _functionName)); + annotation().typeClassFunctions[registeredTypeClass(_builtinClass)] = {{ + std::move(_functionName), + helper.functionType( + helper.tupleType({typeVar, typeVar}), + typeVar + ) + }}; + }; + + auto defineBinaryCompareOperator = [&](BuiltinClass _builtinClass, Token _token, std::string _functionName) { + Type typeVar = m_typeSystem.typeClassInfo(registeredTypeClass(_builtinClass)).typeVariable; + annotation().operators.emplace(_token, std::make_tuple(registeredTypeClass(_builtinClass), _functionName)); + annotation().typeClassFunctions[registeredTypeClass(_builtinClass)] = {{ + std::move(_functionName), + helper.functionType( + helper.tupleType({typeVar, typeVar}), + m_typeSystem.type(PrimitiveType::Bool, {}) + ) + }}; + }; + + declareBuiltinClass("integer", BuiltinClass::Integer); + declareBuiltinClass("*", BuiltinClass::Mul); + declareBuiltinClass("+", BuiltinClass::Add); + declareBuiltinClass("==", BuiltinClass::Equal); + declareBuiltinClass("<", BuiltinClass::Less); + declareBuiltinClass("<=", BuiltinClass::LessOrEqual); + declareBuiltinClass(">", BuiltinClass::Greater); + declareBuiltinClass(">=", BuiltinClass::GreaterOrEqual); + + defineConversion(BuiltinClass::Integer, PrimitiveType::Integer, "fromInteger"); + + defineBinaryMonoidalOperator(BuiltinClass::Mul, Token::Mul, "mul"); + defineBinaryMonoidalOperator(BuiltinClass::Add, Token::Add, "add"); + + defineBinaryCompareOperator(BuiltinClass::Equal, Token::Equal, "eq"); + defineBinaryCompareOperator(BuiltinClass::Less, Token::LessThan, "lt"); + defineBinaryCompareOperator(BuiltinClass::LessOrEqual, Token::LessThanOrEqual, "leq"); + defineBinaryCompareOperator(BuiltinClass::Greater, Token::GreaterThan, "gt"); + defineBinaryCompareOperator(BuiltinClass::GreaterOrEqual, Token::GreaterThanOrEqual, "geq"); +} + +bool TypeInference::analyze(SourceUnit const& _sourceUnit) +{ + _sourceUnit.accept(*this); + return !m_errorReporter.hasErrors(); +} + +bool TypeInference::visit(FunctionDefinition const& _functionDefinition) +{ + solAssert(m_expressionContext == ExpressionContext::Term); + auto& functionAnnotation = annotation(_functionDefinition); + if (functionAnnotation.type) + return false; + + ScopedSaveAndRestore signatureRestore(m_currentFunctionType, std::nullopt); + + Type argumentsType = m_typeSystem.freshTypeVariable({}); + Type returnType = m_typeSystem.freshTypeVariable({}); + Type functionType = TypeSystemHelpers{m_typeSystem}.functionType(argumentsType, returnType); + + m_currentFunctionType = functionType; + functionAnnotation.type = functionType; + + + _functionDefinition.parameterList().accept(*this); + unify(argumentsType, type(_functionDefinition.parameterList()), _functionDefinition.parameterList().location()); + if (_functionDefinition.experimentalReturnExpression()) + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; + _functionDefinition.experimentalReturnExpression()->accept(*this); + unify( + returnType, + type(*_functionDefinition.experimentalReturnExpression()), + _functionDefinition.experimentalReturnExpression()->location() + ); + } + else + unify(returnType, m_unitType, _functionDefinition.location()); + + if (_functionDefinition.isImplemented()) + _functionDefinition.body().accept(*this); + + return false; +} + +void TypeInference::endVisit(Return const& _return) +{ + solAssert(m_currentFunctionType); + Type functionReturnType = std::get<1>(TypeSystemHelpers{m_typeSystem}.destFunctionType(*m_currentFunctionType)); + if (_return.expression()) + unify(functionReturnType, type(*_return.expression()), _return.location()); + else + unify(functionReturnType, m_unitType, _return.location()); +} + +void TypeInference::endVisit(ParameterList const& _parameterList) +{ + auto& listAnnotation = annotation(_parameterList); + solAssert(!listAnnotation.type); + listAnnotation.type = TypeSystemHelpers{m_typeSystem}.tupleType( + _parameterList.parameters() | ranges::views::transform([&](auto _arg) { return type(*_arg); }) | ranges::to> + ); +} + +bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) +{ + solAssert(m_expressionContext == ExpressionContext::Term); + auto& typeClassDefinitionAnnotation = annotation(_typeClassDefinition); + if (typeClassDefinitionAnnotation.type) + return false; + + typeClassDefinitionAnnotation.type = type(&_typeClassDefinition, {}); + + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; + _typeClassDefinition.typeVariable().accept(*this); + } + + std::map functionTypes; + + solAssert(m_analysis.annotation(_typeClassDefinition).typeClass.has_value()); + TypeClass typeClass = m_analysis.annotation(_typeClassDefinition).typeClass.value(); + Type typeVar = m_typeSystem.typeClassVariable(typeClass); + auto& typeMembersAnnotation = annotation().members[typeConstructor(&_typeClassDefinition)]; + + for (auto subNode: _typeClassDefinition.subNodes()) + { + subNode->accept(*this); + auto const* functionDefinition = dynamic_cast(subNode.get()); + solAssert(functionDefinition); + auto functionType = type(*functionDefinition); + if (!functionTypes.emplace(functionDefinition->name(), functionType).second) + m_errorReporter.fatalTypeError(3195_error, functionDefinition->location(), "Function in type class declared multiple times."); + auto typeVars = TypeEnvironmentHelpers{*m_env}.typeVars(functionType); + if (typeVars.size() != 1) + m_errorReporter.fatalTypeError(8379_error, functionDefinition->location(), "Function in type class may only depend on the type class variable."); + unify(typeVars.front(), typeVar, functionDefinition->location()); + typeMembersAnnotation[functionDefinition->name()] = TypeMember{functionType}; + } + + annotation().typeClassFunctions[typeClass] = std::move(functionTypes); + + for (auto [functionName, functionType]: functionTypes) + { + TypeEnvironmentHelpers helper{*m_env}; + auto typeVars = helper.typeVars(functionType); + if (typeVars.empty()) + m_errorReporter.typeError(1723_error, _typeClassDefinition.location(), "Function " + functionName + " does not depend on class variable."); + if (typeVars.size() > 2) + m_errorReporter.typeError(6387_error, _typeClassDefinition.location(), "Function " + functionName + " depends on multiple type variables."); + if (!m_env->typeEquals(typeVars.front(), typeVar)) + m_errorReporter.typeError(1807_error, _typeClassDefinition.location(), "Function " + functionName + " depends on invalid type variable."); + } + + unify(type(_typeClassDefinition.typeVariable()), m_typeSystem.freshTypeVariable({{typeClass}}), _typeClassDefinition.location()); + for (auto instantiation: m_analysis.annotation(_typeClassDefinition).instantiations | ranges::views::values) + // TODO: recursion-safety? Order of instantiation? + instantiation->accept(*this); + + return false; +} + +bool TypeInference::visit(InlineAssembly const& _inlineAssembly) +{ + // External references have already been resolved in a prior stage and stored in the annotation. + // We run the resolve step again regardless. + yul::ExternalIdentifierAccess::Resolver identifierAccess = [&]( + yul::Identifier const& _identifier, + yul::IdentifierContext _context, + bool + ) -> bool + { + if (_context == yul::IdentifierContext::NonExternal) + { + // TODO: do we need this? + // Hack until we can disallow any shadowing: If we found an internal reference, + // clear the external references, so that codegen does not use it. + _inlineAssembly.annotation().externalReferences.erase(& _identifier); + return false; + } + InlineAssemblyAnnotation::ExternalIdentifierInfo* identifierInfo = util::valueOrNullptr(_inlineAssembly.annotation().externalReferences, &_identifier); + if (!identifierInfo) + return false; + Declaration const* declaration = identifierInfo->declaration; + solAssert(!!declaration, ""); + solAssert(identifierInfo->suffix == "", ""); + + unify(type(*declaration), m_wordType, originLocationOf(_identifier)); + identifierInfo->valueSize = 1; + return true; + }; + solAssert(!_inlineAssembly.annotation().analysisInfo, ""); + _inlineAssembly.annotation().analysisInfo = std::make_shared(); + yul::AsmAnalyzer analyzer( + *_inlineAssembly.annotation().analysisInfo, + m_errorReporter, + _inlineAssembly.dialect(), + identifierAccess + ); + if (!analyzer.analyze(_inlineAssembly.operations())) + solAssert(m_errorReporter.hasErrors()); + return false; +} + +bool TypeInference::visit(BinaryOperation const& _binaryOperation) +{ + auto& operationAnnotation = annotation(_binaryOperation); + solAssert(!operationAnnotation.type); + TypeSystemHelpers helper{m_typeSystem}; + switch (m_expressionContext) + { + case ExpressionContext::Term: + if (auto* operatorInfo = util::valueOrNullptr(annotation().operators, _binaryOperation.getOperator())) + { + auto [typeClass, functionName] = *operatorInfo; + // TODO: error robustness? + Type functionType = m_env->fresh(annotation().typeClassFunctions.at(typeClass).at(functionName)); + + _binaryOperation.leftExpression().accept(*this); + _binaryOperation.rightExpression().accept(*this); + + Type argTuple = helper.tupleType({type(_binaryOperation.leftExpression()), type(_binaryOperation.rightExpression())}); + Type resultType = m_typeSystem.freshTypeVariable({}); + Type genericFunctionType = helper.functionType(argTuple, resultType); + unify(functionType, genericFunctionType, _binaryOperation.location()); + + operationAnnotation.type = resultType; + } + else if (_binaryOperation.getOperator() == Token::Colon) + { + _binaryOperation.leftExpression().accept(*this); + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; + _binaryOperation.rightExpression().accept(*this); + } + Type leftType = type(_binaryOperation.leftExpression()); + unify(leftType, type(_binaryOperation.rightExpression()), _binaryOperation.location()); + operationAnnotation.type = leftType; + } + else + { + m_errorReporter.typeError(4504_error, _binaryOperation.location(), "Binary operation in term context not yet supported."); + operationAnnotation.type = m_typeSystem.freshTypeVariable({}); + } + return false; + case ExpressionContext::Type: + if (_binaryOperation.getOperator() == Token::Colon) + { + _binaryOperation.leftExpression().accept(*this); + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Sort}; + _binaryOperation.rightExpression().accept(*this); + } + Type leftType = type(_binaryOperation.leftExpression()); + unify(leftType, type(_binaryOperation.rightExpression()), _binaryOperation.location()); + operationAnnotation.type = leftType; + } + else if (_binaryOperation.getOperator() == Token::RightArrow) + { + _binaryOperation.leftExpression().accept(*this); + _binaryOperation.rightExpression().accept(*this); + operationAnnotation.type = helper.functionType(type(_binaryOperation.leftExpression()), type(_binaryOperation.rightExpression())); + } + else if (_binaryOperation.getOperator() == Token::BitOr) + { + _binaryOperation.leftExpression().accept(*this); + _binaryOperation.rightExpression().accept(*this); + operationAnnotation.type = helper.sumType({type(_binaryOperation.leftExpression()), type(_binaryOperation.rightExpression())}); + } + else + { + m_errorReporter.typeError(1439_error, _binaryOperation.location(), "Invalid binary operations in type context."); + operationAnnotation.type = m_typeSystem.freshTypeVariable({}); + } + return false; + case ExpressionContext::Sort: + m_errorReporter.typeError(1017_error, _binaryOperation.location(), "Invalid binary operation in sort context."); + operationAnnotation.type = m_typeSystem.freshTypeVariable({}); + return false; + } + return false; +} + +void TypeInference::endVisit(VariableDeclarationStatement const& _variableDeclarationStatement) +{ + solAssert(m_expressionContext == ExpressionContext::Term); + if (_variableDeclarationStatement.declarations().size () != 1) + { + m_errorReporter.typeError(2655_error, _variableDeclarationStatement.location(), "Multi variable declaration not supported."); + return; + } + Type variableType = type(*_variableDeclarationStatement.declarations().front()); + if (_variableDeclarationStatement.initialValue()) + unify(variableType, type(*_variableDeclarationStatement.initialValue()), _variableDeclarationStatement.location()); +} + +bool TypeInference::visit(VariableDeclaration const& _variableDeclaration) +{ + solAssert(!_variableDeclaration.value()); + auto& variableAnnotation = annotation(_variableDeclaration); + solAssert(!variableAnnotation.type); + + switch (m_expressionContext) + { + case ExpressionContext::Term: + if (_variableDeclaration.typeExpression()) + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; + _variableDeclaration.typeExpression()->accept(*this); + variableAnnotation.type = type(*_variableDeclaration.typeExpression()); + return false; + } + variableAnnotation.type = m_typeSystem.freshTypeVariable({}); + return false; + case ExpressionContext::Type: + variableAnnotation.type = m_typeSystem.freshTypeVariable({}); + if (_variableDeclaration.typeExpression()) + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Sort}; + _variableDeclaration.typeExpression()->accept(*this); + unify(*variableAnnotation.type, type(*_variableDeclaration.typeExpression()), _variableDeclaration.typeExpression()->location()); + } + return false; + case ExpressionContext::Sort: + m_errorReporter.typeError(2399_error, _variableDeclaration.location(), "Variable declaration in sort context."); + variableAnnotation.type = m_typeSystem.freshTypeVariable({}); + return false; + } + util::unreachable(); +} + +void TypeInference::endVisit(IfStatement const& _ifStatement) +{ + auto& ifAnnotation = annotation(_ifStatement); + solAssert(!ifAnnotation.type); + + if (m_expressionContext != ExpressionContext::Term) + { + m_errorReporter.typeError(2015_error, _ifStatement.location(), "If statement outside term context."); + ifAnnotation.type = m_typeSystem.freshTypeVariable({}); + return; + } + + unify(type(_ifStatement.condition()), m_boolType, _ifStatement.condition().location()); + + ifAnnotation.type = m_unitType; +} + +void TypeInference::endVisit(Assignment const& _assignment) +{ + auto& assignmentAnnotation = annotation(_assignment); + solAssert(!assignmentAnnotation.type); + + if (m_expressionContext != ExpressionContext::Term) + { + m_errorReporter.typeError(4337_error, _assignment.location(), "Assignment outside term context."); + assignmentAnnotation.type = m_typeSystem.freshTypeVariable({}); + return; + } + + Type leftType = type(_assignment.leftHandSide()); + unify(leftType, type(_assignment.rightHandSide()), _assignment.location()); + assignmentAnnotation.type = leftType; +} + +experimental::Type TypeInference::handleIdentifierByReferencedDeclaration(langutil::SourceLocation _location, Declaration const& _declaration) +{ + switch (m_expressionContext) + { + case ExpressionContext::Term: + { + if ( + !dynamic_cast(&_declaration) && + !dynamic_cast(&_declaration) && + !dynamic_cast(&_declaration) && + !dynamic_cast(&_declaration) + ) + { + SecondarySourceLocation ssl; + ssl.append("Referenced node.", _declaration.location()); + m_errorReporter.fatalTypeError(3101_error, _location, ssl, "Attempt to type identifier referring to unexpected node."); + } + + auto& declarationAnnotation = annotation(_declaration); + if (!declarationAnnotation.type) + _declaration.accept(*this); + + solAssert(declarationAnnotation.type); + + if (dynamic_cast(&_declaration)) + return *declarationAnnotation.type; + else if (dynamic_cast(&_declaration)) + return polymorphicInstance(*declarationAnnotation.type); + else if (dynamic_cast(&_declaration)) + { + solAssert(TypeEnvironmentHelpers{*m_env}.typeVars(*declarationAnnotation.type).empty()); + return *declarationAnnotation.type; + } + else if (dynamic_cast(&_declaration)) + { + // TODO: can we avoid this? + Type type = *declarationAnnotation.type; + if (TypeSystemHelpers{m_typeSystem}.isTypeFunctionType(type)) + type = std::get<1>(TypeSystemHelpers{m_typeSystem}.destTypeFunctionType(type)); + return polymorphicInstance(type); + } + else + solAssert(false); + break; + } + case ExpressionContext::Type: + { + if ( + !dynamic_cast(&_declaration) && + !dynamic_cast(&_declaration) + ) + { + SecondarySourceLocation ssl; + ssl.append("Referenced node.", _declaration.location()); + m_errorReporter.fatalTypeError(2217_error, _location, ssl, "Attempt to type identifier referring to unexpected node."); + } + + // TODO: Assert that this is a type class variable declaration? + auto& declarationAnnotation = annotation(_declaration); + if (!declarationAnnotation.type) + _declaration.accept(*this); + + solAssert(declarationAnnotation.type); + + if (dynamic_cast(&_declaration)) + return *declarationAnnotation.type; + else if (dynamic_cast(&_declaration)) + return polymorphicInstance(*declarationAnnotation.type); + else + solAssert(false); + break; + } + case ExpressionContext::Sort: + { + if (auto const* typeClassDefinition = dynamic_cast(&_declaration)) + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Term}; + typeClassDefinition->accept(*this); + + solAssert(m_analysis.annotation(*typeClassDefinition).typeClass.has_value()); + TypeClass typeClass = m_analysis.annotation(*typeClassDefinition).typeClass.value(); + return m_typeSystem.freshTypeVariable(Sort{{typeClass}}); + } + else + { + m_errorReporter.typeError(2599_error, _location, "Expected type class."); + return m_typeSystem.freshTypeVariable({}); + } + break; + } + } + util::unreachable(); +} + +bool TypeInference::visit(Identifier const& _identifier) +{ + auto& identifierAnnotation = annotation(_identifier); + solAssert(!identifierAnnotation.type); + + if (auto const* referencedDeclaration = _identifier.annotation().referencedDeclaration) + { + identifierAnnotation.type = handleIdentifierByReferencedDeclaration(_identifier.location(), *referencedDeclaration); + return false; + } + + switch (m_expressionContext) + { + case ExpressionContext::Term: + // TODO: error handling + solAssert(false); + break; + case ExpressionContext::Type: + // TODO: register free type variable name! + identifierAnnotation.type = m_typeSystem.freshTypeVariable({}); + return false; + case ExpressionContext::Sort: + // TODO: error handling + solAssert(false); + break; + } + + return false; +} + +void TypeInference::endVisit(TupleExpression const& _tupleExpression) +{ + auto& expressionAnnotation = annotation(_tupleExpression); + solAssert(!expressionAnnotation.type); + + TypeSystemHelpers helper{m_typeSystem}; + auto componentTypes = _tupleExpression.components() | ranges::views::transform([&](auto _expr) -> Type { + auto& componentAnnotation = annotation(*_expr); + solAssert(componentAnnotation.type); + return *componentAnnotation.type; + }) | ranges::to>; + switch (m_expressionContext) + { + case ExpressionContext::Term: + case ExpressionContext::Type: + expressionAnnotation.type = helper.tupleType(componentTypes); + break; + case ExpressionContext::Sort: + { + Type type = m_typeSystem.freshTypeVariable({}); + for (auto componentType: componentTypes) + unify(type, componentType, _tupleExpression.location()); + expressionAnnotation.type = type; + break; + } + } +} + +bool TypeInference::visit(IdentifierPath const& _identifierPath) +{ + auto& identifierAnnotation = annotation(_identifierPath); + solAssert(!identifierAnnotation.type); + + if (auto const* referencedDeclaration = _identifierPath.annotation().referencedDeclaration) + { + identifierAnnotation.type = handleIdentifierByReferencedDeclaration(_identifierPath.location(), *referencedDeclaration); + return false; + } + + // TODO: error handling + solAssert(false); +} + +bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) +{ + ScopedSaveAndRestore activeInstantiations{m_activeInstantiations, m_activeInstantiations + std::set{&_typeClassInstantiation}}; + // Note: recursion is resolved due to special handling during unification. + auto& instantiationAnnotation = annotation(_typeClassInstantiation); + if (instantiationAnnotation.type) + return false; + instantiationAnnotation.type = m_voidType; + std::optional typeClass = std::visit(util::GenericVisitor{ + [&](ASTPointer _typeClassName) -> std::optional { + if (auto const* typeClassDefinition = dynamic_cast(_typeClassName->annotation().referencedDeclaration)) + { + // visiting the type class will re-visit this instantiation + typeClassDefinition->accept(*this); + // TODO: more error handling? Should be covered by the visit above. + solAssert(m_analysis.annotation(*typeClassDefinition).typeClass.has_value()); + return m_analysis.annotation(*typeClassDefinition).typeClass.value(); + } + else + { + m_errorReporter.typeError(9817_error, _typeClassInstantiation.typeClass().location(), "Expected type class."); + return std::nullopt; + } + }, + [&](Token _token) -> std::optional { + if (auto builtinClass = builtinClassFromToken(_token)) + if (auto typeClass = util::valueOrNullptr(annotation().builtinClasses, *builtinClass)) + return *typeClass; + m_errorReporter.typeError(2658_error, _typeClassInstantiation.location(), "Invalid type class name."); + return std::nullopt; + } + }, _typeClassInstantiation.typeClass().name()); + if (!typeClass) + return false; + + // TODO: _typeClassInstantiation.typeConstructor().accept(*this); ? + auto typeConstructor = m_analysis.annotation(_typeClassInstantiation.typeConstructor()).typeConstructor; + if (!typeConstructor) + { + m_errorReporter.typeError(2138_error, _typeClassInstantiation.typeConstructor().location(), "Invalid type constructor."); + return false; + } + + std::vector arguments; + Arity arity{ + {}, + *typeClass + }; + + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; + if (_typeClassInstantiation.argumentSorts()) + { + _typeClassInstantiation.argumentSorts()->accept(*this); + auto& argumentSortAnnotation = annotation(*_typeClassInstantiation.argumentSorts()); + solAssert(argumentSortAnnotation.type); + arguments = TypeSystemHelpers{m_typeSystem}.destTupleType(*argumentSortAnnotation.type); + arity.argumentSorts = arguments | ranges::views::transform([&](Type _type) { + return m_env->sort(_type); + }) | ranges::to>; + } + } + + Type instanceType{TypeConstant{*typeConstructor, arguments}}; + + std::map functionTypes; + + for (auto subNode: _typeClassInstantiation.subNodes()) + { + auto const* functionDefinition = dynamic_cast(subNode.get()); + solAssert(functionDefinition); + subNode->accept(*this); + if (!functionTypes.emplace(functionDefinition->name(), type(*functionDefinition)).second) + m_errorReporter.typeError(3654_error, subNode->location(), "Duplicate definition of function " + functionDefinition->name() + " during type class instantiation."); + } + + if (auto error = m_typeSystem.instantiateClass(instanceType, arity)) + m_errorReporter.typeError(5094_error, _typeClassInstantiation.location(), *error); + + auto const& classFunctions = annotation().typeClassFunctions.at(*typeClass); + + TypeEnvironment newEnv = m_env->clone(); + if (!newEnv.unify(m_typeSystem.typeClassVariable(*typeClass), instanceType).empty()) + { + m_errorReporter.typeError(4686_error, _typeClassInstantiation.location(), "Unification of class and instance variable failed."); + return false; + } + + for (auto [name, classFunctionType]: classFunctions) + { + if (!functionTypes.count(name)) + { + m_errorReporter.typeError(6948_error, _typeClassInstantiation.location(), "Missing function: " + name); + continue; + } + Type instanceFunctionType = functionTypes.at(name); + functionTypes.erase(name); + + if (!newEnv.typeEquals(instanceFunctionType, classFunctionType)) + m_errorReporter.typeError(7428_error, _typeClassInstantiation.location(), "Type mismatch for function " + name + " " + TypeEnvironmentHelpers{newEnv}.typeToString(instanceFunctionType) + " != " + TypeEnvironmentHelpers{newEnv}.typeToString(classFunctionType)); + } + + if (!functionTypes.empty()) + m_errorReporter.typeError(4873_error, _typeClassInstantiation.location(), "Additional functions in class instantiation."); + + return false; +} + +bool TypeInference::visit(MemberAccess const& _memberAccess) +{ + if (m_expressionContext != ExpressionContext::Term) + { + m_errorReporter.typeError(5195_error, _memberAccess.location(), "Member access outside term context."); + annotation(_memberAccess).type = m_typeSystem.freshTypeVariable({}); + return false; + } + return true; +} + +experimental::Type TypeInference::memberType(Type _type, std::string _memberName, langutil::SourceLocation _location) +{ + Type resolvedType = m_env->resolve(_type); + TypeSystemHelpers helper{m_typeSystem}; + if (helper.isTypeConstant(resolvedType)) + { + auto constructor = std::get<0>(helper.destTypeConstant(resolvedType)); + if (auto* typeMember = util::valueOrNullptr(annotation().members.at(constructor), _memberName)) + return polymorphicInstance(typeMember->type); + else + { + m_errorReporter.typeError(5755_error, _location, fmt::format("Member {} not found in type {}.", _memberName, TypeEnvironmentHelpers{*m_env}.typeToString(_type))); + return m_typeSystem.freshTypeVariable({}); + } + } + else + { + m_errorReporter.typeError(5104_error, _location, "Unsupported member access expression."); + return m_typeSystem.freshTypeVariable({}); + } +} + +void TypeInference::endVisit(MemberAccess const& _memberAccess) +{ + auto& memberAccessAnnotation = annotation(_memberAccess); + solAssert(!memberAccessAnnotation.type); + Type expressionType = type(_memberAccess.expression()); + memberAccessAnnotation.type = memberType(expressionType, _memberAccess.memberName(), _memberAccess.location()); +} + +bool TypeInference::visit(TypeDefinition const& _typeDefinition) +{ + bool isBuiltIn = dynamic_cast(_typeDefinition.typeExpression()); + + TypeSystemHelpers helper{m_typeSystem}; + auto& typeDefinitionAnnotation = annotation(_typeDefinition); + if (typeDefinitionAnnotation.type) + return false; + + if (_typeDefinition.arguments()) + _typeDefinition.arguments()->accept(*this); + + std::vector arguments; + if (_typeDefinition.arguments()) + for (size_t i = 0; i < _typeDefinition.arguments()->parameters().size(); ++i) + arguments.emplace_back(m_typeSystem.freshTypeVariable({})); + + Type definedType = type(&_typeDefinition, arguments); + if (arguments.empty()) + typeDefinitionAnnotation.type = definedType; + else + typeDefinitionAnnotation.type = helper.typeFunctionType(helper.tupleType(arguments), definedType); + + std::optional underlyingType; + + if (isBuiltIn) + // TODO: This special case should eventually become user-definable. + underlyingType = m_wordType; + else if (_typeDefinition.typeExpression()) + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; + _typeDefinition.typeExpression()->accept(*this); + + underlyingType = annotation(*_typeDefinition.typeExpression()).type; + } + + TypeConstructor constructor = typeConstructor(&_typeDefinition); + auto [members, newlyInserted] = annotation().members.emplace(constructor, std::map{}); + solAssert(newlyInserted, fmt::format("Members of type '{}' are already defined.", m_typeSystem.constructorInfo(constructor).name)); + if (underlyingType) + { + members->second.emplace("abs", TypeMember{helper.functionType(*underlyingType, definedType)}); + members->second.emplace("rep", TypeMember{helper.functionType(definedType, *underlyingType)}); + } + + if (helper.isPrimitiveType(definedType, PrimitiveType::Pair)) + { + solAssert(isBuiltIn); + solAssert(arguments.size() == 2); + members->second.emplace("first", TypeMember{helper.functionType(definedType, arguments[0])}); + members->second.emplace("second", TypeMember{helper.functionType(definedType, arguments[1])}); + } + + return false; +} + +bool TypeInference::visit(FunctionCall const&) { return true; } +void TypeInference::endVisit(FunctionCall const& _functionCall) +{ + auto& functionCallAnnotation = annotation(_functionCall); + solAssert(!functionCallAnnotation.type); + + Type functionType = type(_functionCall.expression()); + + TypeSystemHelpers helper{m_typeSystem}; + std::vector argTypes; + for (auto arg: _functionCall.arguments()) + { + switch (m_expressionContext) + { + case ExpressionContext::Term: + case ExpressionContext::Type: + argTypes.emplace_back(type(*arg)); + break; + case ExpressionContext::Sort: + m_errorReporter.typeError(9173_error, _functionCall.location(), "Function call in sort context."); + functionCallAnnotation.type = m_typeSystem.freshTypeVariable({}); + break; + } + } + + switch (m_expressionContext) + { + case ExpressionContext::Term: + { + Type argTuple = helper.tupleType(argTypes); + Type resultType = m_typeSystem.freshTypeVariable({}); + Type genericFunctionType = helper.functionType(argTuple, resultType); + unify(functionType, genericFunctionType, _functionCall.location()); + functionCallAnnotation.type = resultType; + break; + } + case ExpressionContext::Type: + { + Type argTuple = helper.tupleType(argTypes); + Type resultType = m_typeSystem.freshTypeVariable({}); + Type genericFunctionType = helper.typeFunctionType(argTuple, resultType); + unify(functionType, genericFunctionType, _functionCall.location()); + functionCallAnnotation.type = resultType; + break; + } + case ExpressionContext::Sort: + solAssert(false); + } +} + +// TODO: clean up rational parsing +namespace +{ + +std::optional parseRational(std::string const& _value) +{ + rational value; + try + { + auto radixPoint = find(_value.begin(), _value.end(), '.'); + + if (radixPoint != _value.end()) + { + if ( + !all_of(radixPoint + 1, _value.end(), util::isDigit) || + !all_of(_value.begin(), radixPoint, util::isDigit) + ) + return std::nullopt; + + // Only decimal notation allowed here, leading zeros would switch to octal. + auto fractionalBegin = find_if_not( + radixPoint + 1, + _value.end(), + [](char const& a) { return a == '0'; } + ); + + rational numerator; + rational denominator(1); + + denominator = bigint(std::string(fractionalBegin, _value.end())); + denominator /= boost::multiprecision::pow( + bigint(10), + static_cast(distance(radixPoint + 1, _value.end())) + ); + numerator = bigint(std::string(_value.begin(), radixPoint)); + value = numerator + denominator; + } + else + value = bigint(_value); + return value; + } + catch (...) + { + return std::nullopt; + } +} + +/// Checks whether _mantissa * (10 ** _expBase10) fits into 4096 bits. +bool fitsPrecisionBase10(bigint const& _mantissa, uint32_t _expBase10) +{ + double const log2Of10AwayFromZero = 3.3219280948873624; + return fitsPrecisionBaseX(_mantissa, log2Of10AwayFromZero, _expBase10); +} + +std::optional rationalValue(Literal const& _literal) +{ + rational value; + try + { + ASTString valueString = _literal.valueWithoutUnderscores(); + + auto expPoint = find(valueString.begin(), valueString.end(), 'e'); + if (expPoint == valueString.end()) + expPoint = find(valueString.begin(), valueString.end(), 'E'); + + if (boost::starts_with(valueString, "0x")) + { + // process as hex + value = bigint(valueString); + } + else if (expPoint != valueString.end()) + { + // Parse mantissa and exponent. Checks numeric limit. + std::optional mantissa = parseRational(std::string(valueString.begin(), expPoint)); + + if (!mantissa) + return std::nullopt; + value = *mantissa; + + // 0E... is always zero. + if (value == 0) + return std::nullopt; + + bigint exp = bigint(std::string(expPoint + 1, valueString.end())); + + if (exp > std::numeric_limits::max() || exp < std::numeric_limits::min()) + return std::nullopt; + + uint32_t expAbs = bigint(abs(exp)).convert_to(); + + if (exp < 0) + { + if (!fitsPrecisionBase10(abs(value.denominator()), expAbs)) + return std::nullopt; + value /= boost::multiprecision::pow( + bigint(10), + expAbs + ); + } + else if (exp > 0) + { + if (!fitsPrecisionBase10(abs(value.numerator()), expAbs)) + return std::nullopt; + value *= boost::multiprecision::pow( + bigint(10), + expAbs + ); + } + } + else + { + // parse as rational number + std::optional tmp = parseRational(valueString); + if (!tmp) + return std::nullopt; + value = *tmp; + } + } + catch (...) + { + return std::nullopt; + } + switch (_literal.subDenomination()) + { + case Literal::SubDenomination::None: + case Literal::SubDenomination::Wei: + case Literal::SubDenomination::Second: + break; + case Literal::SubDenomination::Gwei: + value *= bigint("1000000000"); + break; + case Literal::SubDenomination::Ether: + value *= bigint("1000000000000000000"); + break; + case Literal::SubDenomination::Minute: + value *= bigint("60"); + break; + case Literal::SubDenomination::Hour: + value *= bigint("3600"); + break; + case Literal::SubDenomination::Day: + value *= bigint("86400"); + break; + case Literal::SubDenomination::Week: + value *= bigint("604800"); + break; + case Literal::SubDenomination::Year: + value *= bigint("31536000"); + break; + } + + return value; +} +} + +bool TypeInference::visit(Literal const& _literal) +{ + auto& literalAnnotation = annotation(_literal); + if (_literal.token() != Token::Number) + { + m_errorReporter.typeError(4316_error, _literal.location(), "Only number literals are supported."); + return false; + } + std::optional value = rationalValue(_literal); + if (!value) + { + m_errorReporter.typeError(6739_error, _literal.location(), "Invalid number literals."); + return false; + } + if (value->denominator() != 1) + { + m_errorReporter.typeError(2345_error, _literal.location(), "Only integers are supported."); + return false; + } + literalAnnotation.type = m_typeSystem.freshTypeVariable(Sort{{annotation().builtinClasses.at(BuiltinClass::Integer)}}); + return false; +} + + +namespace +{ +// TODO: put at a nice place to deduplicate. +TypeRegistration::TypeClassInstantiations const& typeClassInstantiations(Analysis const& _analysis, TypeClass _class) +{ + auto const* typeClassDeclaration = _analysis.typeSystem().typeClassDeclaration(_class); + if (typeClassDeclaration) + return _analysis.annotation(*typeClassDeclaration).instantiations; + // TODO: better mechanism than fetching by name. + auto const& annotation = _analysis.annotation(); + auto const& inferenceAnnotation = _analysis.annotation(); + return annotation.builtinClassInstantiations.at( + inferenceAnnotation.builtinClassesByName.at( + _analysis.typeSystem().typeClassName(_class) + ) + ); +} +} + +experimental::Type TypeInference::polymorphicInstance(Type const& _scheme) +{ + return m_env->fresh(_scheme); +} + +void TypeInference::unify(Type _a, Type _b, langutil::SourceLocation _location) +{ + TypeSystemHelpers helper{m_typeSystem}; + auto unificationFailures = m_env->unify(_a, _b); + + if (!m_activeInstantiations.empty()) + { + // TODO: This entire logic is superfluous - I thought mutually recursive dependencies between + // class instantiations are a problem, but in fact they're not, they just resolve to mutually recursive + // functions that are fine. So instead, all instantiations can be registered with the type system directly + // when visiting the type class (assuming that they all work out) - and then all instantiations can be checked + // individually, which should still catch all actual issues (while allowing recursions). + // Original comment: Attempt to resolve interdependencies between type class instantiations. + std::vector missingInstantiations; + bool recursion = false; + bool onlyMissingInstantiations = [&]() { + for (auto failure: unificationFailures) + { + if (auto* sortMismatch = std::get_if(&failure)) + if (helper.isTypeConstant(sortMismatch->type)) + { + TypeConstructor constructor = std::get<0>(helper.destTypeConstant(sortMismatch->type)); + for (auto typeClass: sortMismatch->sort.classes) + { + if (auto const* instantiation = util::valueOrDefault(typeClassInstantiations(m_analysis, typeClass), constructor, nullptr)) + { + if (m_activeInstantiations.count(instantiation)) + { + langutil::SecondarySourceLocation ssl; + for (auto activeInstantiation: m_activeInstantiations) + ssl.append("Involved instantiation", activeInstantiation->location()); + m_errorReporter.typeError( + 3573_error, + _location, + ssl, + "Recursion during type class instantiation." + ); + recursion = true; + return false; + } + missingInstantiations.emplace_back(instantiation); + } + else + return false; + } + continue; + } + return false; + } + return true; + }(); + + if (recursion) + return; + + if (onlyMissingInstantiations) + { + for (auto instantiation: missingInstantiations) + instantiation->accept(*this); + unificationFailures = m_env->unify(_a, _b); + } + } + + for (auto failure: unificationFailures) + { + TypeEnvironmentHelpers envHelper{*m_env}; + std::visit(util::GenericVisitor{ + [&](TypeEnvironment::TypeMismatch _typeMismatch) { + m_errorReporter.typeError( + 8456_error, + _location, + fmt::format( + "Cannot unify {} and {}.", + envHelper.typeToString(_typeMismatch.a), + envHelper.typeToString(_typeMismatch.b) + ) + ); + }, + [&](TypeEnvironment::SortMismatch _sortMismatch) { + m_errorReporter.typeError(3111_error, _location, fmt::format( + "{} does not have sort {}", + envHelper.typeToString(_sortMismatch.type), + TypeSystemHelpers{m_typeSystem}.sortToString(_sortMismatch.sort) + )); + }, + [&](TypeEnvironment::RecursiveUnification _recursiveUnification) { + m_errorReporter.typeError( + 6460_error, + _location, + fmt::format( + "Recursive unification: {} occurs in {}.", + envHelper.typeToString(_recursiveUnification.var), + envHelper.typeToString(_recursiveUnification.type) + ) + ); + } + }, failure); + } +} + +experimental::Type TypeInference::type(ASTNode const& _node) const +{ + auto result = annotation(_node).type; + solAssert(result); + return *result; +} +TypeConstructor TypeInference::typeConstructor(Declaration const* _type) const +{ + if (auto const& constructor = m_analysis.annotation(*_type).typeConstructor) + return *constructor; + m_errorReporter.fatalTypeError(5904_error, _type->location(), "Unregistered type."); + util::unreachable(); +} +experimental::Type TypeInference::type(Declaration const* _type, std::vector _arguments) const +{ + return m_typeSystem.type(typeConstructor(_type), std::move(_arguments)); +} + +bool TypeInference::visitNode(ASTNode const& _node) +{ + m_errorReporter.fatalTypeError(5348_error, _node.location(), "Unsupported AST node during type inference."); + return false; +} + +TypeInference::Annotation& TypeInference::annotation(ASTNode const& _node) +{ + return m_analysis.annotation(_node); +} + +TypeInference::Annotation const& TypeInference::annotation(ASTNode const& _node) const +{ + return m_analysis.annotation(_node); +} + +TypeInference::GlobalAnnotation& TypeInference::annotation() +{ + return m_analysis.annotation(); +} diff --git a/libsolidity/experimental/analysis/TypeInference.h b/libsolidity/experimental/analysis/TypeInference.h new file mode 100644 index 000000000000..df9d0610a9a5 --- /dev/null +++ b/libsolidity/experimental/analysis/TypeInference.h @@ -0,0 +1,124 @@ +/* + 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::frontend::experimental +{ + +class Analysis; + +class TypeInference: public ASTConstVisitor +{ +public: + TypeInference(Analysis& _analysis); + + bool analyze(SourceUnit const& _sourceUnit); + + struct Annotation + { + /// Expressions, variable declarations, function declarations. + std::optional type; + }; + struct TypeMember + { + Type type; + }; + struct GlobalAnnotation + { + std::map builtinClasses; + std::map builtinClassesByName; + std::map> typeClassFunctions; + std::map> operators; + std::map> members; + }; + bool visit(Block const&) override { return true; } + bool visit(VariableDeclarationStatement const&) override { return true; } + void endVisit(VariableDeclarationStatement const& _variableDeclarationStatement) override; + bool visit(VariableDeclaration const& _variableDeclaration) override; + + bool visit(FunctionDefinition const& _functionDefinition) override; + bool visit(ParameterList const&) override { return true; } + void endVisit(ParameterList const& _parameterList) override; + bool visit(SourceUnit const&) override { return true; } + bool visit(ContractDefinition const&) override { return true; } + bool visit(InlineAssembly const& _inlineAssembly) override; + bool visit(ImportDirective const&) override { return true; } + bool visit(PragmaDirective const&) override { return false; } + + bool visit(IfStatement const&) override { return true; } + void endVisit(IfStatement const& _ifStatement) override; + bool visit(ExpressionStatement const&) override { return true; } + bool visit(Assignment const&) override { return true; } + void endVisit(Assignment const& _assignment) override; + bool visit(Identifier const&) override; + bool visit(IdentifierPath const&) override; + bool visit(FunctionCall const& _functionCall) override; + void endVisit(FunctionCall const& _functionCall) override; + bool visit(Return const&) override { return true; } + void endVisit(Return const& _return) override; + + bool visit(MemberAccess const& _memberAccess) override; + void endVisit(MemberAccess const& _memberAccess) override; + + bool visit(TypeClassDefinition const& _typeClassDefinition) override; + bool visit(TypeClassInstantiation const& _typeClassInstantiation) override; + bool visit(TupleExpression const&) override { return true; } + void endVisit(TupleExpression const& _tupleExpression) override; + bool visit(TypeDefinition const& _typeDefinition) override; + + bool visitNode(ASTNode const& _node) override; + + bool visit(BinaryOperation const& _operation) override; + + bool visit(Literal const& _literal) override; +private: + Analysis& m_analysis; + langutil::ErrorReporter& m_errorReporter; + TypeSystem& m_typeSystem; + TypeEnvironment* m_env = nullptr; + Type m_voidType; + Type m_wordType; + Type m_integerType; + Type m_unitType; + Type m_boolType; + std::optional m_currentFunctionType; + + Type type(ASTNode const& _node) const; + + Annotation& annotation(ASTNode const& _node); + Annotation const& annotation(ASTNode const& _node) const; + GlobalAnnotation& annotation(); + + void unify(Type _a, Type _b, langutil::SourceLocation _location = {}); + /// Creates a polymorphic instance of a global type scheme + Type polymorphicInstance(Type const& _scheme); + Type memberType(Type _type, std::string _memberName, langutil::SourceLocation _location = {}); + enum class ExpressionContext { Term, Type, Sort }; + Type handleIdentifierByReferencedDeclaration(langutil::SourceLocation _location, Declaration const& _declaration); + TypeConstructor typeConstructor(Declaration const* _type) const; + Type type(Declaration const* _type, std::vector _arguments) const; + ExpressionContext m_expressionContext = ExpressionContext::Term; + std::set> m_activeInstantiations; +}; + +} diff --git a/libsolidity/experimental/analysis/TypeRegistration.cpp b/libsolidity/experimental/analysis/TypeRegistration.cpp new file mode 100644 index 000000000000..43fd8d1175b9 --- /dev/null +++ b/libsolidity/experimental/analysis/TypeRegistration.cpp @@ -0,0 +1,211 @@ +/* + 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 + +using namespace solidity::frontend; +using namespace solidity::frontend::experimental; +using namespace solidity::langutil; + +TypeRegistration::TypeRegistration(Analysis& _analysis): + m_analysis(_analysis), + m_errorReporter(_analysis.errorReporter()), + m_typeSystem(_analysis.typeSystem()) +{ +} + +bool TypeRegistration::analyze(SourceUnit const& _sourceUnit) +{ + _sourceUnit.accept(*this); + return !m_errorReporter.hasErrors(); +} + +bool TypeRegistration::visit(TypeClassDefinition const& _typeClassDefinition) +{ + if (annotation(_typeClassDefinition).typeConstructor) + return false; + annotation(_typeClassDefinition).typeConstructor = m_typeSystem.declareTypeConstructor( + _typeClassDefinition.name(), + "t_" + *_typeClassDefinition.annotation().canonicalName + "_" + util::toString(_typeClassDefinition.id()), + 0, + &_typeClassDefinition + ); + return true; +} + +bool TypeRegistration::visit(Builtin const& _builtin) +{ + if (annotation(_builtin).typeConstructor) + return false; + + auto primitiveType = [&](std::string _name) -> std::optional { + if (_name == "void") return PrimitiveType::Void; + if (_name == "fun") return PrimitiveType::Function; + if (_name == "unit") return PrimitiveType::Unit; + if (_name == "pair") return PrimitiveType::Pair; + if (_name == "word") return PrimitiveType::Word; + if (_name == "integer") return PrimitiveType::Integer; + if (_name == "bool") return PrimitiveType::Bool; + return std::nullopt; + }(_builtin.nameParameter()); + + if (!primitiveType.has_value()) + m_errorReporter.fatalTypeError( + 7758_error, + _builtin.location(), + "Expected the name of a built-in primitive type." + ); + + annotation(_builtin).typeConstructor = m_typeSystem.constructor(primitiveType.value()); + return true; +} + +void TypeRegistration::endVisit(ElementaryTypeNameExpression const & _typeNameExpression) +{ + if (annotation(_typeNameExpression).typeConstructor) + return; + + // TODO: this is not visited in the ElementaryTypeNameExpression visit - is that intentional? + _typeNameExpression.type().accept(*this); + if (auto constructor = annotation(_typeNameExpression.type()).typeConstructor) + annotation(_typeNameExpression).typeConstructor = constructor; + else + solAssert(m_errorReporter.hasErrors()); +} + +bool TypeRegistration::visit(UserDefinedTypeName const& _userDefinedTypeName) +{ + if (annotation(_userDefinedTypeName).typeConstructor) + return false; + auto const* declaration = _userDefinedTypeName.pathNode().annotation().referencedDeclaration; + if (!declaration) + { + // TODO: fatal/non-fatal + m_errorReporter.fatalTypeError(5096_error, _userDefinedTypeName.pathNode().location(), "Expected declaration."); + return false; + } + declaration->accept(*this); + if (!(annotation(_userDefinedTypeName).typeConstructor = annotation(*declaration).typeConstructor)) + { + // TODO: fatal/non-fatal + m_errorReporter.fatalTypeError(9831_error, _userDefinedTypeName.pathNode().location(), "Expected type declaration."); + return false; + } + return true; +} + +bool TypeRegistration::visit(TypeClassInstantiation const& _typeClassInstantiation) +{ + if (annotation(_typeClassInstantiation).typeConstructor) + return false; + _typeClassInstantiation.typeConstructor().accept(*this); + auto typeConstructor = annotation(_typeClassInstantiation.typeConstructor()).typeConstructor; + if (!typeConstructor) + { + m_errorReporter.typeError(5577_error, _typeClassInstantiation.typeConstructor().location(), "Invalid type name."); + return false; + } + auto* instantiations = std::visit(util::GenericVisitor{ + [&](ASTPointer _path) -> TypeClassInstantiations* + { + if (TypeClassDefinition const* classDefinition = dynamic_cast(_path->annotation().referencedDeclaration)) + return &annotation(*classDefinition).instantiations; + m_errorReporter.typeError(3570_error, _typeClassInstantiation.typeClass().location(), "Expected a type class."); + return nullptr; + }, + [&](Token _token) -> TypeClassInstantiations* + { + if (auto typeClass = builtinClassFromToken(_token)) + return &annotation().builtinClassInstantiations[*typeClass]; + m_errorReporter.typeError(5262_error, _typeClassInstantiation.typeClass().location(), "Expected a type class."); + return nullptr; + } + }, _typeClassInstantiation.typeClass().name()); + + if (!instantiations) + return false; + + if ( + auto [instantiation, newlyInserted] = instantiations->emplace(*typeConstructor, &_typeClassInstantiation); + !newlyInserted + ) + { + SecondarySourceLocation ssl; + ssl.append("Previous instantiation.", instantiation->second->location()); + m_errorReporter.typeError(6620_error, _typeClassInstantiation.location(), ssl, "Duplicate type class instantiation."); + } + + return true; +} + +bool TypeRegistration::visit(TypeDefinition const& _typeDefinition) +{ + return !annotation(_typeDefinition).typeConstructor.has_value(); +} + +void TypeRegistration::endVisit(TypeDefinition const& _typeDefinition) +{ + if (annotation(_typeDefinition).typeConstructor.has_value()) + return; + + if (auto const* builtin = dynamic_cast(_typeDefinition.typeExpression())) + { + auto [previousDefinitionIt, inserted] = annotation().builtinTypeDefinitions.try_emplace( + builtin->nameParameter(), + &_typeDefinition + ); + + if (inserted) + annotation(_typeDefinition).typeConstructor = annotation(*builtin).typeConstructor; + else + { + auto const& [builtinName, previousDefinition] = *previousDefinitionIt; + m_errorReporter.typeError( + 9609_error, + _typeDefinition.location(), + SecondarySourceLocation{}.append("Previous definition:", previousDefinition->location()), + "Duplicate builtin type definition." + ); + } + } + else + annotation(_typeDefinition).typeConstructor = m_typeSystem.declareTypeConstructor( + _typeDefinition.name(), + "t_" + *_typeDefinition.annotation().canonicalName + "_" + util::toString(_typeDefinition.id()), + _typeDefinition.arguments() ? _typeDefinition.arguments()->parameters().size() : 0, + &_typeDefinition + ); +} + +TypeRegistration::Annotation& TypeRegistration::annotation(ASTNode const& _node) +{ + return m_analysis.annotation(_node); +} + +TypeRegistration::GlobalAnnotation& TypeRegistration::annotation() +{ + return m_analysis.annotation(); +} diff --git a/libsolidity/experimental/analysis/TypeRegistration.h b/libsolidity/experimental/analysis/TypeRegistration.h new file mode 100644 index 000000000000..9bc49a29cfe4 --- /dev/null +++ b/libsolidity/experimental/analysis/TypeRegistration.h @@ -0,0 +1,67 @@ +/* + 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::frontend::experimental +{ + +class Analysis; + +class TypeRegistration: public ASTConstVisitor +{ +public: + using TypeClassInstantiations = std::map; + struct Annotation + { + // For type class definitions. + TypeClassInstantiations instantiations; + // For builtins, type definitions, type class definitions, type names and type name expressions. + std::optional typeConstructor; + }; + struct GlobalAnnotation + { + std::map primitiveClassInstantiations; + std::map builtinClassInstantiations; + std::map builtinTypeDefinitions; + }; + TypeRegistration(Analysis& _analysis); + + bool analyze(SourceUnit const& _sourceUnit); +private: + bool visit(TypeClassDefinition const& _typeClassDefinition) override; + bool visit(TypeClassInstantiation const& _typeClassInstantiation) override; + bool visit(TypeDefinition const& _typeDefinition) override; + void endVisit(TypeDefinition const& _typeDefinition) override; + bool visit(UserDefinedTypeName const& _typeName) override; + void endVisit(ElementaryTypeNameExpression const& _typeName) override; + bool visit(Builtin const& _builtin) override; + Annotation& annotation(ASTNode const& _node); + GlobalAnnotation& annotation(); + + Analysis& m_analysis; + langutil::ErrorReporter& m_errorReporter; + TypeSystem& m_typeSystem; + std::set m_visitedClasses; +}; + +} diff --git a/libsolidity/experimental/ast/FunctionCallGraph.h b/libsolidity/experimental/ast/FunctionCallGraph.h new file mode 100644 index 000000000000..0a1db845514b --- /dev/null +++ b/libsolidity/experimental/ast/FunctionCallGraph.h @@ -0,0 +1,40 @@ +/* + 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 + +/// Data structure representing a function dependency graph. + +#pragma once + +#include + +#include +#include +#include + +namespace solidity::frontend::experimental +{ + +struct FunctionDependencyGraph +{ + /// Graph edges. Edges are directed and lead from the caller to the callee. + /// The map contains a key for every function, even if does not actually perform + /// any calls. + std::map>, ASTCompareByID> edges; +}; + +} diff --git a/libsolidity/experimental/ast/Type.cpp b/libsolidity/experimental/ast/Type.cpp new file mode 100644 index 000000000000..ec822dc64c54 --- /dev/null +++ b/libsolidity/experimental/ast/Type.cpp @@ -0,0 +1,62 @@ +/* + 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 + +using namespace solidity; +using namespace solidity::frontend::experimental; + +bool Sort::operator==(Sort const& _rhs) const +{ + if (classes.size() != _rhs.classes.size()) + return false; + for (auto [lhs, rhs]: ranges::zip_view(classes, _rhs.classes)) + if (lhs != rhs) + return false; + return true; +} + +bool Sort::operator<=(Sort const& _rhs) const +{ + for (auto c: classes) + if (!_rhs.classes.count(c)) + return false; + return true; +} + +Sort Sort::operator+(Sort const& _rhs) const +{ + Sort result { classes }; + result.classes += _rhs.classes; + return result; +} + + +Sort Sort::operator-(Sort const& _rhs) const +{ + Sort result { classes }; + result.classes -= _rhs.classes; + return result; +} diff --git a/libsolidity/experimental/ast/Type.h b/libsolidity/experimental/ast/Type.h new file mode 100644 index 000000000000..dde41b1df875 --- /dev/null +++ b/libsolidity/experimental/ast/Type.h @@ -0,0 +1,154 @@ +/* + 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::frontend::experimental +{ + +class TypeSystem; + +struct TypeConstant; +struct TypeVariable; + +using Type = std::variant; + +enum class PrimitiveType +{ + Void, + Function, + TypeFunction, + Itself, + Unit, + Pair, + Sum, + Word, + Bool, + Integer +}; + +enum class PrimitiveClass +{ + Type +}; + +// TODO: move elsewhere? +enum class BuiltinClass +{ + Integer, + Mul, + Add, + Equal, + Less, + LessOrEqual, + Greater, + GreaterOrEqual +}; + +struct TypeConstructor +{ +public: + TypeConstructor(TypeConstructor const& _typeConstructor): m_index(_typeConstructor.m_index) {} + TypeConstructor& operator=(TypeConstructor const& _typeConstructor) + { + m_index = _typeConstructor.m_index; + return *this; + } + bool operator<(TypeConstructor const& _rhs) const + { + return m_index < _rhs.m_index; + } + bool operator==(TypeConstructor const& _rhs) const + { + return m_index == _rhs.m_index; + } + bool operator!=(TypeConstructor const& _rhs) const + { + return m_index != _rhs.m_index; + } +private: + friend class TypeSystem; + TypeConstructor(std::size_t _index): m_index(_index) {} + std::size_t m_index = 0; +}; + +struct TypeConstant +{ + TypeConstructor constructor; + std::vector arguments; +}; + +struct TypeClass +{ +public: + TypeClass(TypeClass const& _typeClass): m_index(_typeClass.m_index) {} + TypeClass& operator=(TypeClass const& _typeConstructor) + { + m_index = _typeConstructor.m_index; + return *this; + } + bool operator<(TypeClass const& _rhs) const + { + return m_index < _rhs.m_index; + } + bool operator==(TypeClass const& _rhs) const + { + return m_index == _rhs.m_index; + } + bool operator!=(TypeClass const& _rhs) const + { + return m_index != _rhs.m_index; + } +private: + friend class TypeSystem; + TypeClass(std::size_t _index): m_index(_index) {} + std::size_t m_index = 0; +}; + +struct Sort +{ + std::set classes; + bool operator==(Sort const& _rhs) const; + bool operator!=(Sort const& _rhs) const { return !operator==(_rhs); } + bool operator<=(Sort const& _rhs) const; + Sort operator+(Sort const& _rhs) const; + Sort operator-(Sort const& _rhs) const; +}; + +struct Arity +{ + std::vector argumentSorts; + TypeClass typeClass; +}; + +struct TypeVariable +{ + std::size_t index() const { return m_index; } + Sort const& sort() const { return m_sort; } +private: + friend class TypeSystem; + std::size_t m_index = 0; + Sort m_sort; + TypeVariable(std::size_t _index, Sort _sort): m_index(_index), m_sort(std::move(_sort)) {} +}; + +} diff --git a/libsolidity/experimental/ast/TypeSystem.cpp b/libsolidity/experimental/ast/TypeSystem.cpp new file mode 100644 index 000000000000..680edce845f6 --- /dev/null +++ b/libsolidity/experimental/ast/TypeSystem.cpp @@ -0,0 +1,347 @@ +/* + 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 + +using namespace solidity; +using namespace solidity::frontend; +using namespace solidity::frontend::experimental; + +std::vector TypeEnvironment::unify(Type _a, Type _b) +{ + std::vector failures; + auto unificationFailure = [&]() { + failures.emplace_back(UnificationFailure{TypeMismatch{_a, _b}}); + }; + _a = resolve(_a); + _b = resolve(_b); + std::visit(util::GenericVisitor{ + [&](TypeVariable _left, TypeVariable _right) { + if (_left.index() == _right.index()) + solAssert(_left.sort() == _right.sort()); + else + { + if (_left.sort() <= _right.sort()) + failures += instantiate(_left, _right); + else if (_right.sort() <= _left.sort()) + failures += instantiate(_right, _left); + else + { + Type newVar = m_typeSystem.freshVariable(_left.sort() + _right.sort()); + failures += instantiate(_left, newVar); + failures += instantiate(_right, newVar); + } + } + }, + [&](TypeVariable _var, auto) { + failures += instantiate(_var, _b); + }, + [&](auto, TypeVariable _var) { + failures += instantiate(_var, _a); + }, + [&](TypeConstant _left, TypeConstant _right) { + if (_left.constructor != _right.constructor) + return unificationFailure(); + if (_left.arguments.size() != _right.arguments.size()) + return unificationFailure(); + for (auto&& [left, right]: ranges::zip_view(_left.arguments, _right.arguments)) + failures += unify(left, right); + }, + [&](auto, auto) { + unificationFailure(); + } + }, _a, _b); + return failures; +} + +bool TypeEnvironment::typeEquals(Type _lhs, Type _rhs) const +{ + return std::visit(util::GenericVisitor{ + [&](TypeVariable _left, TypeVariable _right) { + if (_left.index() == _right.index()) + { + solAssert(_left.sort() == _right.sort()); + return true; + } + return false; + }, + [&](TypeConstant _left, TypeConstant _right) { + if (_left.constructor != _right.constructor) + return false; + if (_left.arguments.size() != _right.arguments.size()) + return false; + for (auto&& [left, right]: ranges::zip_view(_left.arguments, _right.arguments)) + if (!typeEquals(left, right)) + return false; + return true; + }, + [&](auto, auto) { + return false; + } + }, resolve(_lhs), resolve(_rhs)); +} + +TypeEnvironment TypeEnvironment::clone() const +{ + TypeEnvironment result{m_typeSystem}; + result.m_typeVariables = m_typeVariables; + return result; +} + +TypeSystem::TypeSystem() +{ + auto declarePrimitiveClass = [&](std::string _name) { + return std::visit(util::GenericVisitor{ + [](std::string _error) -> TypeClass { + solAssert(false, _error); + }, + [](TypeClass _class) -> TypeClass { return _class; } + }, declareTypeClass(_name, nullptr, true /* _primitive */)); + }; + + m_primitiveTypeClasses.emplace(PrimitiveClass::Type, declarePrimitiveClass("type")); + + for (auto [type, name, arity]: std::initializer_list>{ + {PrimitiveType::TypeFunction, "tfun", 2}, + {PrimitiveType::Function, "fun", 2}, + {PrimitiveType::Itself, "itself", 1}, + {PrimitiveType::Void, "void", 0}, + {PrimitiveType::Unit, "unit", 0}, + {PrimitiveType::Pair, "pair", 2}, + {PrimitiveType::Sum, "sum", 2}, + {PrimitiveType::Word, "word", 0}, + {PrimitiveType::Integer, "integer", 0}, + {PrimitiveType::Bool, "bool", 0}, + }) + m_primitiveTypeConstructors.emplace(type, declareTypeConstructor(name, name, arity, nullptr)); + + TypeClass classType = primitiveClass(PrimitiveClass::Type); + //TypeClass classKind = primitiveClass(PrimitiveClass::Kind); + Sort typeSort{{classType}}; + m_typeConstructors.at(m_primitiveTypeConstructors.at(PrimitiveType::TypeFunction).m_index).arities = {Arity{std::vector{{typeSort},{typeSort}}, classType}}; + m_typeConstructors.at(m_primitiveTypeConstructors.at(PrimitiveType::Function).m_index).arities = {Arity{std::vector{{typeSort, typeSort}}, classType}}; + m_typeConstructors.at(m_primitiveTypeConstructors.at(PrimitiveType::Itself).m_index).arities = {Arity{std::vector{{typeSort, typeSort}}, classType}}; +} + +experimental::Type TypeSystem::freshVariable(Sort _sort) +{ + size_t index = m_numTypeVariables++; + return TypeVariable(index, std::move(_sort)); +} + +experimental::Type TypeSystem::freshTypeVariable(Sort _sort) +{ + _sort.classes.emplace(primitiveClass(PrimitiveClass::Type)); + return freshVariable(_sort); +} + +std::vector TypeEnvironment::instantiate(TypeVariable _variable, Type _type) +{ + for (auto const& maybeTypeVar: TypeEnvironmentHelpers{*this}.typeVars(_type)) + if (auto const* typeVar = std::get_if(&maybeTypeVar)) + if (typeVar->index() == _variable.index()) + return {UnificationFailure{RecursiveUnification{_variable, _type}}}; + Sort typeSort = sort(_type); + if (!(_variable.sort() <= typeSort)) + { + return {UnificationFailure{SortMismatch{_type, _variable.sort() - typeSort}}}; + } + solAssert(m_typeVariables.emplace(_variable.index(), _type).second); + return {}; +} + +experimental::Type TypeEnvironment::resolve(Type _type) const +{ + Type result = _type; + while (auto const* var = std::get_if(&result)) + if (Type const* resolvedType = util::valueOrNullptr(m_typeVariables, var->index())) + result = *resolvedType; + else + break; + return result; +} + +experimental::Type TypeEnvironment::resolveRecursive(Type _type) const +{ + return std::visit(util::GenericVisitor{ + [&](TypeConstant const& _typeConstant) -> Type { + return TypeConstant{ + _typeConstant.constructor, + _typeConstant.arguments | ranges::views::transform([&](Type const& _argType) { + return resolveRecursive(_argType); + }) | ranges::to> + }; + }, + [](TypeVariable const& _typeVar) -> Type { + return _typeVar; + }, + [](std::monostate _nothing) -> Type { + return _nothing; + } + }, resolve(_type)); +} + +Sort TypeEnvironment::sort(Type _type) const +{ + return std::visit(util::GenericVisitor{ + [&](TypeConstant const& _expression) -> Sort + { + auto const& constructorInfo = m_typeSystem.constructorInfo(_expression.constructor); + auto argumentSorts = _expression.arguments | ranges::views::transform([&](Type _argumentType) { + return sort(resolve(_argumentType)); + }) | ranges::to>; + Sort sort; + for (auto const& arity: constructorInfo.arities) + { + solAssert(arity.argumentSorts.size() == argumentSorts.size()); + bool hasArity = true; + for (auto&& [argumentSort, arityArgumentSort]: ranges::zip_view(argumentSorts, arity.argumentSorts)) + { + if (!(arityArgumentSort <= argumentSort)) + { + hasArity = false; + break; + } + } + + if (hasArity) + sort.classes.insert(arity.typeClass); + } + return sort; + }, + [](TypeVariable const& _variable) -> Sort { return _variable.sort(); }, + [](std::monostate) -> Sort { solAssert(false); } + }, _type); +} + +TypeConstructor TypeSystem::declareTypeConstructor(std::string _name, std::string _canonicalName, size_t _arguments, Declaration const* _declaration) +{ + solAssert(m_canonicalTypeNames.insert(_canonicalName).second, "Duplicate canonical type name."); + Sort baseSort{{primitiveClass(PrimitiveClass::Type)}}; + size_t index = m_typeConstructors.size(); + m_typeConstructors.emplace_back(TypeConstructorInfo{ + _name, + _canonicalName, + {Arity{std::vector{_arguments, baseSort}, primitiveClass(PrimitiveClass::Type)}}, + _declaration + }); + TypeConstructor constructor{index}; + if (_arguments) + { + std::vector argumentSorts; + std::generate_n(std::back_inserter(argumentSorts), _arguments, [&](){ return Sort{{primitiveClass(PrimitiveClass::Type)}}; }); + std::vector argumentTypes; + std::generate_n(std::back_inserter(argumentTypes), _arguments, [&](){ return freshVariable({}); }); + auto error = instantiateClass(type(constructor, argumentTypes), Arity{argumentSorts, primitiveClass(PrimitiveClass::Type)}); + solAssert(!error, *error); + } + else + { + auto error = instantiateClass(type(constructor, {}), Arity{{}, primitiveClass(PrimitiveClass::Type)}); + solAssert(!error, *error); + } + return constructor; +} + +std::variant TypeSystem::declareTypeClass(std::string _name, Declaration const* _declaration, bool _primitive) +{ + TypeClass typeClass{m_typeClasses.size()}; + + Type typeVariable = (_primitive ? freshVariable({{typeClass}}) : freshTypeVariable({{typeClass}})); + solAssert(std::holds_alternative(typeVariable)); + + m_typeClasses.emplace_back(TypeClassInfo{ + typeVariable, + _name, + _declaration + }); + return typeClass; +} + +experimental::Type TypeSystem::type(TypeConstructor _constructor, std::vector _arguments) const +{ + // TODO: proper error handling + auto const& info = m_typeConstructors.at(_constructor.m_index); + solAssert( + info.arguments() == _arguments.size(), + fmt::format("Type constructor '{}' accepts {} type arguments (got {}).", constructorInfo(_constructor).name, info.arguments(), _arguments.size()) + ); + return TypeConstant{_constructor, _arguments}; +} + +experimental::Type TypeEnvironment::fresh(Type _type) +{ + std::unordered_map mapping; + auto freshImpl = [&](Type _type, auto _recurse) -> Type { + return std::visit(util::GenericVisitor{ + [&](TypeConstant const& _type) -> Type { + return TypeConstant{ + _type.constructor, + _type.arguments | ranges::views::transform([&](Type _argType) { + return _recurse(_argType, _recurse); + }) | ranges::to> + }; + }, + [&](TypeVariable const& _var) -> Type { + if (auto* mapped = util::valueOrNullptr(mapping, _var.index())) + { + auto* typeVariable = std::get_if(mapped); + solAssert(typeVariable); + // TODO: can there be a mismatch? + solAssert(typeVariable->sort() == _var.sort()); + return *mapped; + } + return mapping[_var.index()] = m_typeSystem.freshTypeVariable(_var.sort()); + }, + [](std::monostate) -> Type { solAssert(false); } + }, resolve(_type)); + }; + return freshImpl(_type, freshImpl); +} + +std::optional TypeSystem::instantiateClass(Type _instanceVariable, Arity _arity) +{ + if (!TypeSystemHelpers{*this}.isTypeConstant(_instanceVariable)) + return "Invalid instance variable."; + auto [typeConstructor, typeArguments] = TypeSystemHelpers{*this}.destTypeConstant(_instanceVariable); + auto& typeConstructorInfo = m_typeConstructors.at(typeConstructor.m_index); + if (_arity.argumentSorts.size() != typeConstructorInfo.arguments()) + return "Invalid arity."; + if (typeArguments.size() != typeConstructorInfo.arguments()) + return "Invalid arity."; + + typeConstructorInfo.arities.emplace_back(_arity); + + return std::nullopt; +} diff --git a/libsolidity/experimental/ast/TypeSystem.h b/libsolidity/experimental/ast/TypeSystem.h new file mode 100644 index 000000000000..cac0feb81fe2 --- /dev/null +++ b/libsolidity/experimental/ast/TypeSystem.h @@ -0,0 +1,184 @@ +/* + 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::frontend +{ +class Declaration; +} + +namespace solidity::frontend::experimental +{ + +class TypeEnvironment +{ +public: + struct TypeMismatch + { + Type a; + Type b; + }; + + struct SortMismatch { + Type type; + Sort sort; + }; + + struct RecursiveUnification + { + Type var; + Type type; + }; + + using UnificationFailure = std::variant< + TypeMismatch, + SortMismatch, + RecursiveUnification + >; + + TypeEnvironment(TypeSystem& _typeSystem): m_typeSystem(_typeSystem) {} + TypeEnvironment(TypeEnvironment const&) = delete; + TypeEnvironment& operator=(TypeEnvironment const&) = delete; + TypeEnvironment clone() const; + + Type resolve(Type _type) const; + Type resolveRecursive(Type _type) const; + Type fresh(Type _type); + [[nodiscard]] std::vector unify(Type _a, Type _b); + Sort sort(Type _type) const; + bool typeEquals(Type _lhs, Type _rhs) const; + + TypeSystem& typeSystem() { return m_typeSystem; } + TypeSystem const& typeSystem() const { return m_typeSystem; } + +private: + TypeEnvironment(TypeEnvironment&& _env): + m_typeSystem(_env.m_typeSystem), + m_typeVariables(std::move(_env.m_typeVariables)) + {} + + [[nodiscard]] std::vector instantiate(TypeVariable _variable, Type _type); + + TypeSystem& m_typeSystem; + std::map m_typeVariables; +}; + +class TypeSystem +{ +public: + struct TypeConstructorInfo + { + std::string name; + std::string canonicalName; + std::vector arities; + Declaration const* typeDeclaration = nullptr; + size_t arguments() const + { + solAssert(!arities.empty()); + return arities.front().argumentSorts.size(); + } + }; + struct TypeClassInfo + { + Type typeVariable; + std::string name; + Declaration const* classDeclaration = nullptr; + }; + TypeSystem(); + TypeSystem(TypeSystem const&) = delete; + TypeSystem const& operator=(TypeSystem const&) = delete; + Type type(PrimitiveType _typeConstructor, std::vector _arguments) const + { + return type(m_primitiveTypeConstructors.at(_typeConstructor), std::move(_arguments)); + } + Type type(TypeConstructor _typeConstructor, std::vector _arguments) const; + std::string typeName(TypeConstructor _typeConstructor) const + { + // TODO: proper error handling + return m_typeConstructors.at(_typeConstructor.m_index).name; + } + std::string canonicalName(TypeConstructor _typeConstructor) const + { + // TODO: proper error handling + return m_typeConstructors.at(_typeConstructor.m_index).canonicalName; + } + TypeConstructor declareTypeConstructor(std::string _name, std::string _canonicalName, size_t _arguments, Declaration const* _declaration); + TypeConstructor constructor(PrimitiveType _type) const + { + return m_primitiveTypeConstructors.at(_type); + } + TypeClass primitiveClass(PrimitiveClass _class) const + { + return m_primitiveTypeClasses.at(_class); + } + size_t constructorArguments(TypeConstructor _typeConstructor) const + { + // TODO: error handling + return m_typeConstructors.at(_typeConstructor.m_index).arguments(); + } + TypeConstructorInfo const& constructorInfo(TypeConstructor _typeConstructor) const + { + // TODO: error handling + return m_typeConstructors.at(_typeConstructor.m_index); + } + TypeConstructorInfo const& constructorInfo(PrimitiveType _typeConstructor) const + { + return constructorInfo(constructor(_typeConstructor)); + } + + std::variant declareTypeClass(std::string _name, Declaration const* _declaration, bool _primitive = false); + [[nodiscard]] std::optional instantiateClass(Type _instanceVariable, Arity _arity); + + Type freshTypeVariable(Sort _sort); + + TypeEnvironment const& env() const { return m_globalTypeEnvironment; } + TypeEnvironment& env() { return m_globalTypeEnvironment; } + + Type freshVariable(Sort _sort); + std::string typeClassName(TypeClass _class) const { return m_typeClasses.at(_class.m_index).name; } + Declaration const* typeClassDeclaration(TypeClass _class) const { return m_typeClasses.at(_class.m_index).classDeclaration; } + Type typeClassVariable(TypeClass _class) const + { + return m_typeClasses.at(_class.m_index).typeVariable; + } + + TypeClassInfo const& typeClassInfo(TypeClass _class) const + { + return m_typeClasses.at(_class.m_index); + } + +private: + friend class TypeEnvironment; + size_t m_numTypeVariables = 0; + std::map m_primitiveTypeConstructors; + std::map m_primitiveTypeClasses; + std::set m_canonicalTypeNames; + std::vector m_typeConstructors; + std::vector m_typeClasses; + TypeEnvironment m_globalTypeEnvironment{*this}; +}; + +} diff --git a/libsolidity/experimental/ast/TypeSystemHelper.cpp b/libsolidity/experimental/ast/TypeSystemHelper.cpp new file mode 100644 index 000000000000..bc7dd4fcc35a --- /dev/null +++ b/libsolidity/experimental/ast/TypeSystemHelper.cpp @@ -0,0 +1,404 @@ +/* + 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 + +using namespace solidity::langutil; +using namespace solidity::frontend; +using namespace solidity::frontend::experimental; + +/*std::optional experimental::typeConstructorFromTypeName(Analysis const& _analysis, TypeName const& _typeName) +{ + if (auto const* elementaryTypeName = dynamic_cast(&_typeName)) + { + if (auto constructor = typeConstructorFromToken(_analysis, elementaryTypeName->typeName().token())) + return *constructor; + } + else if (auto const* userDefinedType = dynamic_cast(&_typeName)) + { + if (auto const* referencedDeclaration = userDefinedType->pathNode().annotation().referencedDeclaration) + return _analysis.annotation(*referencedDeclaration).typeConstructor; + } + return nullopt; +}*/ +/* +std::optional experimental::typeConstructorFromToken(Analysis const& _analysis, langutil::Token _token) +{ + TypeSystem const& typeSystem = _analysis.typeSystem(); + switch (_token) + { + case Token::Void: + return typeSystem.builtinConstructor(BuiltinType::Void); + case Token::Fun: + return typeSystem.builtinConstructor(BuiltinType::Function); + case Token::Unit: + return typeSystem.builtinConstructor(BuiltinType::Unit); + case Token::Pair: + return typeSystem.builtinConstructor(BuiltinType::Pair); + case Token::Word: + return typeSystem.builtinConstructor(BuiltinType::Word); + case Token::IntegerType: + return typeSystem.builtinConstructor(BuiltinType::Integer); + case Token::Bool: + return typeSystem.builtinConstructor(BuiltinType::Bool); + default: + return nullopt; + } +}*/ + +std::optional experimental::builtinClassFromToken(langutil::Token _token) +{ + switch (_token) + { + case Token::Integer: + return BuiltinClass::Integer; + case Token::Mul: + return BuiltinClass::Mul; + case Token::Add: + return BuiltinClass::Add; + case Token::Equal: + return BuiltinClass::Equal; + case Token::LessThan: + return BuiltinClass::Less; + case Token::LessThanOrEqual: + return BuiltinClass::LessOrEqual; + case Token::GreaterThan: + return BuiltinClass::Greater; + case Token::GreaterThanOrEqual: + return BuiltinClass::GreaterOrEqual; + default: + return std::nullopt; + } +} +/* +std::optional experimental::typeClassFromTypeClassName(TypeClassName const& _typeClass) +{ + return std::visit(util::GenericVisitor{ + [&](ASTPointer _path) -> optional { + auto classDefinition = dynamic_cast(_path->annotation().referencedDeclaration); + if (!classDefinition) + return nullopt; + return TypeClass{classDefinition}; + }, + [&](Token _token) -> optional { + return typeClassFromToken(_token); + } + }, _typeClass.name()); +} +*/ +experimental::Type TypeSystemHelpers::tupleType(std::vector _elements) const +{ + if (_elements.empty()) + return typeSystem.type(PrimitiveType::Unit, {}); + if (_elements.size() == 1) + return _elements.front(); + Type result = _elements.back(); + for (Type type: _elements | ranges::views::reverse | ranges::views::drop_exactly(1)) + result = typeSystem.type(PrimitiveType::Pair, {type, result}); + return result; +} + +std::vector TypeSystemHelpers::destTupleType(Type _tupleType) const +{ + if (!isTypeConstant(_tupleType)) + return {_tupleType}; + TypeConstructor pairConstructor = typeSystem.constructor(PrimitiveType::Pair); + auto [constructor, arguments] = destTypeConstant(_tupleType); + if (constructor == typeSystem.constructor(PrimitiveType::Unit)) + return {}; + if (constructor != pairConstructor) + return {_tupleType}; + solAssert(arguments.size() == 2); + + std::vector result; + result.emplace_back(arguments.front()); + Type tail = arguments.back(); + while (true) + { + if (!isTypeConstant(tail)) + break; + auto [tailConstructor, tailArguments] = destTypeConstant(tail); + if (tailConstructor != pairConstructor) + break; + solAssert(tailArguments.size() == 2); + result.emplace_back(tailArguments.front()); + tail = tailArguments.back(); + } + result.emplace_back(tail); + return result; +} + +experimental::Type TypeSystemHelpers::sumType(std::vector _elements) const +{ + if (_elements.empty()) + return typeSystem.type(PrimitiveType::Void, {}); + if (_elements.size() == 1) + return _elements.front(); + Type result = _elements.back(); + for (Type type: _elements | ranges::views::reverse | ranges::views::drop_exactly(1)) + result = typeSystem.type(PrimitiveType::Sum, {type, result}); + return result; +} + +std::vector TypeSystemHelpers::destSumType(Type _tupleType) const +{ + if (!isTypeConstant(_tupleType)) + return {_tupleType}; + TypeConstructor sumConstructor = typeSystem.constructor(PrimitiveType::Sum); + auto [constructor, arguments] = destTypeConstant(_tupleType); + if (constructor == typeSystem.constructor(PrimitiveType::Void)) + return {}; + if (constructor != sumConstructor) + return {_tupleType}; + solAssert(arguments.size() == 2); + + std::vector result; + result.emplace_back(arguments.front()); + Type tail = arguments.back(); + while (true) + { + if (!isTypeConstant(tail)) + break; + auto [tailConstructor, tailArguments] = destTypeConstant(tail); + if (tailConstructor != sumConstructor) + break; + solAssert(tailArguments.size() == 2); + result.emplace_back(tailArguments.front()); + tail = tailArguments.back(); + } + result.emplace_back(tail); + return result; +} + +std::tuple> TypeSystemHelpers::destTypeConstant(Type _type) const +{ + using ResultType = std::tuple>; + return std::visit(util::GenericVisitor{ + [&](TypeConstant const& _type) -> ResultType { + return std::make_tuple(_type.constructor, _type.arguments); + }, + [](auto const&) -> ResultType { + solAssert(false); + } + }, _type); +} + +bool TypeSystemHelpers::isTypeConstant(Type _type) const +{ + return std::visit(util::GenericVisitor{ + [&](TypeConstant const&) -> bool { + return true; + }, + [](auto const&) -> bool { + return false; + } + }, _type); +} + +bool TypeSystemHelpers::isPrimitiveType(Type _type, PrimitiveType _primitiveType) const +{ + if (!isTypeConstant(_type)) + return false; + auto constructor = std::get<0>(destTypeConstant(_type)); + return constructor == typeSystem.constructor(_primitiveType); +} + +experimental::Type TypeSystemHelpers::functionType(experimental::Type _argType, experimental::Type _resultType) const +{ + return typeSystem.type(PrimitiveType::Function, {_argType, _resultType}); +} + +std::tuple TypeSystemHelpers::destFunctionType(Type _functionType) const +{ + auto [constructor, arguments] = destTypeConstant(_functionType); + solAssert(constructor == typeSystem.constructor(PrimitiveType::Function)); + solAssert(arguments.size() == 2); + return std::make_tuple(arguments.front(), arguments.back()); +} + +bool TypeSystemHelpers::isFunctionType(Type _type) const +{ + return isPrimitiveType(_type, PrimitiveType::Function); +} + +experimental::Type TypeSystemHelpers::typeFunctionType(experimental::Type _argType, experimental::Type _resultType) const +{ + return typeSystem.type(PrimitiveType::TypeFunction, {_argType, _resultType}); +} + +std::tuple TypeSystemHelpers::destTypeFunctionType(Type _functionType) const +{ + auto [constructor, arguments] = destTypeConstant(_functionType); + solAssert(constructor == typeSystem.constructor(PrimitiveType::TypeFunction)); + solAssert(arguments.size() == 2); + return std::make_tuple(arguments.front(), arguments.back()); +} + +bool TypeSystemHelpers::isTypeFunctionType(Type _type) const +{ + return isPrimitiveType(_type, PrimitiveType::TypeFunction); +} + +std::vector TypeEnvironmentHelpers::typeVars(Type _type) const +{ + std::set indices; + std::vector typeVars; + auto typeVarsImpl = [&](Type _type, auto _recurse) -> void { + std::visit(util::GenericVisitor{ + [&](TypeConstant const& _type) { + for (auto arg: _type.arguments) + _recurse(arg, _recurse); + }, + [&](TypeVariable const& _var) { + if (indices.emplace(_var.index()).second) + typeVars.emplace_back(_var); + }, + [](std::monostate) { solAssert(false); } + }, env.resolve(_type)); + }; + typeVarsImpl(_type, typeVarsImpl); + return typeVars; + +} + + +std::string TypeSystemHelpers::sortToString(Sort _sort) const +{ + switch (_sort.classes.size()) + { + case 0: + return "()"; + case 1: + return typeSystem.typeClassName(*_sort.classes.begin()); + default: + { + std::stringstream stream; + stream << "("; + for (auto typeClass: _sort.classes | ranges::views::drop_last(1)) + stream << typeSystem.typeClassName(typeClass) << ", "; + stream << typeSystem.typeClassName(*_sort.classes.rbegin()) << ")"; + return stream.str(); + } + } +} + +std::string TypeEnvironmentHelpers::canonicalTypeName(Type _type) const +{ + return visit(util::GenericVisitor{ + [&](TypeConstant _type) -> std::string { + std::stringstream stream; + stream << env.typeSystem().constructorInfo(_type.constructor).canonicalName; + if (!_type.arguments.empty()) + { + stream << "$"; + for (auto type: _type.arguments | ranges::views::drop_last(1)) + stream << canonicalTypeName(type) << "$"; + stream << canonicalTypeName(_type.arguments.back()); + stream << "$"; + } + return stream.str(); + }, + [](TypeVariable) -> std::string { + solAssert(false); + }, + [](std::monostate) -> std::string { + solAssert(false); + }, + }, env.resolve(_type)); +} + +std::string TypeEnvironmentHelpers::typeToString(Type const& _type) const +{ + std::map)>> formatters{ + {env.typeSystem().constructor(PrimitiveType::Function), [&](auto const& _args) { + solAssert(_args.size() == 2); + return fmt::format("{} -> {}", typeToString(_args.front()), typeToString(_args.back())); + }}, + {env.typeSystem().constructor(PrimitiveType::Unit), [&](auto const& _args) { + solAssert(_args.size() == 0); + return "()"; + }}, + {env.typeSystem().constructor(PrimitiveType::Pair), [&](auto const& _arguments) { + auto tupleTypes = TypeSystemHelpers{env.typeSystem()}.destTupleType(_arguments.back()); + std::string result = "("; + result += typeToString(_arguments.front()); + for (auto type: tupleTypes) + result += ", " + typeToString(type); + result += ")"; + return result; + }}, + }; + return std::visit(util::GenericVisitor{ + [&](TypeConstant const& _type) { + if (auto* formatter = util::valueOrNullptr(formatters, _type.constructor)) + return (*formatter)(_type.arguments); + std::stringstream stream; + stream << env.typeSystem().constructorInfo(_type.constructor).name; + if (!_type.arguments.empty()) + { + stream << "("; + for (auto type: _type.arguments | ranges::views::drop_last(1)) + stream << typeToString(type) << ", "; + stream << typeToString(_type.arguments.back()); + stream << ")"; + } + return stream.str(); + }, + [&](TypeVariable const& _type) { + std::stringstream stream; + std::string varName; + size_t index = _type.index(); + varName += static_cast('a' + (index%26)); + while (index /= 26) + varName += static_cast('a' + (index%26)); + reverse(varName.begin(), varName.end()); + stream << '\'' << varName; + switch (_type.sort().classes.size()) + { + case 0: + break; + case 1: + stream << ":" << env.typeSystem().typeClassName(*_type.sort().classes.begin()); + break; + default: + stream << ":("; + for (auto typeClass: _type.sort().classes | ranges::views::drop_last(1)) + stream << env.typeSystem().typeClassName(typeClass) << ", "; + stream << env.typeSystem().typeClassName(*_type.sort().classes.rbegin()); + stream << ")"; + break; + } + return stream.str(); + }, + [](std::monostate) -> std::string { solAssert(false); } + }, env.resolve(_type)); +} diff --git a/libsolidity/experimental/ast/TypeSystemHelper.h b/libsolidity/experimental/ast/TypeSystemHelper.h new file mode 100644 index 000000000000..fd55c1bf842e --- /dev/null +++ b/libsolidity/experimental/ast/TypeSystemHelper.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 +#include + +namespace solidity::frontend::experimental +{ +class Analysis; +enum class BuiltinClass; +//std::optional typeConstructorFromTypeName(Analysis const& _analysis, TypeName const& _typeName); +//std::optional typeConstructorFromToken(Analysis const& _analysis, langutil::Token _token); +//std::optional typeClassFromTypeClassName(TypeClassName const& _typeClass); +std::optional builtinClassFromToken(langutil::Token _token); + +struct TypeSystemHelpers +{ + TypeSystem const& typeSystem; + std::tuple> destTypeConstant(Type _type) const; + bool isTypeConstant(Type _type) const; + bool isPrimitiveType(Type _type, PrimitiveType _primitiveType) const; + Type tupleType(std::vector _elements) const; + std::vector destTupleType(Type _tupleType) const; + Type sumType(std::vector _elements) const; + std::vector destSumType(Type _tupleType) const; + Type functionType(Type _argType, Type _resultType) const; + std::tuple destFunctionType(Type _functionType) const; + bool isFunctionType(Type _type) const; + Type typeFunctionType(Type _argType, Type _resultType) const; + std::tuple destTypeFunctionType(Type _functionType) const; + bool isTypeFunctionType(Type _type) const; + std::string sortToString(Sort _sort) const; +}; + +struct TypeEnvironmentHelpers +{ + TypeEnvironment const& env; + std::string typeToString(Type const& _type) const; + std::string canonicalTypeName(Type _type) const; + std::vector typeVars(Type _type) const; +}; + +} diff --git a/libsolidity/experimental/codegen/Common.cpp b/libsolidity/experimental/codegen/Common.cpp new file mode 100644 index 000000000000..de50fd47c682 --- /dev/null +++ b/libsolidity/experimental/codegen/Common.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 +#include + +#include + +#include + +using namespace solidity::langutil; +using namespace solidity::frontend; +using namespace solidity::util; +using namespace solidity::yul; + +namespace solidity::frontend::experimental +{ + +std::string IRNames::function(TypeEnvironment const& _env, FunctionDefinition const& _function, Type _type) +{ + if (_function.isConstructor()) + return constructor(*_function.annotation().contract); + + return "fun_" + _function.name() + "_" + std::to_string(_function.id()) + "$" + TypeEnvironmentHelpers{_env}.canonicalTypeName(_type) + "$"; +} + +std::string IRNames::function(VariableDeclaration const& _varDecl) +{ + return "getter_fun_" + _varDecl.name() + "_" + std::to_string(_varDecl.id()); +} + +std::string IRNames::creationObject(ContractDefinition const& _contract) +{ + return _contract.name() + "_" + toString(_contract.id()); +} + +std::string IRNames::deployedObject(ContractDefinition const& _contract) +{ + return _contract.name() + "_" + toString(_contract.id()) + "_deployed"; +} + +std::string IRNames::constructor(ContractDefinition const& _contract) +{ + return "constructor_" + _contract.name() + "_" + std::to_string(_contract.id()); +} + +std::string IRNames::localVariable(VariableDeclaration const& _declaration) +{ + return "var_" + _declaration.name() + '_' + std::to_string(_declaration.id()); +} + +std::string IRNames::localVariable(Expression const& _expression) +{ + return "expr_" + std::to_string(_expression.id()); +} + +} diff --git a/libsolidity/experimental/codegen/Common.h b/libsolidity/experimental/codegen/Common.h new file mode 100644 index 000000000000..25f05efcbf09 --- /dev/null +++ b/libsolidity/experimental/codegen/Common.h @@ -0,0 +1,41 @@ +/* + 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::frontend::experimental +{ + +struct IRNames +{ + static std::string function(TypeEnvironment const& _env, FunctionDefinition const& _function, Type _type); + static std::string function(VariableDeclaration const& _varDecl); + static std::string creationObject(ContractDefinition const& _contract); + static std::string deployedObject(ContractDefinition const& _contract); + static std::string constructor(ContractDefinition const& _contract); + static std::string localVariable(VariableDeclaration const& _declaration); + static std::string localVariable(Expression const& _expression); +}; + +} diff --git a/libsolidity/experimental/codegen/IRGenerationContext.h b/libsolidity/experimental/codegen/IRGenerationContext.h new file mode 100644 index 000000000000..b0958cf6780a --- /dev/null +++ b/libsolidity/experimental/codegen/IRGenerationContext.h @@ -0,0 +1,55 @@ +/* + 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::frontend::experimental +{ + +class Analysis; + +struct IRGenerationContext +{ + Analysis const& analysis; + TypeEnvironment const* env = nullptr; + void enqueueFunctionDefinition(FunctionDefinition const* _functionDefinition, Type _type) + { + QueuedFunction queue{_functionDefinition, env->resolve(_type)}; + for (auto type: generatedFunctions[_functionDefinition]) + if (env->typeEquals(type, _type)) + return; + functionQueue.emplace_back(queue); + } + struct QueuedFunction + { + FunctionDefinition const* function = nullptr; + Type type = std::monostate{}; + }; + std::list functionQueue; + std::map> generatedFunctions; +}; + +} diff --git a/libsolidity/experimental/codegen/IRGenerator.cpp b/libsolidity/experimental/codegen/IRGenerator.cpp new file mode 100644 index 000000000000..257bd2be16ba --- /dev/null +++ b/libsolidity/experimental/codegen/IRGenerator.cpp @@ -0,0 +1,160 @@ +/* + 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 + +using namespace solidity; +using namespace solidity::frontend::experimental; +using namespace solidity::langutil; +using namespace solidity::util; + +IRGenerator::IRGenerator( + EVMVersion _evmVersion, + std::optional _eofVersion, + frontend::RevertStrings, std::map, + DebugInfoSelection const&, + CharStreamProvider const*, + Analysis const& _analysis +) +: +m_evmVersion(_evmVersion), +m_eofVersion(_eofVersion), +// m_debugInfoSelection(_debugInfoSelection), +// m_soliditySourceProvider(_soliditySourceProvider), +m_env(_analysis.typeSystem().env().clone()), +m_context{_analysis, &m_env, {}, {}} +{ +} + +std::string IRGenerator::run( + ContractDefinition const& _contract, + bytes const& /*_cborMetadata*/, + std::map const& /*_otherYulSources*/ +) +{ + + Whiskers t(R"( + object "" { + code { + codecopy(0, dataoffset(""), datasize("")) + return(0, datasize("")) + } + object "" { + code { + + } + } + } + )"); + t("CreationObject", IRNames::creationObject(_contract)); + t("DeployedObject", IRNames::deployedObject(_contract)); + t("code", generate(_contract)); + + return t.render(); +} + +std::string IRGenerator::generate(ContractDefinition const& _contract) +{ + std::stringstream code; + code << "{\n"; + if (_contract.fallbackFunction()) + { + auto type = m_context.analysis.annotation(*_contract.fallbackFunction()).type; + solAssert(type); + type = m_context.env->resolve(*type); + code << IRNames::function(*m_context.env, *_contract.fallbackFunction(), *type) << "()\n"; + m_context.enqueueFunctionDefinition(_contract.fallbackFunction(), *type); + } + code << "revert(0,0)\n"; + code << "}\n"; + + while (!m_context.functionQueue.empty()) + { + auto queueEntry = m_context.functionQueue.front(); + m_context.functionQueue.pop_front(); + auto& generatedTypes = m_context.generatedFunctions.insert(std::make_pair(queueEntry.function, std::vector{})).first->second; + if (!util::contains_if(generatedTypes, [&](auto const& _generatedType) { return m_context.env->typeEquals(_generatedType, queueEntry.type); })) + { + generatedTypes.emplace_back(queueEntry.type); + code << generate(*queueEntry.function, queueEntry.type); + } + } + + return code.str(); +} + +std::string IRGenerator::generate(FunctionDefinition const& _function, Type _type) +{ + TypeEnvironment newEnv = m_context.env->clone(); + ScopedSaveAndRestore envRestore{m_context.env, &newEnv}; + auto type = m_context.analysis.annotation(_function).type; + solAssert(type); + for (auto err: newEnv.unify(*type, _type)) + { + TypeEnvironmentHelpers helper{newEnv}; + solAssert(false, helper.typeToString(*type) + " <-> " + helper.typeToString(_type)); + } + std::stringstream code; + code << "function " << IRNames::function(newEnv, _function, _type) << "("; + if (_function.parameters().size() > 1) + for (auto const& arg: _function.parameters() | ranges::views::drop_last(1)) + code << IRNames::localVariable(*arg) << ", "; + if (!_function.parameters().empty()) + code << IRNames::localVariable(*_function.parameters().back()); + code << ")"; + if (_function.experimentalReturnExpression()) + { + auto returnType = m_context.analysis.annotation(*_function.experimentalReturnExpression()).type; + solAssert(returnType); + if (!m_env.typeEquals(*returnType, m_context.analysis.typeSystem().type(PrimitiveType::Unit, {}))) + { + // TODO: destructure tuples. + code << " -> " << IRNames::localVariable(*_function.experimentalReturnExpression()) << " "; + } + } + code << "{\n"; + for (auto _statement: _function.body().statements()) + { + IRGeneratorForStatements statementGenerator{m_context}; + code << statementGenerator.generate(*_statement); + } + code << "}\n"; + return code.str(); +} diff --git a/libsolidity/experimental/codegen/IRGenerator.h b/libsolidity/experimental/codegen/IRGenerator.h new file mode 100644 index 000000000000..cce8c730572b --- /dev/null +++ b/libsolidity/experimental/codegen/IRGenerator.h @@ -0,0 +1,72 @@ +/* + 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 + +#include +#include +#include + +#include + +#include + +namespace solidity::frontend::experimental +{ + +class Analysis; + +class IRGenerator +{ +public: + IRGenerator( + langutil::EVMVersion _evmVersion, + std::optional _eofVersion, + RevertStrings /*_revertStrings*/, + std::map /*_sourceIndices*/, + langutil::DebugInfoSelection const& /*_debugInfoSelection*/, + langutil::CharStreamProvider const* /*_soliditySourceProvider*/, + Analysis const& _analysis + ); + + std::string run( + ContractDefinition const& _contract, + bytes const& _cborMetadata, + std::map const& _otherYulSources + ); + + std::string generate(ContractDefinition const& _contract); + std::string generate(FunctionDefinition const& _function, Type _type); +private: + langutil::EVMVersion const m_evmVersion; + std::optional const m_eofVersion; + OptimiserSettings const m_optimiserSettings; +// langutil::DebugInfoSelection m_debugInfoSelection = {}; +// langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr; + TypeEnvironment m_env; + IRGenerationContext m_context; +}; + +} diff --git a/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp new file mode 100644 index 000000000000..cb4705f0d189 --- /dev/null +++ b/libsolidity/experimental/codegen/IRGeneratorForStatements.cpp @@ -0,0 +1,393 @@ +/* + 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 + +using namespace solidity; +using namespace solidity::util; +using namespace solidity::frontend; +using namespace solidity::frontend::experimental; +using namespace std::string_literals; + +std::string IRGeneratorForStatements::generate(ASTNode const& _node) +{ + _node.accept(*this); + return m_code.str(); +} + + +namespace +{ + +struct CopyTranslate: public yul::ASTCopier +{ + CopyTranslate( + IRGenerationContext const& _context, + yul::Dialect const& _dialect, + std::map _references + ): m_context(_context), m_dialect(_dialect), m_references(std::move(_references)) {} + + using ASTCopier::operator(); + + yul::Expression operator()(yul::Identifier const& _identifier) override + { + // The operator() function is only called in lvalue context. In rvalue context, + // only translate(yul::Identifier) is called. + if (m_references.count(&_identifier)) + return translateReference(_identifier); + else + return ASTCopier::operator()(_identifier); + } + + yul::YulString translateIdentifier(yul::YulString _name) override + { + if (m_dialect.builtin(_name)) + return _name; + else + return yul::YulString{"usr$" + _name.str()}; + } + + yul::Identifier translate(yul::Identifier const& _identifier) override + { + if (!m_references.count(&_identifier)) + return ASTCopier::translate(_identifier); + + yul::Expression translated = translateReference(_identifier); + solAssert(std::holds_alternative(translated)); + return std::get(std::move(translated)); + } + +private: + + /// Translates a reference to a local variable, potentially including + /// a suffix. Might return a literal, which causes this to be invalid in + /// lvalue-context. + yul::Expression translateReference(yul::Identifier const& _identifier) + { + auto const& reference = m_references.at(&_identifier); + auto const varDecl = dynamic_cast(reference.declaration); + solAssert(varDecl, "External reference in inline assembly to something that is not a variable declaration."); + auto type = m_context.analysis.annotation(*varDecl).type; + solAssert(type); + solAssert(m_context.env->typeEquals(*type, m_context.analysis.typeSystem().type(PrimitiveType::Word, {}))); + std::string value = IRNames::localVariable(*varDecl); + return yul::Identifier{_identifier.debugData, yul::YulString{value}}; + } + + IRGenerationContext const& m_context; + yul::Dialect const& m_dialect; + std::map m_references; +}; + +} + +bool IRGeneratorForStatements::visit(TupleExpression const& _tupleExpression) +{ + std::vector components; + for (auto const& component: _tupleExpression.components()) + { + solUnimplementedAssert(component); + component->accept(*this); + components.emplace_back(IRNames::localVariable(*component)); + } + + solUnimplementedAssert(false, "No support for tuples."); + + return false; +} + +bool IRGeneratorForStatements::visit(InlineAssembly const& _assembly) +{ + CopyTranslate bodyCopier{m_context, _assembly.dialect(), _assembly.annotation().externalReferences}; + yul::Statement modified = bodyCopier(_assembly.operations()); + solAssert(std::holds_alternative(modified)); + m_code << yul::AsmPrinter()(std::get(modified)) << "\n"; + return false; +} + +bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _variableDeclarationStatement) +{ + if (_variableDeclarationStatement.initialValue()) + _variableDeclarationStatement.initialValue()->accept(*this); + solAssert(_variableDeclarationStatement.declarations().size() == 1, "multi variable declarations not supported"); + VariableDeclaration const* variableDeclaration = _variableDeclarationStatement.declarations().front().get(); + solAssert(variableDeclaration); + // TODO: check the type of the variable; register local variable; initialize + m_code << "let " << IRNames::localVariable(*variableDeclaration); + if (_variableDeclarationStatement.initialValue()) + m_code << " := " << IRNames::localVariable(*_variableDeclarationStatement.initialValue()); + m_code << "\n"; + return false; +} + +bool IRGeneratorForStatements::visit(ExpressionStatement const&) +{ + return true; +} + +bool IRGeneratorForStatements::visit(Identifier const& _identifier) +{ + if (auto const* var = dynamic_cast(_identifier.annotation().referencedDeclaration)) + { + m_code << "let " << IRNames::localVariable(_identifier) << " := " << IRNames::localVariable(*var) << "\n"; + } + else if (auto const* function = dynamic_cast(_identifier.annotation().referencedDeclaration)) + solAssert(m_expressionDeclaration.emplace(&_identifier, function).second); + else if (auto const* typeClass = dynamic_cast(_identifier.annotation().referencedDeclaration)) + solAssert(m_expressionDeclaration.emplace(&_identifier, typeClass).second); + else if (auto const* typeDefinition = dynamic_cast(_identifier.annotation().referencedDeclaration)) + solAssert(m_expressionDeclaration.emplace(&_identifier, typeDefinition).second); + else + solAssert(false, "Unsupported Identifier"); + return false; +} + +void IRGeneratorForStatements::endVisit(Return const& _return) +{ + if (Expression const* value = _return.expression()) + { + solAssert(_return.annotation().function, "Invalid return."); + solAssert(_return.annotation().function->experimentalReturnExpression(), "Invalid return."); + m_code << IRNames::localVariable(*_return.annotation().function->experimentalReturnExpression()) << " := " << IRNames::localVariable(*value) << "\n"; + } + + m_code << "leave\n"; +} + +experimental::Type IRGeneratorForStatements::type(ASTNode const& _node) const +{ + auto type = m_context.analysis.annotation(_node).type; + solAssert(type); + return *type; +} + +void IRGeneratorForStatements::endVisit(BinaryOperation const& _binaryOperation) +{ + TypeSystemHelpers helper{m_context.analysis.typeSystem()}; + Type leftType = type(_binaryOperation.leftExpression()); + Type rightType = type(_binaryOperation.rightExpression()); + Type resultType = type(_binaryOperation); + Type functionType = helper.functionType(helper.tupleType({leftType, rightType}), resultType); + auto [typeClass, memberName] = m_context.analysis.annotation().operators.at(_binaryOperation.getOperator()); + auto const& functionDefinition = resolveTypeClassFunction(typeClass, memberName, functionType); + // TODO: deduplicate with FunctionCall + // TODO: get around resolveRecursive by passing the environment further down? + functionType = m_context.env->resolveRecursive(functionType); + m_context.enqueueFunctionDefinition(&functionDefinition, functionType); + // TODO: account for return stack size + m_code << "let " << IRNames::localVariable(_binaryOperation) << " := " << IRNames::function(*m_context.env, functionDefinition, functionType) << "(" + << IRNames::localVariable(_binaryOperation.leftExpression()) << ", " << IRNames::localVariable(_binaryOperation.rightExpression()) << ")\n"; +} + +namespace +{ +TypeRegistration::TypeClassInstantiations const& typeClassInstantiations(IRGenerationContext const& _context, TypeClass _class) +{ + auto const* typeClassDeclaration = _context.analysis.typeSystem().typeClassDeclaration(_class); + if (typeClassDeclaration) + return _context.analysis.annotation(*typeClassDeclaration).instantiations; + // TODO: better mechanism than fetching by name. + auto& instantiations = _context.analysis.annotation().builtinClassInstantiations; + auto& builtinClassesByName = _context.analysis.annotation().builtinClassesByName; + return instantiations.at(builtinClassesByName.at(_context.analysis.typeSystem().typeClassName(_class))); +} +} + +FunctionDefinition const& IRGeneratorForStatements::resolveTypeClassFunction(TypeClass _class, std::string _name, Type _type) +{ + TypeSystemHelpers helper{m_context.analysis.typeSystem()}; + + TypeEnvironment env = m_context.env->clone(); + Type genericFunctionType = env.fresh(m_context.analysis.annotation().typeClassFunctions.at(_class).at(_name)); + auto typeVars = TypeEnvironmentHelpers{env}.typeVars(genericFunctionType); + solAssert(typeVars.size() == 1); + solAssert(env.unify(genericFunctionType, _type).empty()); + auto typeClassInstantiation = std::get<0>(helper.destTypeConstant(env.resolve(typeVars.front()))); + + auto const& instantiations = typeClassInstantiations(m_context, _class); + TypeClassInstantiation const* instantiation = instantiations.at(typeClassInstantiation); + FunctionDefinition const* functionDefinition = nullptr; + for (auto const& node: instantiation->subNodes()) + { + auto const* def = dynamic_cast(node.get()); + solAssert(def); + if (def->name() == _name) + { + functionDefinition = def; + break; + } + } + solAssert(functionDefinition); + return *functionDefinition; +} + +void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) +{ + TypeSystemHelpers helper{m_context.analysis.typeSystem()}; + // TODO: avoid resolve? + auto expressionType = m_context.env->resolve(type(_memberAccess.expression())); + auto constructor = std::get<0>(helper.destTypeConstant(expressionType)); + auto memberAccessType = type(_memberAccess); + // TODO: better mechanism + if (constructor == m_context.analysis.typeSystem().constructor(PrimitiveType::Bool)) + { + if (_memberAccess.memberName() == "abs") + solAssert(m_expressionDeclaration.emplace(&_memberAccess, Builtins::ToBool).second); + else if (_memberAccess.memberName() == "rep") + solAssert(m_expressionDeclaration.emplace(&_memberAccess, Builtins::FromBool).second); + return; + } + auto const* declaration = m_context.analysis.typeSystem().constructorInfo(constructor).typeDeclaration; + solAssert(declaration); + if (auto const* typeClassDefinition = dynamic_cast(declaration)) + { + solAssert(m_context.analysis.annotation(*typeClassDefinition).typeClass.has_value()); + TypeClass typeClass = m_context.analysis.annotation(*typeClassDefinition).typeClass.value(); + solAssert(m_expressionDeclaration.emplace( + &_memberAccess, + &resolveTypeClassFunction(typeClass, _memberAccess.memberName(), memberAccessType) + ).second); + } + else if (dynamic_cast(declaration)) + { + if (_memberAccess.memberName() == "abs" || _memberAccess.memberName() == "rep") + solAssert(m_expressionDeclaration.emplace(&_memberAccess, Builtins::Identity).second); + else + solAssert(false); + } + else + solAssert(false); +} + +bool IRGeneratorForStatements::visit(ElementaryTypeNameExpression const&) +{ + // TODO: is this always a no-op? + return false; +} + +void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) +{ + Type functionType = type(_functionCall.expression()); + solUnimplementedAssert(m_expressionDeclaration.count(&_functionCall.expression()) != 0, "No support for calling functions pointers yet."); + auto declaration = m_expressionDeclaration.at(&_functionCall.expression()); + if (auto builtin = std::get_if(&declaration)) + { + switch (*builtin) + { + case Builtins::FromBool: + case Builtins::Identity: + solAssert(_functionCall.arguments().size() == 1); + m_code << "let " << IRNames::localVariable(_functionCall) << " := " << IRNames::localVariable(*_functionCall.arguments().front()) << "\n"; + return; + case Builtins::ToBool: + solAssert(_functionCall.arguments().size() == 1); + m_code << "let " << IRNames::localVariable(_functionCall) << " := iszero(iszero(" << IRNames::localVariable(*_functionCall.arguments().front()) << "))\n"; + return; + } + solAssert(false); + } + FunctionDefinition const* functionDefinition = dynamic_cast(std::get(declaration)); + solAssert(functionDefinition); + // TODO: get around resolveRecursive by passing the environment further down? + functionType = m_context.env->resolveRecursive(functionType); + m_context.enqueueFunctionDefinition(functionDefinition, functionType); + // TODO: account for return stack size + solAssert(!functionDefinition->returnParameterList()); + if (functionDefinition->experimentalReturnExpression()) + m_code << "let " << IRNames::localVariable(_functionCall) << " := "; + m_code << IRNames::function(*m_context.env, *functionDefinition, functionType) << "("; + auto const& arguments = _functionCall.arguments(); + if (arguments.size() > 1) + for (auto arg: arguments | ranges::views::drop_last(1)) + m_code << IRNames::localVariable(*arg) << ", "; + if (!arguments.empty()) + m_code << IRNames::localVariable(*arguments.back()); + m_code << ")\n"; +} + +bool IRGeneratorForStatements::visit(FunctionCall const&) +{ + return true; +} + +bool IRGeneratorForStatements::visit(Block const& _block) +{ + m_code << "{\n"; + solAssert(!_block.unchecked()); + for (auto const& statement: _block.statements()) + statement->accept(*this); + m_code << "}\n"; + return false; +} + +bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement) +{ + _ifStatement.condition().accept(*this); + if (_ifStatement.falseStatement()) + { + m_code << "switch " << IRNames::localVariable(_ifStatement.condition()) << " {\n"; + m_code << "case 0 {\n"; + _ifStatement.falseStatement()->accept(*this); + m_code << "}\n"; + m_code << "default {\n"; + _ifStatement.trueStatement().accept(*this); + m_code << "}\n"; + } + else + { + m_code << "if " << IRNames::localVariable(_ifStatement.condition()) << " {\n"; + _ifStatement.trueStatement().accept(*this); + m_code << "}\n"; + } + return false; +} + +bool IRGeneratorForStatements::visit(Assignment const& _assignment) +{ + _assignment.rightHandSide().accept(*this); + auto const* lhs = dynamic_cast(&_assignment.leftHandSide()); + solAssert(lhs, "Can only assign to identifiers."); + auto const* lhsVar = dynamic_cast(lhs->annotation().referencedDeclaration); + solAssert(lhsVar, "Can only assign to identifiers referring to variables."); + m_code << IRNames::localVariable(*lhsVar) << " := " << IRNames::localVariable(_assignment.rightHandSide()) << "\n"; + + m_code << "let " << IRNames::localVariable(_assignment) << " := " << IRNames::localVariable(*lhsVar) << "\n"; + return false; +} + + +bool IRGeneratorForStatements::visitNode(ASTNode const&) +{ + solAssert(false, "Unsupported AST node during statement code generation."); +} diff --git a/libsolidity/experimental/codegen/IRGeneratorForStatements.h b/libsolidity/experimental/codegen/IRGeneratorForStatements.h new file mode 100644 index 000000000000..527a6abe8562 --- /dev/null +++ b/libsolidity/experimental/codegen/IRGeneratorForStatements.h @@ -0,0 +1,71 @@ +/* + 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::frontend::experimental +{ +class Analysis; + +class IRGeneratorForStatements: public ASTConstVisitor +{ +public: + IRGeneratorForStatements(IRGenerationContext& _context): m_context(_context) {} + + std::string generate(ASTNode const& _node); +private: + bool visit(ExpressionStatement const& _expressionStatement) override; + bool visit(Block const& _block) override; + bool visit(IfStatement const& _ifStatement) override; + bool visit(Assignment const& _assignment) override; + bool visit(Identifier const& _identifier) override; + bool visit(FunctionCall const& _functionCall) override; + void endVisit(FunctionCall const& _functionCall) override; + bool visit(ElementaryTypeNameExpression const& _elementaryTypeNameExpression) override; + bool visit(MemberAccess const&) override { return true; } + bool visit(TupleExpression const&) override; + void endVisit(MemberAccess const& _memberAccess) override; + bool visit(InlineAssembly const& _inlineAssembly) override; + bool visit(BinaryOperation const&) override { return true; } + void endVisit(BinaryOperation const& _binaryOperation) override; + bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; + bool visit(Return const&) override { return true; } + void endVisit(Return const& _return) override; + /// Default visit will reject all AST nodes that are not explicitly supported. + bool visitNode(ASTNode const& _node) override; + IRGenerationContext& m_context; + std::stringstream m_code; + enum class Builtins + { + Identity, + FromBool, + ToBool + }; + std::map> m_expressionDeclaration; + Type type(ASTNode const& _node) const; + + FunctionDefinition const& resolveTypeClassFunction(TypeClass _class, std::string _name, Type _type); +}; + +} diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 058151928b2a..b4e3cf7f389a 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -57,6 +57,8 @@ #include #include +#include +#include #include #include @@ -312,6 +314,7 @@ void CompilerStack::reset(bool _keepSettings) { m_stackState = Empty; m_sources.clear(); + m_maxAstId.reset(); m_smtlib2Responses.clear(); m_unhandledSMTLib2Queries.clear(); if (!_keepSettings) @@ -412,6 +415,10 @@ bool CompilerStack::parse() m_stackState = (m_stopAfter <= Parsed ? Parsed : ParsedAndImported); storeContractDefinitions(); + + solAssert(!m_maxAstId.has_value()); + m_maxAstId = parser.maxID(); + return true; } @@ -464,7 +471,7 @@ bool CompilerStack::analyze() m_globalContext = std::make_shared(); // We need to keep the same resolver during the whole process. - NameAndTypeResolver resolver(*m_globalContext, m_evmVersion, m_errorReporter); + NameAndTypeResolver resolver(*m_globalContext, m_evmVersion, m_errorReporter, experimentalSolidity); for (Source const* source: m_sourceOrder) if (source->ast && !resolver.registerDeclarations(*source->ast)) return false; @@ -478,10 +485,12 @@ bool CompilerStack::analyze() resolver.warnHomonymDeclarations(); - DocStringTagParser docStringTagParser(m_errorReporter); - for (Source const* source: m_sourceOrder) - if (source->ast && !docStringTagParser.parseDocStrings(*source->ast)) - noErrors = false; + { + DocStringTagParser docStringTagParser(m_errorReporter); + for (Source const* source: m_sourceOrder) + if (source->ast && !docStringTagParser.parseDocStrings(*source->ast)) + noErrors = false; + } // Requires DocStringTagParser for (Source const* source: m_sourceOrder) @@ -658,7 +667,8 @@ bool CompilerStack::analyzeLegacy(bool _noErrorsSoFar) bool CompilerStack::analyzeExperimental() { solAssert(!m_experimentalAnalysis); - m_experimentalAnalysis = std::make_unique(m_errorReporter); + solAssert(m_maxAstId && *m_maxAstId >= 0); + m_experimentalAnalysis = std::make_unique(m_errorReporter, static_cast(*m_maxAstId)); std::vector> sourceAsts; for (Source const* source: m_sourceOrder) if (source->ast) @@ -925,8 +935,6 @@ std::string const& CompilerStack::yulIR(std::string const& _contractName) const if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); - solUnimplementedAssert(!isExperimentalSolidity()); - return contract(_contractName).yulIR; } @@ -945,8 +953,6 @@ std::string const& CompilerStack::yulIROptimized(std::string const& _contractNam if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); - solUnimplementedAssert(!isExperimentalSolidity()); - return contract(_contractName).yulIROptimized; } @@ -965,8 +971,6 @@ evmasm::LinkerObject const& CompilerStack::object(std::string const& _contractNa if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); - solUnimplementedAssert(!isExperimentalSolidity()); - return contract(_contractName).object; } @@ -975,8 +979,6 @@ evmasm::LinkerObject const& CompilerStack::runtimeObject(std::string const& _con if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); - solUnimplementedAssert(!isExperimentalSolidity()); - return contract(_contractName).runtimeObject; } @@ -986,8 +988,6 @@ std::string CompilerStack::assemblyString(std::string const& _contractName, Stri if (m_stackState != CompilationSuccessful) solThrow(CompilerError, "Compilation was not successful."); - solUnimplementedAssert(!isExperimentalSolidity()); - Contract const& currentContract = contract(_contractName); if (currentContract.evmAssembly) return currentContract.evmAssembly->assemblyString(m_debugInfoSelection, _sourceCodes); @@ -1496,9 +1496,6 @@ void CompilerStack::generateIR(ContractDefinition const& _contract) { solAssert(m_stackState >= AnalysisSuccessful, ""); - if (m_experimentalAnalysis) - solThrow(CompilerError, "IR codegen after experimental analysis is unsupported."); - Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); if (!compiledContract.yulIR.empty()) return; @@ -1522,20 +1519,40 @@ void CompilerStack::generateIR(ContractDefinition const& _contract) for (auto const& pair: m_contracts) otherYulSources.emplace(pair.second.contract, pair.second.yulIR); - IRGenerator generator( - m_evmVersion, - m_eofVersion, - m_revertStrings, - sourceIndices(), - m_debugInfoSelection, - this, - m_optimiserSettings - ); - compiledContract.yulIR = generator.run( - _contract, - createCBORMetadata(compiledContract, /* _forIR */ true), - otherYulSources - ); + if (m_experimentalAnalysis) + { + experimental::IRGenerator generator( + m_evmVersion, + m_eofVersion, + m_revertStrings, + sourceIndices(), + m_debugInfoSelection, + this, + *m_experimentalAnalysis + ); + compiledContract.yulIR = generator.run( + _contract, + {}, // TODO: createCBORMetadata(compiledContract, /* _forIR */ true), + otherYulSources + ); + } + else + { + IRGenerator generator( + m_evmVersion, + m_eofVersion, + m_revertStrings, + sourceIndices(), + m_debugInfoSelection, + this, + m_optimiserSettings + ); + compiledContract.yulIR = generator.run( + _contract, + createCBORMetadata(compiledContract, /* _forIR */ true), + otherYulSources + ); + } yul::YulStack stack( m_evmVersion, @@ -1977,3 +1994,9 @@ bool CompilerStack::isExperimentalSolidity() const ranges::all_of(m_sourceOrder, [](auto const* _source) { return _source->ast->experimentalSolidity(); } ) ; } + +experimental::Analysis const& CompilerStack::experimentalAnalysis() const +{ + solAssert(!!m_experimentalAnalysis); + return *m_experimentalAnalysis; +} diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 0ccb33219e54..57d30e602d1d 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -236,6 +236,13 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac /// @returns false on error. bool compile(State _stopAfter = State::CompilationSuccessful); + /// Checks whether experimental analysis is on; used in SyntaxTests to skip compilation in case it's ``true``. + /// @returns true if experimental analysis is set + bool isExperimentalAnalysis() const + { + return !!m_experimentalAnalysis; + } + /// @returns the list of sources (paths) used virtual std::vector sourceNames() const override; @@ -350,6 +357,8 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac bool isExperimentalSolidity() const; + experimental::Analysis const& experimentalAnalysis() const; + static MetadataFormat defaultMetadataFormat() { return VersionIsRelease ? MetadataFormat::WithReleaseVersionTag : MetadataFormat::WithPrereleaseVersionTag; @@ -509,6 +518,7 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac std::map m_libraries; ImportRemapper m_importRemapper; std::map m_sources; + std::optional m_maxAstId; std::vector m_unhandledSMTLib2Queries; std::map m_smtlib2Responses; std::shared_ptr m_globalContext; diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 76b5d23d31ac..42be1ab9eecf 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -100,6 +100,9 @@ ASTPointer Parser::parse(CharStream& _charStream) while (m_scanner->currentToken() == Token::Pragma) nodes.push_back(parsePragmaDirective(false)); + if (m_experimentalSolidityEnabledInCurrentSourceUnit) + m_scanner->setScannerMode(ScannerKind::ExperimentalSolidity); + while (m_scanner->currentToken() != Token::EOS) { switch (m_scanner->currentToken()) @@ -123,7 +126,10 @@ ASTPointer Parser::parse(CharStream& _charStream) nodes.push_back(parseEnumDefinition()); break; case Token::Type: - nodes.push_back(parseUserDefinedValueTypeDefinition()); + if (m_experimentalSolidityEnabledInCurrentSourceUnit) + nodes.push_back(parseTypeDefinition()); + else + nodes.push_back(parseUserDefinedValueTypeDefinition()); break; case Token::Using: nodes.push_back(parseUsingDirective()); @@ -134,6 +140,14 @@ ASTPointer Parser::parse(CharStream& _charStream) case Token::Event: nodes.push_back(parseEventDefinition()); break; + case Token::Class: + solAssert(m_experimentalSolidityEnabledInCurrentSourceUnit); + nodes.push_back(parseTypeClassDefinition()); + break; + case Token::Instantiation: + solAssert(m_experimentalSolidityEnabledInCurrentSourceUnit); + nodes.push_back(parseTypeClassInstantiation()); + break; default: if ( // Workaround because `error` is not a keyword. @@ -591,18 +605,29 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari else break; } - if (m_scanner->currentToken() == Token::Returns) + if (m_experimentalSolidityEnabledInCurrentSourceUnit) { - bool const permitEmptyParameterList = false; - advance(); - result.returnParameters = parseParameterList(options, permitEmptyParameterList); + if (m_scanner->currentToken() == Token::RightArrow) + { + advance(); + result.experimentalReturnExpression = parseBinaryExpression(); + } } else - result.returnParameters = createEmptyParameterList(); + { + if (m_scanner->currentToken() == Token::Returns) + { + bool const permitEmptyParameterList = m_experimentalSolidityEnabledInCurrentSourceUnit; + advance(); + result.returnParameters = parseParameterList(options, permitEmptyParameterList); + } + else + result.returnParameters = createEmptyParameterList(); + } return result; } -ASTPointer Parser::parseFunctionDefinition(bool _freeFunction) +ASTPointer Parser::parseFunctionDefinition(bool _freeFunction, bool _allowBody) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); @@ -650,9 +675,16 @@ ASTPointer Parser::parseFunctionDefinition(bool _freeFunction) FunctionHeaderParserResult header = parseFunctionHeader(false); + if (m_experimentalSolidityEnabledInCurrentSourceUnit) + solAssert(!header.returnParameters); + else + solAssert(!header.experimentalReturnExpression); + ASTPointer block; nodeFactory.markEndPosition(); - if (m_scanner->currentToken() == Token::Semicolon) + if (!_allowBody) + expectToken(Token::Semicolon); + else if (m_scanner->currentToken() == Token::Semicolon) advance(); else { @@ -672,7 +704,8 @@ ASTPointer Parser::parseFunctionDefinition(bool _freeFunction) header.parameters, header.modifiers, header.returnParameters, - block + block, + header.experimentalReturnExpression ); } @@ -1190,10 +1223,12 @@ ASTPointer Parser::parseTypeName() ASTPointer Parser::parseFunctionType() { + solAssert(!m_experimentalSolidityEnabledInCurrentSourceUnit); RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); expectToken(Token::Function); FunctionHeaderParserResult header = parseFunctionHeader(true); + solAssert(!header.experimentalReturnExpression); return nodeFactory.createNode( header.parameters, header.returnParameters, @@ -1249,16 +1284,29 @@ ASTPointer Parser::parseParameterList( std::vector> parameters; VarDeclParserOptions options(_options); options.allowEmptyName = true; + if (m_experimentalSolidityEnabledInCurrentSourceUnit && m_scanner->currentToken() == Token::Identifier) + { + // Parses unary parameter lists without parentheses. TODO: is this a good idea in all cases? Including arguments? + parameters = {parsePostfixVariableDeclaration()}; + nodeFactory.setEndPositionFromNode(parameters.front()); + return nodeFactory.createNode(parameters); + } expectToken(Token::LParen); + auto parseSingleVariableDeclaration = [&]() { + if (m_experimentalSolidityEnabledInCurrentSourceUnit) + return parsePostfixVariableDeclaration(); + else + return parseVariableDeclaration(options); + }; if (!_allowEmpty || m_scanner->currentToken() != Token::RParen) { - parameters.push_back(parseVariableDeclaration(options)); + parameters.push_back(parseSingleVariableDeclaration()); while (m_scanner->currentToken() != Token::RParen) { if (m_scanner->currentToken() == Token::Comma && m_scanner->peekNextToken() == Token::RParen) fatalParserError(7591_error, "Unexpected trailing comma in parameter list."); expectToken(Token::Comma); - parameters.push_back(parseVariableDeclaration(options)); + parameters.push_back(parseSingleVariableDeclaration()); } } nodeFactory.markEndPosition(); @@ -1607,12 +1655,221 @@ ASTPointer Parser::parseRevertStatement(ASTPointer c return nodeFactory.createNode(_docString, errorCall); } +ASTPointer Parser::parsePostfixVariableDeclarationStatement( + ASTPointer const& _docString +) +{ + RecursionGuard recursionGuard(*this); + ASTNodeFactory nodeFactory(*this); + + expectToken(Token::Let); + + std::vector> variables; + variables.emplace_back(parsePostfixVariableDeclaration()); + nodeFactory.setEndPositionFromNode(variables.back()); + + ASTPointer value; + if (m_scanner->currentToken() == Token::Assign) + { + advance(); + value = parseExpression(); + nodeFactory.setEndPositionFromNode(value); + } + return nodeFactory.createNode(_docString, variables, value); +} + +ASTPointer Parser::parsePostfixVariableDeclaration() +{ + RecursionGuard recursionGuard(*this); + ASTNodeFactory nodeFactory(*this); + + ASTPointer const documentation = parseStructuredDocumentation(); + + nodeFactory.markEndPosition(); + auto [identifier, nameLocation] = expectIdentifierWithLocation(); + + ASTPointer type; + if (m_scanner->currentToken() == Token::Colon) + { + advance(); + type = parseBinaryExpression(); + nodeFactory.setEndPositionFromNode(type); + } + + return nodeFactory.createNode( + nullptr, + identifier, + nameLocation, + nullptr, + Visibility::Default, + documentation, + false, + VariableDeclaration::Mutability::Mutable, + nullptr, + VariableDeclaration::Location::Unspecified, + type + ); +} + +ASTPointer Parser::parseTypeClassDefinition() +{ + solAssert(m_experimentalSolidityEnabledInCurrentSourceUnit); + RecursionGuard recursionGuard(*this); + ASTNodeFactory nodeFactory(*this); + + std::vector> subNodes; + + ASTPointer const documentation = parseStructuredDocumentation(); + + expectToken(Token::Class); + // TODO: parseTypeVariable()? parseTypeVariableDeclaration()? + ASTPointer typeVariable; + { + ASTNodeFactory nodeFactory(*this); + nodeFactory.markEndPosition(); + auto [identifier, nameLocation] = expectIdentifierWithLocation(); + typeVariable = nodeFactory.createNode( + nullptr, + identifier, + nameLocation, + nullptr, + Visibility::Default, + nullptr + ); + } + expectToken(Token::Colon); + auto [name, nameLocation] = expectIdentifierWithLocation(); + expectToken(Token::LBrace); + while (true) + { + Token currentTokenValue = m_scanner->currentToken(); + if (currentTokenValue == Token::RBrace) + break; + expectToken(Token::Function, false); + subNodes.push_back(parseFunctionDefinition(false, false)); + } + nodeFactory.markEndPosition(); + expectToken(Token::RBrace); + + return nodeFactory.createNode( + typeVariable, + name, + nameLocation, + documentation, + subNodes + ); +} + +ASTPointer Parser::parseTypeClassName() +{ + RecursionGuard recursionGuard(*this); + ASTNodeFactory nodeFactory(*this); + std::variant> name; + if (TokenTraits::isBuiltinTypeClassName(m_scanner->currentToken())) + { + nodeFactory.markEndPosition(); + name = m_scanner->currentToken(); + advance(); + } + else + { + auto identifierPath = parseIdentifierPath(); + name = identifierPath; + nodeFactory.setEndPositionFromNode(identifierPath); + } + return nodeFactory.createNode(name); +} + +ASTPointer Parser::parseTypeClassInstantiation() +{ + solAssert(m_experimentalSolidityEnabledInCurrentSourceUnit); + RecursionGuard recursionGuard(*this); + ASTNodeFactory nodeFactory(*this); + + std::vector> subNodes; + + expectToken(Token::Instantiation); + // TODO: parseTypeConstructor() + ASTPointer typeConstructor = parseTypeName(); + ASTPointer argumentSorts; + if (m_scanner->currentToken() == Token::LParen) + { + argumentSorts = parseParameterList(); + } + expectToken(Token::Colon); + ASTPointer typeClassName = parseTypeClassName(); + expectToken(Token::LBrace); + while (true) + { + Token currentTokenValue = m_scanner->currentToken(); + if (currentTokenValue == Token::RBrace) + break; + expectToken(Token::Function, false); + // TODO: require body already during parsing? + subNodes.push_back(parseFunctionDefinition(false, true)); + } + nodeFactory.markEndPosition(); + expectToken(Token::RBrace); + + return nodeFactory.createNode( + typeConstructor, + argumentSorts, + typeClassName, + subNodes + ); +} + +ASTPointer Parser::parseTypeDefinition() +{ + solAssert(m_experimentalSolidityEnabledInCurrentSourceUnit); + ASTNodeFactory nodeFactory(*this); + expectToken(Token::Type); + auto&& [name, nameLocation] = expectIdentifierWithLocation(); + + ASTPointer arguments; + if (m_scanner->currentToken() == Token::LParen) + arguments = parseParameterList(); + + ASTPointer expression; + if (m_scanner->currentToken() == Token::Assign) + { + expectToken(Token::Assign); + + if (m_scanner->currentToken() != Token::Builtin) + expression = parseExpression(); + else + { + expectToken(Token::Builtin); + expectToken(Token::LParen); + + expression = nodeFactory.createNode( + std::make_shared(m_scanner->currentLiteral()), + m_scanner->currentLocation() + ); + + expectToken(Token::StringLiteral); + expectToken(Token::RParen); + } + } + nodeFactory.markEndPosition(); + expectToken(Token::Semicolon); + return nodeFactory.createNode( + std::move(name), + std::move(nameLocation), + std::move(arguments), + std::move(expression) + ); +} + ASTPointer Parser::parseSimpleStatement(ASTPointer const& _docString) { RecursionGuard recursionGuard(*this); LookAheadInfo statementType; IndexAccessedPath iap; + if (m_experimentalSolidityEnabledInCurrentSourceUnit && m_scanner->currentToken() == Token::Let) + return parsePostfixVariableDeclarationStatement(_docString); + if (m_scanner->currentToken() == Token::LParen) { ASTNodeFactory nodeFactory(*this); @@ -1715,7 +1972,10 @@ std::pair Parser::tryParseInde { case LookAheadInfo::VariableDeclaration: case LookAheadInfo::Expression: - return std::make_pair(statementType, IndexAccessedPath()); + return std::make_pair( + m_experimentalSolidityEnabledInCurrentSourceUnit ? LookAheadInfo::Expression : statementType, + IndexAccessedPath() + ); default: break; } @@ -1726,6 +1986,9 @@ std::pair Parser::tryParseInde // VariableDeclarationStatement out of it. IndexAccessedPath iap = parseIndexAccessedPath(); + if (m_experimentalSolidityEnabledInCurrentSourceUnit) + return std::make_pair(LookAheadInfo::Expression, std::move(iap)); + if (m_scanner->currentToken() == Token::Identifier || TokenTraits::isLocationSpecifier(m_scanner->currentToken())) return std::make_pair(LookAheadInfo::VariableDeclaration, std::move(iap)); else @@ -1808,9 +2071,9 @@ ASTPointer Parser::parseBinaryExpression( RecursionGuard recursionGuard(*this); ASTPointer expression = parseUnaryExpression(_partiallyParsedExpression); ASTNodeFactory nodeFactory(*this, expression); - int precedence = TokenTraits::precedence(m_scanner->currentToken()); + int precedence = tokenPrecedence(m_scanner->currentToken()); for (; precedence >= _minPrecedence; --precedence) - while (TokenTraits::precedence(m_scanner->currentToken()) == precedence) + while (tokenPrecedence(m_scanner->currentToken()) == precedence) { Token op = m_scanner->currentToken(); advance(); @@ -1827,6 +2090,23 @@ ASTPointer Parser::parseBinaryExpression( return expression; } +int Parser::tokenPrecedence(Token _token) const +{ + if (m_experimentalSolidityEnabledInCurrentSourceUnit) + { + switch (_token) + { + case Token::Colon: + return 1000; + case Token::RightArrow: + return 999; + default: + break; + } + } + return TokenTraits::precedence(m_scanner->currentToken()); +} + ASTPointer Parser::parseUnaryExpression( ASTPointer const& _partiallyParsedExpression ) @@ -2285,7 +2565,14 @@ Parser::IndexAccessedPath Parser::parseIndexAccessedPath() while (m_scanner->currentToken() == Token::Period) { advance(); - iap.path.push_back(parseIdentifierOrAddress()); + if (m_experimentalSolidityEnabledInCurrentSourceUnit && m_scanner->currentToken() == Token::Number) + { + ASTNodeFactory nodeFactory(*this); + nodeFactory.markEndPosition(); + iap.path.push_back(nodeFactory.createNode(getLiteralAndAdvance())); + } + else + iap.path.push_back(parseIdentifierOrAddress()); } } else diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 5f2a1213d5ae..d4c48c7a638a 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -48,6 +48,8 @@ class Parser: public langutil::ParserBase ASTPointer parse(langutil::CharStream& _charStream); + /// Returns the maximal AST node ID assigned so far + int64_t maxID() const { return m_currentNodeID; } private: class ASTNodeFactory; @@ -74,6 +76,7 @@ class Parser: public langutil::ParserBase Visibility visibility = Visibility::Default; StateMutability stateMutability = StateMutability::NonPayable; std::vector> modifiers; + ASTPointer experimentalReturnExpression; }; /// Struct to share parsed function call arguments. @@ -99,7 +102,7 @@ class Parser: public langutil::ParserBase ASTPointer parseOverrideSpecifier(); StateMutability parseStateMutability(); FunctionHeaderParserResult parseFunctionHeader(bool _isStateVariable); - ASTPointer parseFunctionDefinition(bool _freeFunction = false); + ASTPointer parseFunctionDefinition(bool _freeFunction = false, bool _allowBody = true); ASTPointer parseStructDefinition(); ASTPointer parseEnumDefinition(); ASTPointer parseUserDefinedValueTypeDefinition(); @@ -167,6 +170,18 @@ class Parser: public langutil::ParserBase std::pair, langutil::SourceLocation> expectIdentifierWithLocation(); ///@} + ///@{ + ///@name Specialized parsing functions for the AST nodes of experimental solidity. + ASTPointer parsePostfixVariableDeclarationStatement( + ASTPointer const& _docString + ); + ASTPointer parsePostfixVariableDeclaration(); + ASTPointer parseTypeClassDefinition(); + ASTPointer parseTypeClassInstantiation(); + ASTPointer parseTypeDefinition(); + ASTPointer parseTypeClassName(); + ///@} + ///@{ ///@name Helper functions @@ -221,6 +236,8 @@ class Parser: public langutil::ParserBase bool isQuotedPath() const; bool isStdlibPath() const; + int tokenPrecedence(Token _token) const; + ASTPointer getStdlibImportPathAndAdvance(); /// Creates an empty ParameterList at the current location (used if parameters can be omitted). diff --git a/libstdlib/src/stub.sol b/libstdlib/src/stub.sol index 46ae21dcf5a2..bd34e0475acd 100644 --- a/libstdlib/src/stub.sol +++ b/libstdlib/src/stub.sol @@ -3,7 +3,6 @@ pragma solidity >=0.0; pragma experimental solidity; -function identity(uint256 x) pure returns (uint256) +function identity() { - return x; } diff --git a/scripts/ASTImportTest.sh b/scripts/ASTImportTest.sh index 5c3f04d8e0ff..c7f504087c96 100755 --- a/scripts/ASTImportTest.sh +++ b/scripts/ASTImportTest.sh @@ -326,7 +326,14 @@ esac # boost_filesystem_bug specifically tests a local fix for a boost::filesystem # bug. Since the test involves a malformed path, there is no point in running # tests on it. See https://github.com/boostorg/filesystem/issues/176 -IMPORT_TEST_FILES=$(find "${TEST_DIRS[@]}" -name "*.sol" -and -not -name "boost_filesystem_bug.sol" -not -path "*/experimental/*") +# In addition, exclude all experimental Solidity tests (new type inference system) +EXCLUDE_FILES=( + boost_filesystem_bug.sol + pragma_experimental_solidity.sol +) +EXCLUDE_FILES_JOINED=$(printf "! -name %s " "${EXCLUDE_FILES[@]}") +# shellcheck disable=SC2086 +IMPORT_TEST_FILES=$(find "${TEST_DIRS[@]}" -name "*.sol" -and $EXCLUDE_FILES_JOINED -not -path "*/experimental/*") NSOURCES="$(echo "${IMPORT_TEST_FILES}" | wc -l)" echo "Looking at ${NSOURCES} .sol files..." diff --git a/scripts/check_style.sh b/scripts/check_style.sh index 00230c4a4af0..7bcd8cca36c9 100755 --- a/scripts/check_style.sh +++ b/scripts/check_style.sh @@ -29,6 +29,7 @@ NAMESPACE_STD_FREE_FILES=( libsolidity/ast/* libsolidity/codegen/ir/* libsolidity/codegen/* + libsolidity/experimental/* libsolidity/formal/* libsolidity/interface/* libsolidity/lsp/* diff --git a/scripts/common.sh b/scripts/common.sh index 39c1226d1145..d149253bf71f 100644 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -313,3 +313,14 @@ function command_available fail "'${program}' not found or not executed successfully with parameter(s) '${parameters[*]}'. aborting." fi } + +function gnu_grep +{ + if [[ "$OSTYPE" == "darwin"* ]]; then + command_available ggrep --version + ggrep "$@" + else + command_available grep --version + grep "$@" + fi +} diff --git a/scripts/error_codes.py b/scripts/error_codes.py index 8fe2c4c1bee3..4aff6b85e80d 100755 --- a/scripts/error_codes.py +++ b/scripts/error_codes.py @@ -232,16 +232,109 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): return False old_source_only_ids = { - "1218", "1584", "1823", - "1988", "2066", "2833", "3356", - "3893", "3996", "4010", "4458", "4802", - "4902", "5272", "5622", "5798", "5840", "7128", "7400", - "7589", "7593", "7649", "7710", - "8065", "8084", "8140", "8158", - "8312", "8592", "9134", "9609", + "1218", + "1584", + "1823", + "1988", + "2066", + "2833", + "3356", + "3893", + "3996", + "4010", + "4458", + "4802", + "4902", + "5272", + "5622", + "5798", + "5840", + "7128", + "7400", + "7589", + "7593", + "7649", + "7710", + "8065", + "8084", + "8140", + "8158", + "8312", + "8592", + "9134", + "9609", } - new_source_only_ids = source_only_ids - old_source_only_ids + # TODO Cover these with tests and remove from this list as the development of experimental + # TODO Solidity progresses. The aim should be to completely get rid of `experimental_source_only_ids`. + experimental_source_only_ids = { + "1017", + "1439", + "1723", + "1741", + "1801", + "1807", + "2015", + "2138", + "2345", + "2399", + "2599", + "2655", + "2658", + "2934", + "3101", + "3111", + "3195", + "3520", + "3570", + "3573", + "3654", + "4316", + "4337", + "4496", + "4504", + "4686", + "4767", + "4873", + "4955", + "5044", + "5094", + "5096", + "5104", + "5195", + "5262", + "5348", + "5360", + "5387", + "5577", + "5714", + "5731", + "5755", + "5904", + "6175", + "6387", + "6388", + "6460", + "6620", + "6739", + "6948", + "7341", + "7428", + "7531", + "8379", + "8809", + "8953", + "9159", + "9173", + "9282", + "9603", + "9658", + "9817", + "9831", + "9988", + } + + new_source_only_ids = source_only_ids - old_source_only_ids - experimental_source_only_ids if len(new_source_only_ids) != 0: print("The following new error code(s), not covered by tests, found:") print_ids(new_source_only_ids) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f32a14d9183e..d5435237ec7b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -73,6 +73,8 @@ set(libsolidity_sources libsolidity/ASTJSONTest.h libsolidity/ErrorCheck.cpp libsolidity/ErrorCheck.h + libsolidity/FunctionDependencyGraphTest.cpp + libsolidity/FunctionDependencyGraphTest.h libsolidity/GasCosts.cpp libsolidity/GasMeter.cpp libsolidity/GasTest.cpp diff --git a/test/InteractiveTests.h b/test/InteractiveTests.h index 5021d801b3ce..2519fa86f488 100644 --- a/test/InteractiveTests.h +++ b/test/InteractiveTests.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -60,26 +61,27 @@ struct Testsuite /// Array of testsuits that can be run interactively as well as automatically Testsuite const g_interactiveTestsuites[] = { /* - Title Path Subpath SMT NeedsVM Creator function */ - {"Yul Optimizer", "libyul", "yulOptimizerTests", false, false, &yul::test::YulOptimizerTest::create}, - {"Yul Interpreter", "libyul", "yulInterpreterTests", false, false, &yul::test::YulInterpreterTest::create}, - {"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create}, - {"Yul Control Flow Graph", "libyul", "yulControlFlowGraph", false, false, &yul::test::ControlFlowGraphTest::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}, - {"Function Side Effects", "libyul", "functionSideEffects", false, false, &yul::test::FunctionSideEffects::create}, - {"Yul Syntax", "libyul", "yulSyntaxTests", false, false, &yul::test::SyntaxTest::create}, - {"EVM Code Transform", "libyul", "evmCodeTransform", false, false, &yul::test::EVMCodeTransformTest::create, {"nooptions"}}, - {"Syntax", "libsolidity", "syntaxTests", false, false, &SyntaxTest::create}, - {"Semantic", "libsolidity", "semanticTests", false, true, &SemanticTest::create}, - {"JSON AST", "libsolidity", "ASTJSON", false, false, &ASTJSONTest::create}, - {"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create}, - {"JSON Natspec", "libsolidity", "natspecJSON", false, false, &NatspecJSONTest::create}, - {"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create}, - {"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create}, - {"Memory Guard", "libsolidity", "memoryGuardTests", false, false, &MemoryGuardTest::create}, - {"AST Properties", "libsolidity", "astPropertyTests", false, false, &ASTPropertyTest::create}, + Title Path Subpath SMT NeedsVM Creator function */ + {"Yul Optimizer", "libyul", "yulOptimizerTests", false, false, &yul::test::YulOptimizerTest::create}, + {"Yul Interpreter", "libyul", "yulInterpreterTests", false, false, &yul::test::YulInterpreterTest::create}, + {"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create}, + {"Yul Control Flow Graph", "libyul", "yulControlFlowGraph", false, false, &yul::test::ControlFlowGraphTest::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}, + {"Function Side Effects", "libyul", "functionSideEffects", false, false, &yul::test::FunctionSideEffects::create}, + {"Yul Syntax", "libyul", "yulSyntaxTests", false, false, &yul::test::SyntaxTest::create}, + {"EVM Code Transform", "libyul", "evmCodeTransform", false, false, &yul::test::EVMCodeTransformTest::create, {"nooptions"}}, + {"Syntax", "libsolidity", "syntaxTests", false, false, &SyntaxTest::create}, + {"Semantic", "libsolidity", "semanticTests", false, true, &SemanticTest::create}, + {"JSON AST", "libsolidity", "ASTJSON", false, false, &ASTJSONTest::create}, + {"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create}, + {"JSON Natspec", "libsolidity", "natspecJSON", false, false, &NatspecJSONTest::create}, + {"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create}, + {"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create}, + {"Memory Guard", "libsolidity", "memoryGuardTests", false, false, &MemoryGuardTest::create}, + {"AST Properties", "libsolidity", "astPropertyTests", false, false, &ASTPropertyTest::create}, + {"Function Dependency Graph", "libsolidity", "functionDependencyGraphTests", false, false, &FunctionDependencyGraphTest::create}, }; } diff --git a/test/cmdlineTests/~soljson_via_fuzzer/test.sh b/test/cmdlineTests/~soljson_via_fuzzer/test.sh index 2d2312fd1586..9c1d338c0102 100755 --- a/test/cmdlineTests/~soljson_via_fuzzer/test.sh +++ b/test/cmdlineTests/~soljson_via_fuzzer/test.sh @@ -10,7 +10,7 @@ cd "$SOLTMPDIR" "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/test/ "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ -echo ./*.sol | xargs -P 4 -n 50 "${SOLIDITY_BUILD_DIR}/test/tools/solfuzzer" --quiet --input-files -echo ./*.sol | xargs -P 4 -n 50 "${SOLIDITY_BUILD_DIR}/test/tools/solfuzzer" --without-optimizer --quiet --input-files +gnu_grep -L --include="*.sol" "^pragma experimental solidity;$" ./* | xargs -P 4 -n 50 "${SOLIDITY_BUILD_DIR}/test/tools/solfuzzer" --quiet --input-files +gnu_grep -L --include="*.sol" "^pragma experimental solidity;$" ./* | xargs -P 4 -n 50 "${SOLIDITY_BUILD_DIR}/test/tools/solfuzzer" --without-optimizer --quiet --input-files rm -r "$SOLTMPDIR" diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index dcfe49ab48d2..ccad7d49d12e 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -64,7 +64,7 @@ evmasm::AssemblyItems compileContract(std::shared_ptr _sourceCode) Scoper::assignScopes(*sourceUnit); BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit)); GlobalContext globalContext; - NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter); + NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter, false); DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion()); solAssert(!Error::containsErrors(errorReporter.errors()), ""); resolver.registerDeclarations(*sourceUnit); diff --git a/test/libsolidity/FunctionDependencyGraphTest.cpp b/test/libsolidity/FunctionDependencyGraphTest.cpp new file mode 100644 index 000000000000..914fae06f065 --- /dev/null +++ b/test/libsolidity/FunctionDependencyGraphTest.cpp @@ -0,0 +1,59 @@ +/* + 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 + +using namespace solidity; +using namespace solidity::util; +using namespace solidity::util::formatting; +using namespace solidity::langutil; +using namespace solidity::frontend; +using namespace solidity::frontend::test; + +TestCase::TestResult FunctionDependencyGraphTest::run(std::ostream& _stream, std::string const& _linePrefix, bool _formatted) +{ + compiler().reset(); + compiler().setSources(StringMap{{"", m_source}}); + compiler().setViaIR(true); + compiler().setOptimiserSettings(OptimiserSettings::none()); + if (!compiler().compile(CompilerStack::AnalysisSuccessful)) + { + _stream << formatErrors(filteredErrors(), _formatted); + return TestResult::FatalError; + } + + m_obtainedResult.clear(); + for (auto [top, subs]: compiler().experimentalAnalysis().annotation().functionCallGraph.edges) + { + std::string topName = top->name().empty() ? "fallback" : top->name(); + m_obtainedResult += "(" + topName + ") --> {"; + for (auto sub: subs) + m_obtainedResult += sub->name() + ","; + m_obtainedResult += "}\n"; + } + + return checkResult(_stream, _linePrefix, _formatted); +} diff --git a/test/libsolidity/FunctionDependencyGraphTest.h b/test/libsolidity/FunctionDependencyGraphTest.h new file mode 100644 index 000000000000..83f3eb0ab394 --- /dev/null +++ b/test/libsolidity/FunctionDependencyGraphTest.h @@ -0,0 +1,50 @@ +/* + 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 +#include + +namespace solidity::frontend::test +{ + using solidity::test::SyntaxTestError; + + class FunctionDependencyGraphTest: public AnalysisFramework, public TestCase + { + public: + static std::unique_ptr create(Config const& _config) + { + return std::make_unique(_config.filename); + } + + FunctionDependencyGraphTest(std::string const& _filename): TestCase(_filename) + { + m_source = m_reader.source(); + m_expectation = m_reader.simpleExpectations(); + } + + TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override; + }; +} diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index b05d28a8403f..a171a5be0571 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -127,7 +127,7 @@ bytes compileFirstExpression( GlobalContext globalContext; Scoper::assignScopes(*sourceUnit); BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit)); - NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter); + NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter, false); resolver.registerDeclarations(*sourceUnit); BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*sourceUnit), "Resolving names failed"); DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion()); diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 7c4fa8da1e84..f17898c042a5 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -73,7 +73,7 @@ void SyntaxTest::parseAndAnalyze() try { runFramework(withPreamble(m_sources.sources), PipelineStage::Compilation); - if (!pipelineSuccessful() && stageSuccessful(PipelineStage::Analysis)) + if (!pipelineSuccessful() && stageSuccessful(PipelineStage::Analysis) && !compiler().isExperimentalAnalysis()) { ErrorList const& errors = compiler().errors(); auto codeGeneretionErrorCount = count_if(errors.cbegin(), errors.cend(), [](auto const& error) { diff --git a/test/libsolidity/functionDependencyGraphTests/callgraph.sol b/test/libsolidity/functionDependencyGraphTests/callgraph.sol new file mode 100644 index 000000000000..21f0e7e5f622 --- /dev/null +++ b/test/libsolidity/functionDependencyGraphTests/callgraph.sol @@ -0,0 +1,60 @@ +// fallback---------->f----------+ +// | | +// | | +// | | +// | | +// v v +// g h +// +// +// add +// +// unreferenced + +pragma experimental solidity; + +type uint256 = __builtin("word"); + +instantiation uint256: + { + function add(x, y) -> uint256 { + let a = uint256.rep(x); + let b = uint256.rep(y); + assembly { + a := add(a,b) + } + return uint256.abs(a); + } +} + +function unreferenced(x:uint256) -> uint256 +{ + return x; +} + +function f(x:uint256) -> uint256 +{ + return g(h(x)); +} + +function g(x:uint256) -> uint256 +{ + return x; +} + +function h(x:uint256) -> uint256 +{ + return x; +} + +contract C { + fallback() external { + let a: uint256->uint256 = f; + } +} +// ---- +// (add) --> {} +// (unreferenced) --> {} +// (f) --> {g,h,} +// (g) --> {} +// (h) --> {} +// (fallback) --> {f,} diff --git a/test/libsolidity/functionDependencyGraphTests/callgraph_no_leaves.sol b/test/libsolidity/functionDependencyGraphTests/callgraph_no_leaves.sol new file mode 100644 index 000000000000..5fe1a75fed7e --- /dev/null +++ b/test/libsolidity/functionDependencyGraphTests/callgraph_no_leaves.sol @@ -0,0 +1,66 @@ +// a<------b +// | ^ +// | | +// | | +// | | +// | | +// +------>c------->d-------->e------->f +// ^ | +// | | +// | | +// | | +// | v +// h<-------g + +pragma experimental solidity; + +function a() +{ + c(); +} + +function b() +{ + a(); +} + +function c() +{ + b(); + d(); +} + +function d() +{ + e(); +} + +function e() +{ + f(); +} + +function f() +{ + g(); +} + +function g() +{ + h(); +} + +function h() +{ + e(); +} + +// ---- +// (a) --> {c,} +// (b) --> {a,} +// (c) --> {b,d,} +// (d) --> {e,} +// (e) --> {f,} +// (f) --> {g,} +// (g) --> {h,} +// (h) --> {e,} diff --git a/test/libsolidity/semanticTests/experimental/stub.sol b/test/libsolidity/semanticTests/experimental/stub.sol new file mode 100644 index 000000000000..6e7a750fe980 --- /dev/null +++ b/test/libsolidity/semanticTests/experimental/stub.sol @@ -0,0 +1,97 @@ +pragma experimental solidity; + +type word = __builtin("word"); +type bool = __builtin("bool"); +type integer = __builtin("integer"); + +type uint256 = word; + +instantiation uint256: + { + function add(x, y) -> uint256 { + let a = uint256.rep(x); + let b = uint256.rep(y); + assembly { + a := add(a,b) + } + return uint256.abs(a); + } +} + + +instantiation uint256: * { + function mul(x, y) -> uint256 { + let a = uint256.rep(x); + let b = uint256.rep(y); + assembly { + a := mul(a,b) + } + return uint256.abs(a); + } +} +instantiation word: * { + function mul(x, y) -> word { + let z: word; + assembly { + z := mul(x,y) + } + return z; + } +} + +instantiation word: Integer { + function fromInteger(x:integer) -> word { + //x + x; + } +} + +instantiation word: == { + function eq(x, y) -> bool { + assembly { + x := eq(x, y) + } + } +} + + +function f(x:uint256->uint256,y:uint256) -> uint256 +{ + return x(y); +} + +function g(x:uint256) -> uint256 +{ + return x; +} + +contract C { + fallback() external { + let arg; + assembly { + arg := calldataload(0) + } + let x : word; + if (bool.abs(arg)) { + assembly { + x := 0x10 + } + } + let w = uint256.abs(x); +// w = f(g, w); + w = w * w + w; + let y : word; + let z : (uint256,uint256); + assembly { y := 2 } + y = uint256.rep(w) * y; + assembly { + mstore(0, y) + return(0, 32) + } + } +} +// ==== +// EVMVersion: >=constantinople +// ==== +// compileViaYul: true +// ---- +// (): 0 -> 0 +// (): 1 -> 544 diff --git a/test/libsolidity/semanticTests/experimental/type_class.sol b/test/libsolidity/semanticTests/experimental/type_class.sol new file mode 100644 index 000000000000..69fa568dfd96 --- /dev/null +++ b/test/libsolidity/semanticTests/experimental/type_class.sol @@ -0,0 +1,67 @@ +pragma experimental solidity; + +type word = __builtin("word"); +type bool = __builtin("bool"); + +type Cat = word; +type Dog = word; + +class Self: Animal { + function new() -> Self; + function alive(self: Self) -> bool; +} + +instantiation Cat: Animal { + function new() -> Cat { + let c; + return c; + } + + function alive(self: Cat) -> bool { + // TODO: Boolean literals or operators not implemented. + let w; + assembly { + w := 1 + } + return bool.abs(w); + } +} + +instantiation Dog: Animal { + function new() -> Dog { + let d: Dog; + return d; + } + + function alive(self: Dog) -> bool { + let b: bool; + return b; + } +} + +contract C { + fallback() external { + let boolResult1: bool; + let boolResult2: bool; + + let c: Cat = Animal.new(); + boolResult1 = Animal.alive(c); + + let d: Dog = Animal.new(); + boolResult2 = Animal.alive(d); + + let wordResult1 = bool.rep(boolResult1); + let wordResult2 = bool.rep(boolResult2); + assembly { + mstore(0, wordResult1) + mstore(32, wordResult2) + return(0, 64) + } + } +} + +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// () -> 1, 0 diff --git a/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition.sol b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition.sol new file mode 100644 index 000000000000..fc4783ceba19 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition.sol @@ -0,0 +1,92 @@ +pragma experimental solidity; + +type void = __builtin("void"); + +type bool = __builtin("bool"); +type word = __builtin("word"); +type integer = __builtin("integer"); +type unit = __builtin("unit"); + +type fun(T, U) = __builtin("fun"); +type pair(T, U) = __builtin("pair"); + +contract C { + fallback() external { + let v: void; + + let b: bool; + bool.abs(bool.rep(b)); + + let w: word; + let i: integer; + let u: unit; + + let f: fun(word, bool); + b = f(w); + + let p: pair(bool, word); + pair.first(p); + pair.second(p); + } +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// UnimplementedFeatureError: No support for calling functions pointers yet. +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// Info 4164: (31-61): Inferred type: void +// Info 4164: (63-93): Inferred type: bool +// Info 4164: (94-124): Inferred type: word +// Info 4164: (125-161): Inferred type: integer +// Info 4164: (162-192): Inferred type: () +// Info 4164: (194-228): Inferred type: tfun(('u:type, 'v:type), 'u:type -> 'v:type) +// Info 4164: (202-208): Inferred type: ('s:type, 't:type) +// Info 4164: (203-204): Inferred type: 's:type +// Info 4164: (206-207): Inferred type: 't:type +// Info 4164: (229-265): Inferred type: tfun(('y:type, 'z:type), ('y:type, 'z:type)) +// Info 4164: (238-244): Inferred type: ('w:type, 'x:type) +// Info 4164: (239-240): Inferred type: 'w:type +// Info 4164: (242-243): Inferred type: 'x:type +// Info 4164: (284-584): Inferred type: () -> () +// Info 4164: (292-294): Inferred type: () +// Info 4164: (318-325): Inferred type: void +// Info 4164: (321-325): Inferred type: void +// Info 4164: (340-347): Inferred type: bool +// Info 4164: (343-347): Inferred type: bool +// Info 4164: (357-378): Inferred type: bool +// Info 4164: (357-365): Inferred type: word -> bool +// Info 4164: (357-361): Inferred type: bool +// Info 4164: (366-377): Inferred type: word +// Info 4164: (366-374): Inferred type: bool -> word +// Info 4164: (366-370): Inferred type: bool +// Info 4164: (375-376): Inferred type: bool +// Info 4164: (393-400): Inferred type: word +// Info 4164: (396-400): Inferred type: word +// Info 4164: (414-424): Inferred type: integer +// Info 4164: (417-424): Inferred type: integer +// Info 4164: (438-445): Inferred type: () +// Info 4164: (441-445): Inferred type: () +// Info 4164: (460-478): Inferred type: word -> bool +// Info 4164: (463-478): Inferred type: word -> bool +// Info 4164: (463-466): Inferred type: tfun((word, bool), word -> bool) +// Info 4164: (467-471): Inferred type: word +// Info 4164: (473-477): Inferred type: bool +// Info 4164: (488-496): Inferred type: bool +// Info 4164: (488-489): Inferred type: bool +// Info 4164: (492-496): Inferred type: bool +// Info 4164: (492-493): Inferred type: word -> bool +// Info 4164: (494-495): Inferred type: word +// Info 4164: (511-530): Inferred type: (bool, word) +// Info 4164: (514-530): Inferred type: (bool, word) +// Info 4164: (514-518): Inferred type: tfun((bool, word), (bool, word)) +// Info 4164: (519-523): Inferred type: bool +// Info 4164: (525-529): Inferred type: word +// Info 4164: (540-553): Inferred type: bool +// Info 4164: (540-550): Inferred type: (bool, word) -> bool +// Info 4164: (540-544): Inferred type: ('bl:type, 'bm:type) +// Info 4164: (551-552): Inferred type: (bool, word) +// Info 4164: (563-577): Inferred type: word +// Info 4164: (563-574): Inferred type: (bool, word) -> word +// Info 4164: (563-567): Inferred type: ('bq:type, 'br:type) +// Info 4164: (575-576): Inferred type: (bool, word) diff --git a/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_duplicate.sol b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_duplicate.sol new file mode 100644 index 000000000000..28f6c39fd754 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_duplicate.sol @@ -0,0 +1,17 @@ +pragma experimental solidity; + +type void1 = __builtin("void"); +type void2 = __builtin("void"); + +type word1 = __builtin("word"); +type word2 = __builtin("word"); + +type fun1(T, U) = __builtin("fun"); +type fun2(T, U) = __builtin("fun"); +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 9609: (63-94): Duplicate builtin type definition. +// TypeError 9609: (128-159): Duplicate builtin type definition. +// TypeError 9609: (197-232): Duplicate builtin type definition. diff --git a/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_unknown.sol b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_unknown.sol new file mode 100644 index 000000000000..90072f19838f --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition_unknown.sol @@ -0,0 +1,8 @@ +pragma experimental solidity; + +type someUnknownType = __builtin("someUnknownType"); +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 7758: (31-81): Expected the name of a built-in primitive type. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_solidity.sol b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity.sol similarity index 70% rename from test/libsolidity/syntaxTests/pragma/experimental_solidity.sol rename to test/libsolidity/syntaxTests/experimental/import/experimental_solidity.sol index 285c76ed3030..a84ac0e759ab 100644 --- a/test/libsolidity/syntaxTests/pragma/experimental_solidity.sol +++ b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity.sol @@ -1,6 +1,6 @@ pragma experimental solidity; // ==== // EVMVersion: >=constantinople +// compileViaYul: true // ---- // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. -// UnimplementedFeatureError 6547: Experimental Analysis is not implemented yet. diff --git a/test/libsolidity/syntaxTests/pragma/experimental_solidity_multisource_not_all_enable.sol b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_multisource_not_all_enable.sol similarity index 100% rename from test/libsolidity/syntaxTests/pragma/experimental_solidity_multisource_not_all_enable.sol rename to test/libsolidity/syntaxTests/experimental/import/experimental_solidity_multisource_not_all_enable.sol diff --git a/test/libsolidity/syntaxTests/pragma/experimental_solidity_out_of_order_1.sol b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_out_of_order_1.sol similarity index 100% rename from test/libsolidity/syntaxTests/pragma/experimental_solidity_out_of_order_1.sol rename to test/libsolidity/syntaxTests/experimental/import/experimental_solidity_out_of_order_1.sol diff --git a/test/libsolidity/syntaxTests/pragma/experimental_solidity_out_of_order_2.sol b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_out_of_order_2.sol similarity index 100% rename from test/libsolidity/syntaxTests/pragma/experimental_solidity_out_of_order_2.sol rename to test/libsolidity/syntaxTests/experimental/import/experimental_solidity_out_of_order_2.sol diff --git a/test/libsolidity/syntaxTests/pragma/experimental_solidity_wrong_evm_version.sol b/test/libsolidity/syntaxTests/experimental/import/experimental_solidity_wrong_evm_version.sol similarity index 100% rename from test/libsolidity/syntaxTests/pragma/experimental_solidity_wrong_evm_version.sol rename to test/libsolidity/syntaxTests/experimental/import/experimental_solidity_wrong_evm_version.sol diff --git a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_1.sol b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_1.sol similarity index 76% rename from test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_1.sol rename to test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_1.sol index f8799de42b65..5628a588f802 100644 --- a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_1.sol +++ b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_1.sol @@ -7,4 +7,5 @@ import std.stub; // ---- // Warning 2264: (std.stub:63-92): Experimental features are turned on. Do not use experimental features on live deployments. // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. -// UnimplementedFeatureError 6547: Experimental Analysis is not implemented yet. +// Info 4164: (std.stub:94-117): Inferred type: () -> () +// Info 4164: (std.stub:111-113): Inferred type: () diff --git a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_2.sol b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_2.sol similarity index 77% rename from test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_2.sol rename to test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_2.sol index f5bcf8efcdf5..bdc66e4ad393 100644 --- a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_2.sol +++ b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_2.sol @@ -7,4 +7,5 @@ import std.stub as stub; // ---- // Warning 2264: (std.stub:63-92): Experimental features are turned on. Do not use experimental features on live deployments. // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. -// UnimplementedFeatureError 6547: Experimental Analysis is not implemented yet. +// Info 4164: (std.stub:94-117): Inferred type: () -> () +// Info 4164: (std.stub:111-113): Inferred type: () diff --git a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_3.sol b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_3.sol similarity index 70% rename from test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_3.sol rename to test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_3.sol index 4b325ea4edf1..1bb340536c11 100644 --- a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_3.sol +++ b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_3.sol @@ -7,4 +7,6 @@ import { identity } from std.stub; // ---- // Warning 2264: (std.stub:63-92): Experimental features are turned on. Do not use experimental features on live deployments. // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. -// UnimplementedFeatureError 6547: Experimental Analysis is not implemented yet. +// Info 4164: (std.stub:94-117): Inferred type: () -> () +// Info 4164: (std.stub:111-113): Inferred type: () +// Info 4164: (40-48): Inferred type: () -> () diff --git a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_4.sol b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_4.sol similarity index 77% rename from test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_4.sol rename to test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_4.sol index 865e59b554f4..64a67fe6c96d 100644 --- a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_4.sol +++ b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_4.sol @@ -7,4 +7,5 @@ import * as stub from std.stub; // ---- // Warning 2264: (std.stub:63-92): Experimental features are turned on. Do not use experimental features on live deployments. // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. -// UnimplementedFeatureError 6547: Experimental Analysis is not implemented yet. +// Info 4164: (std.stub:94-117): Inferred type: () -> () +// Info 4164: (std.stub:111-113): Inferred type: () diff --git a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_without_pragma_1.sol b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_without_pragma_1.sol similarity index 100% rename from test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_without_pragma_1.sol rename to test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_without_pragma_1.sol diff --git a/test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_without_pragma_2.sol b/test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_without_pragma_2.sol similarity index 100% rename from test/libsolidity/syntaxTests/experimental/parsing_stdlib_import_without_pragma_2.sol rename to test/libsolidity/syntaxTests/experimental/import/parsing_stdlib_import_without_pragma_2.sol diff --git a/test/libsolidity/syntaxTests/experimental/inference/experimental_keywords.sol b/test/libsolidity/syntaxTests/experimental/inference/experimental_keywords.sol new file mode 100644 index 000000000000..b5920aed0759 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/experimental_keywords.sol @@ -0,0 +1,4 @@ +function f() pure { + uint word; word; + uint static_assert; static_assert; +} \ No newline at end of file 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 new file mode 100644 index 000000000000..fc75e85947eb --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/import_and_call_stdlib_function.sol @@ -0,0 +1,25 @@ +pragma experimental solidity; + +import { identity as id } from std.stub; + +contract C +{ + fallback() external + { + id(); + } +} + +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// Warning 2264: (std.stub:63-92): Experimental features are turned on. Do not use experimental features on live deployments. +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// Info 4164: (std.stub:94-117): Inferred type: () -> () +// Info 4164: (std.stub:111-113): Inferred type: () +// Info 4164: (40-48): Inferred type: () -> () +// Info 4164: (90-135): Inferred type: () -> () +// Info 4164: (98-100): Inferred type: () +// Info 4164: (124-128): Inferred type: () +// Info 4164: (124-126): Inferred type: () -> () diff --git a/test/libsolidity/syntaxTests/experimental/inference/invalid_type_referenced.sol b/test/libsolidity/syntaxTests/experimental/inference/invalid_type_referenced.sol new file mode 100644 index 000000000000..ac097bf3927f --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/invalid_type_referenced.sol @@ -0,0 +1,13 @@ +pragma experimental solidity; + +function f() {} + +class Self: C +{ + function g(self: Self, x: f); +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 2217: (94-95): Attempt to type identifier referring to unexpected node. 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 new file mode 100644 index 000000000000..e7af573aeed6 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/monomorphic_function_call_type_mismatch.sol @@ -0,0 +1,16 @@ +pragma experimental solidity; + +type T; +type U; + +function f(x: T, y: U) {} + +function run(a: U, b: T) { + f(a, b); +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 8456: (106-113): Cannot unify T and U. +// TypeError 8456: (106-113): Cannot unify U and T. diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol new file mode 100644 index 000000000000..eacc895202f3 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol @@ -0,0 +1,53 @@ +pragma experimental solidity; + +type T; +type U(A); + +function f(x, y: X, z: U(Y)) {} + +function run(a: T, b: U(T), c: U(U(T))) { + f(a, a, b); + f(b, b, c); +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// Info 4164: (31-38): Inferred type: T +// Info 4164: (39-49): Inferred type: tfun('u:type, U('u:type)) +// Info 4164: (45-48): Inferred type: 't:type +// Info 4164: (46-47): Inferred type: 't:type +// Info 4164: (51-82): Inferred type: ('x:type, 'y:type, U('ba:type)) -> () +// Info 4164: (61-79): Inferred type: ('x:type, 'y:type, U('ba:type)) +// Info 4164: (62-63): Inferred type: 'x:type +// Info 4164: (65-69): Inferred type: 'y:type +// Info 4164: (68-69): Inferred type: 'y:type +// Info 4164: (71-78): Inferred type: U('ba:type) +// Info 4164: (74-78): Inferred type: U('ba:type) +// Info 4164: (74-75): Inferred type: tfun('ba:type, U('ba:type)) +// Info 4164: (76-77): Inferred type: 'ba:type +// Info 4164: (84-159): Inferred type: (T, U(T), U(U(T))) -> () +// Info 4164: (96-123): Inferred type: (T, U(T), U(U(T))) +// Info 4164: (97-101): Inferred type: T +// Info 4164: (100-101): Inferred type: T +// Info 4164: (103-110): Inferred type: U(T) +// Info 4164: (106-110): Inferred type: U(T) +// Info 4164: (106-107): Inferred type: tfun(T, U(T)) +// Info 4164: (108-109): Inferred type: T +// Info 4164: (112-122): Inferred type: U(U(T)) +// Info 4164: (115-122): Inferred type: U(U(T)) +// Info 4164: (115-116): Inferred type: tfun(U(T), U(U(T))) +// Info 4164: (117-121): Inferred type: U(T) +// Info 4164: (117-118): Inferred type: tfun(T, U(T)) +// Info 4164: (119-120): Inferred type: T +// Info 4164: (130-140): Inferred type: () +// Info 4164: (130-131): Inferred type: (T, T, U(T)) -> () +// Info 4164: (132-133): Inferred type: T +// Info 4164: (135-136): Inferred type: T +// Info 4164: (138-139): Inferred type: U(T) +// Info 4164: (146-156): Inferred type: () +// Info 4164: (146-147): Inferred type: (U(T), U(T), U(U(T))) -> () +// Info 4164: (148-149): Inferred type: U(T) +// Info 4164: (151-152): Inferred type: U(T) +// Info 4164: (154-155): Inferred type: U(U(T)) 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 new file mode 100644 index 000000000000..1a43d0807ac2 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call_let_polymorphism.sol @@ -0,0 +1,19 @@ +pragma experimental solidity; + +type T; +type U; + +function f(x) {} + +function run(a: T, b: U) { + // NOTE: The type of f is polymorphic but the inferred type of g is not - this would be + // let-polymorphism, which we decided not to support. + let g = f; + g(a); + g(b); +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 8456: (272-276): Cannot unify T and U. 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 new file mode 100644 index 000000000000..74f102a88183 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call_type_mismatch.sol @@ -0,0 +1,16 @@ +pragma experimental solidity; + +type T(A); +type U; +type V; + +function f(x: T(U)) {} + +function run(a: T(V)) { + f(a); +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 8456: (111-115): Cannot unify U and V. diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol new file mode 100644 index 000000000000..b597a5b25295 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol @@ -0,0 +1,47 @@ +pragma experimental solidity; + +type T(P, Q, R); +type U; +type V; + +class Self: C {} +class Self: D {} + +function run() { + let x: T(U, X, Z: C); + let y: T(V, Y, Z: D); +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// Info 4164: (31-47): Inferred type: tfun(('ba:type, 'bb:type, 'bc:type), T('ba:type, 'bb:type, 'bc:type)) +// Info 4164: (37-46): Inferred type: ('x:type, 'y:type, 'z:type) +// Info 4164: (38-39): Inferred type: 'x:type +// Info 4164: (41-42): Inferred type: 'y:type +// Info 4164: (44-45): Inferred type: 'z:type +// Info 4164: (48-55): Inferred type: U +// Info 4164: (56-63): Inferred type: V +// Info 4164: (65-81): Inferred type: C +// Info 4164: (71-75): Inferred type: 'be:(type, C) +// Info 4164: (82-98): Inferred type: D +// Info 4164: (88-92): Inferred type: 'bg:(type, D) +// Info 4164: (100-170): Inferred type: () -> () +// Info 4164: (112-114): Inferred type: () +// Info 4164: (125-141): Inferred type: T(U, 'bm:type, 'bo:(type, C)) +// Info 4164: (128-141): Inferred type: T(U, 'bm:type, 'bo:(type, C)) +// Info 4164: (128-129): Inferred type: tfun((U, 'bm:type, 'bo:(type, C)), T(U, 'bm:type, 'bo:(type, C))) +// Info 4164: (130-131): Inferred type: U +// Info 4164: (133-134): Inferred type: 'bm:type +// Info 4164: (136-140): Inferred type: 'bo:(type, C) +// Info 4164: (136-137): Inferred type: 'bo:(type, C) +// Info 4164: (139-140): Inferred type: 'bo:(type, C) +// Info 4164: (151-167): Inferred type: T(V, 'bt:type, 'bv:(type, D)) +// Info 4164: (154-167): Inferred type: T(V, 'bt:type, 'bv:(type, D)) +// Info 4164: (154-155): Inferred type: tfun((V, 'bt:type, 'bv:(type, D)), T(V, 'bt:type, 'bv:(type, D))) +// Info 4164: (156-157): Inferred type: V +// Info 4164: (159-160): Inferred type: 'bt:type +// Info 4164: (162-166): Inferred type: 'bv:(type, D) +// Info 4164: (162-163): Inferred type: 'bv:(type, D) +// Info 4164: (165-166): Inferred type: 'bv:(type, D) 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 new file mode 100644 index 000000000000..89f3f5f1a8b9 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type_abs_and_rep.sol @@ -0,0 +1,69 @@ +pragma experimental solidity; + +type uint; +type string; + +type T(A); +type U(B) = T(B); + +function fun() { + let w: U(uint); + let v: T(uint); + U.rep(w); + U.abs(v); + + let s: U(string); + let t: T(string); + U.rep(s); + U.abs(t); +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// Info 4164: (31-41): Inferred type: uint +// Info 4164: (42-54): Inferred type: string +// Info 4164: (56-66): Inferred type: tfun('v:type, T('v:type)) +// Info 4164: (62-65): Inferred type: 'u:type +// Info 4164: (63-64): Inferred type: 'u:type +// Info 4164: (67-84): Inferred type: tfun('x:type, U('x:type)) +// Info 4164: (73-76): Inferred type: 'w:type +// Info 4164: (74-75): Inferred type: 'w:type +// Info 4164: (79-83): Inferred type: T('w:type) +// Info 4164: (79-80): Inferred type: tfun('w:type, T('w:type)) +// Info 4164: (81-82): Inferred type: 'w:type +// Info 4164: (86-245): Inferred type: () -> () +// Info 4164: (98-100): Inferred type: () +// Info 4164: (111-121): Inferred type: U(uint) +// Info 4164: (114-121): Inferred type: U(uint) +// Info 4164: (114-115): Inferred type: tfun(uint, U(uint)) +// Info 4164: (116-120): Inferred type: uint +// Info 4164: (131-141): Inferred type: T(uint) +// Info 4164: (134-141): Inferred type: T(uint) +// Info 4164: (134-135): Inferred type: tfun(uint, T(uint)) +// Info 4164: (136-140): Inferred type: uint +// Info 4164: (147-155): Inferred type: T('bi:type) +// Info 4164: (147-152): Inferred type: U(uint) -> T('bi:type) +// Info 4164: (147-148): Inferred type: U('bg:type) +// Info 4164: (153-154): Inferred type: U(uint) +// Info 4164: (161-169): Inferred type: U('bm:type) +// Info 4164: (161-166): Inferred type: T(uint) -> U('bm:type) +// Info 4164: (161-162): Inferred type: U('bk:type) +// Info 4164: (167-168): Inferred type: T(uint) +// Info 4164: (180-192): Inferred type: U(string) +// Info 4164: (183-192): Inferred type: U(string) +// Info 4164: (183-184): Inferred type: tfun(string, U(string)) +// Info 4164: (185-191): Inferred type: string +// Info 4164: (202-214): Inferred type: T(string) +// Info 4164: (205-214): Inferred type: T(string) +// Info 4164: (205-206): Inferred type: tfun(string, T(string)) +// Info 4164: (207-213): Inferred type: string +// Info 4164: (220-228): Inferred type: T('bu:type) +// Info 4164: (220-225): Inferred type: U(string) -> T('bu:type) +// Info 4164: (220-221): Inferred type: U('bs:type) +// Info 4164: (226-227): Inferred type: U(string) +// Info 4164: (234-242): Inferred type: U('by:type) +// Info 4164: (234-239): Inferred type: T(string) -> U('by:type) +// Info 4164: (234-235): Inferred type: U('bw:type) +// Info 4164: (240-241): Inferred type: T(string) 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 new file mode 100644 index 000000000000..25ab330f3b32 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type_instantiation_and_operators.sol @@ -0,0 +1,168 @@ +pragma experimental solidity; + +type bool = __builtin("bool"); + +type T(A); +type int; +type str; + +class Self: C { + function foo(a: Self, b: Self) -> Self; +} + +class Self: P1 {} +class Self: P2 {} +class Self: P3 {} +class Self: P4 {} + +instantiation int: P1 {} +instantiation int: P2 {} +instantiation int: P3 {} + +instantiation str: P1 {} +instantiation str: P2 {} +instantiation str: P4 {} + +instantiation T(A: P1): + { + function add(x: T(A), y: T(A)) -> T(A) {} +} + +instantiation T(A: P2): == { + function eq(x: T(A), y: T(A)) -> bool {} +} + +instantiation T(A: (P1, P2)): C { + function foo(x: T(A), y: T(A)) -> T(A) {} +} + +function fun(a: T(int: P3), b: T(str: P4)) { + a + a; + b + b; + + a == a; + b == b; + + C.foo(a, a); + C.foo(b, b); +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// Info 4164: (31-61): Inferred type: bool +// Info 4164: (63-73): Inferred type: tfun('z:type, T('z:type)) +// Info 4164: (69-72): Inferred type: 'y:type +// Info 4164: (70-71): Inferred type: 'y:type +// Info 4164: (74-83): Inferred type: int +// Info 4164: (84-93): Inferred type: str +// Info 4164: (95-156): Inferred type: C +// Info 4164: (101-105): Inferred type: 'bd:(type, C) +// Info 4164: (115-154): Inferred type: ('bd:(type, C), 'bd:(type, C)) -> 'bd:(type, C) +// Info 4164: (127-145): Inferred type: ('bd:(type, C), 'bd:(type, C)) +// Info 4164: (128-135): Inferred type: 'bd:(type, C) +// Info 4164: (131-135): Inferred type: 'bd:(type, C) +// Info 4164: (137-144): Inferred type: 'bd:(type, C) +// Info 4164: (140-144): Inferred type: 'bd:(type, C) +// Info 4164: (149-153): Inferred type: 'bd:(type, C) +// Info 4164: (158-175): Inferred type: P1 +// Info 4164: (164-168): Inferred type: 'bg:(type, P1) +// Info 4164: (176-193): Inferred type: P2 +// Info 4164: (182-186): Inferred type: 'bj:(type, P2) +// Info 4164: (194-211): Inferred type: P3 +// Info 4164: (200-204): Inferred type: 'bw:(type, P3) +// Info 4164: (212-229): Inferred type: P4 +// Info 4164: (218-222): Inferred type: 'by:(type, P4) +// Info 4164: (231-255): Inferred type: void +// Info 4164: (256-280): Inferred type: void +// Info 4164: (281-305): Inferred type: void +// Info 4164: (307-331): Inferred type: void +// Info 4164: (332-356): Inferred type: void +// Info 4164: (357-381): Inferred type: void +// Info 4164: (383-458): Inferred type: void +// Info 4164: (398-405): Inferred type: 'ca:(type, P1) +// Info 4164: (399-404): Inferred type: 'ca:(type, P1) +// Info 4164: (402-404): Inferred type: 'ca:(type, P1) +// Info 4164: (415-456): Inferred type: (T('ca:(type, P1)), T('ca:(type, P1))) -> T('ca:(type, P1)) +// Info 4164: (427-445): Inferred type: (T('ca:(type, P1)), T('ca:(type, P1))) +// Info 4164: (428-435): Inferred type: T('ca:(type, P1)) +// Info 4164: (431-435): Inferred type: T('ca:(type, P1)) +// Info 4164: (431-432): Inferred type: tfun('ca:(type, P1), T('ca:(type, P1))) +// Info 4164: (433-434): Inferred type: 'ca:(type, P1) +// Info 4164: (437-444): Inferred type: T('ca:(type, P1)) +// Info 4164: (440-444): Inferred type: T('ca:(type, P1)) +// Info 4164: (440-441): Inferred type: tfun('ca:(type, P1), T('ca:(type, P1))) +// Info 4164: (442-443): Inferred type: 'ca:(type, P1) +// Info 4164: (449-453): Inferred type: T('ca:(type, P1)) +// Info 4164: (449-450): Inferred type: tfun('ca:(type, P1), T('ca:(type, P1))) +// Info 4164: (451-452): Inferred type: 'ca:(type, P1) +// Info 4164: (460-535): Inferred type: void +// Info 4164: (475-482): Inferred type: 'ck:(type, P2) +// Info 4164: (476-481): Inferred type: 'ck:(type, P2) +// Info 4164: (479-481): Inferred type: 'ck:(type, P2) +// Info 4164: (493-533): Inferred type: (T('ck:(type, P2)), T('ck:(type, P2))) -> bool +// Info 4164: (504-522): Inferred type: (T('ck:(type, P2)), T('ck:(type, P2))) +// Info 4164: (505-512): Inferred type: T('ck:(type, P2)) +// Info 4164: (508-512): Inferred type: T('ck:(type, P2)) +// Info 4164: (508-509): Inferred type: tfun('ck:(type, P2), T('ck:(type, P2))) +// Info 4164: (510-511): Inferred type: 'ck:(type, P2) +// Info 4164: (514-521): Inferred type: T('ck:(type, P2)) +// Info 4164: (517-521): Inferred type: T('ck:(type, P2)) +// Info 4164: (517-518): Inferred type: tfun('ck:(type, P2), T('ck:(type, P2))) +// Info 4164: (519-520): Inferred type: 'ck:(type, P2) +// Info 4164: (526-530): Inferred type: bool +// Info 4164: (537-618): Inferred type: void +// Info 4164: (552-565): Inferred type: 'bm:(type, P1, P2) +// Info 4164: (553-564): Inferred type: 'bm:(type, P1, P2) +// Info 4164: (556-564): Inferred type: 'bm:(type, P1, P2) +// Info 4164: (557-559): Inferred type: 'bm:(type, P1, P2) +// Info 4164: (561-563): Inferred type: 'bm:(type, P1, P2) +// Info 4164: (575-616): Inferred type: (T('bm:(type, P1, P2)), T('bm:(type, P1, P2))) -> T('bm:(type, P1, P2)) +// Info 4164: (587-605): Inferred type: (T('bm:(type, P1, P2)), T('bm:(type, P1, P2))) +// Info 4164: (588-595): Inferred type: T('bm:(type, P1, P2)) +// Info 4164: (591-595): Inferred type: T('bm:(type, P1, P2)) +// Info 4164: (591-592): Inferred type: tfun('bm:(type, P1, P2), T('bm:(type, P1, P2))) +// Info 4164: (593-594): Inferred type: 'bm:(type, P1, P2) +// Info 4164: (597-604): Inferred type: T('bm:(type, P1, P2)) +// Info 4164: (600-604): Inferred type: T('bm:(type, P1, P2)) +// Info 4164: (600-601): Inferred type: tfun('bm:(type, P1, P2), T('bm:(type, P1, P2))) +// Info 4164: (602-603): Inferred type: 'bm:(type, P1, P2) +// Info 4164: (609-613): Inferred type: T('bm:(type, P1, P2)) +// Info 4164: (609-610): Inferred type: tfun('bm:(type, P1, P2), T('bm:(type, P1, P2))) +// Info 4164: (611-612): Inferred type: 'bm:(type, P1, P2) +// Info 4164: (620-748): Inferred type: (T(int), T(str)) -> () +// Info 4164: (632-662): Inferred type: (T(int), T(str)) +// Info 4164: (633-646): Inferred type: T(int) +// Info 4164: (636-646): Inferred type: T(int) +// Info 4164: (636-637): Inferred type: tfun(int, T(int)) +// Info 4164: (638-645): Inferred type: int +// Info 4164: (638-641): Inferred type: int +// Info 4164: (643-645): Inferred type: int +// Info 4164: (648-661): Inferred type: T(str) +// Info 4164: (651-661): Inferred type: T(str) +// Info 4164: (651-652): Inferred type: tfun(str, T(str)) +// Info 4164: (653-660): Inferred type: str +// Info 4164: (653-656): Inferred type: str +// Info 4164: (658-660): Inferred type: str +// Info 4164: (669-674): Inferred type: T(int) +// Info 4164: (669-670): Inferred type: T(int) +// Info 4164: (673-674): Inferred type: T(int) +// Info 4164: (680-685): Inferred type: T(str) +// Info 4164: (680-681): Inferred type: T(str) +// Info 4164: (684-685): Inferred type: T(str) +// Info 4164: (692-698): Inferred type: bool +// Info 4164: (692-693): Inferred type: T(int) +// Info 4164: (697-698): Inferred type: T(int) +// Info 4164: (704-710): Inferred type: bool +// Info 4164: (704-705): Inferred type: T(str) +// Info 4164: (709-710): Inferred type: T(str) +// Info 4164: (717-728): Inferred type: T(int) +// Info 4164: (717-722): Inferred type: (T(int), T(int)) -> T(int) +// Info 4164: (717-718): Inferred type: C +// Info 4164: (723-724): Inferred type: T(int) +// Info 4164: (726-727): Inferred type: T(int) +// Info 4164: (734-745): Inferred type: T(str) +// Info 4164: (734-739): Inferred type: (T(str), T(str)) -> T(str) +// Info 4164: (734-735): Inferred type: C +// Info 4164: (740-741): Inferred type: T(str) +// Info 4164: (743-744): Inferred type: T(str) diff --git a/test/stopAfterParseTests.sh b/test/stopAfterParseTests.sh index 7d74704c5e38..cb15603e2399 100755 --- a/test/stopAfterParseTests.sh +++ b/test/stopAfterParseTests.sh @@ -70,5 +70,5 @@ while read -r file; do echo "$file" exit 1 fi -done < <(find "${REPO_ROOT}/test" -iname "*.sol" -and -not -name "documentation.sol" -and -not -name "boost_filesystem_bug.sol") +done < <(find "${REPO_ROOT}/test" -iname "*.sol" -and -not -name "documentation.sol" -and -not -name "boost_filesystem_bug.sol" -not -path "*/experimental/*" -not -path "*/functionDependencyGraphTests/*") echo diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index a86e6382df8d..532a08d9fc55 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -34,6 +34,7 @@ add_executable(isoltest ../libsolidity/ABIJsonTest.cpp ../libsolidity/ASTJSONTest.cpp ../libsolidity/ASTPropertyTest.cpp + ../libsolidity/FunctionDependencyGraphTest.cpp ../libsolidity/SMTCheckerTest.cpp ../libyul/Common.cpp ../libyul/ControlFlowGraphTest.cpp From e6555d8080ba25f069021abb9bf79b0267c7b0e9 Mon Sep 17 00:00:00 2001 From: r0qs Date: Mon, 18 Dec 2023 14:47:26 -0300 Subject: [PATCH 041/189] Increase resource_class of prb-math external test --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index db505fc4a568..d69f3b38e030 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -738,6 +738,7 @@ defaults: project: prb-math binary_type: native image: cimg/rust:1.74.0-node + resource_class: medium - job_native_test_ext_elementfi: &job_native_test_ext_elementfi <<: *requires_b_ubu_static From 5fd52f32e7dfdbafbe5cc13b0e86d0cda8e3f088 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Tue, 26 Dec 2023 12:28:44 +0100 Subject: [PATCH 042/189] Switch to building GMP from an unofficial git mirror --- .../Dockerfile.ubuntu.clang.ossfuzz | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz b/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz index d5e6599ea71e..b6121f659d14 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz @@ -22,12 +22,13 @@ # (c) 2016-2021 solidity contributors. #------------------------------------------------------------------------------ FROM gcr.io/oss-fuzz-base/base-clang:latest as base -LABEL version="2" +LABEL version="3" ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update; \ apt-get -qqy install --no-install-recommends \ + automake libtool bison texinfo \ build-essential sudo \ software-properties-common \ ninja-build git wget \ @@ -108,18 +109,22 @@ RUN set -ex; \ # with sanitized version. Do not perform apt # remove because it removes mlton as well that # we need for building libabicoder + cd /usr/src/; \ + git clone --depth 1 --branch gmp-6.2.1 https://github.com/gmp-mirror/gmp-6.2 gmp/; \ + rm -r gmp/.git/; \ + test \ + "$(tar --create gmp/ --sort=name --mtime=1970-01-01Z --owner=0 --group=0 --numeric-owner | sha256sum)" = \ + "d606ff6a4ce98692f9920031e85ea8fcf4a65ce1426f6f0048b8794aefed174b -"; \ + # NOTE: This removes also libgmp.so, which git depends on rm -f /usr/lib/x86_64-linux-gnu/libgmp.*; \ rm -f /usr/include/x86_64-linux-gnu/gmp.h; \ - cd /usr/src; \ - wget -q 'https://gmplib.org/download/gmp/gmp-6.2.1.tar.xz' -O gmp.tar.xz; \ - test "$(sha256sum gmp.tar.xz)" = "fd4829912cddd12f84181c3451cc752be224643e87fac497b69edddadc49b4f2 gmp.tar.xz"; \ - tar -xf gmp.tar.xz; \ - cd gmp-6.2.1; \ - ./configure --prefix=/usr --enable-static=yes; \ + cd gmp/; \ + autoreconf -i; \ + ./configure --prefix=/usr --enable-static=yes --enable-maintainer-mode; \ make -j; \ + make check; \ make install; \ - rm -rf /usr/src/gmp-6.2.1; \ - rm -f /usr/src/gmp.tar.xz + rm -rf /usr/src/gmp/ # libabicoder RUN set -ex; \ From 5c79d5e49b067d138ca1707f148f26ddffc13395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 5 Jan 2024 13:18:36 +0100 Subject: [PATCH 043/189] CI: Update ossfuzz docker image hash --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d69f3b38e030..e2e10267a732 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,8 +21,8 @@ parameters: default: "solbuildpackpusher/solidity-buildpack-deps@sha256:538596bf55961197f8b5670d8a6742d9bcd502b6a1045ae9d372cdf35ce69d93" ubuntu-clang-ossfuzz-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu.clang.ossfuzz-2 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:a4fc3a41240c3bc58882d3f504e446c6931b547119012f5c45f79b0df91dbdd1" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu.clang.ossfuzz-3 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:46a6df64a21dc8914a8e4d43f50a1f56a942b01cbc1c30741ca28239e4ae081a" emscripten-docker-image: type: string # NOTE: Please remember to update the `build_emscripten.sh` whenever the hash of this image changes. From d5119f7247dc2def7b6ab0ddcbd01904776f6b3e Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Fri, 22 Dec 2023 09:49:39 +0100 Subject: [PATCH 044/189] Bump evmone to 11 in Docker images --- .../docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz | 4 ++-- scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 | 6 +++--- scripts/docker/buildpack-deps/Dockerfile.ubuntu2204 | 4 ++-- scripts/docker/buildpack-deps/Dockerfile.ubuntu2204.clang | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz b/scripts/docker/buildpack-deps/Dockerfile.ubuntu.clang.ossfuzz index b6121f659d14..fd98de73e07f 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-clang:latest as base -LABEL version="3" +LABEL version="4" ARG DEBIAN_FRONTEND=noninteractive @@ -94,7 +94,7 @@ RUN set -ex; \ # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.10.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.11.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 index dbd1990023e7..6ff8d1bd43ec 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:focal AS base -LABEL version="20" +LABEL version="21" ARG DEBIAN_FRONTEND=noninteractive @@ -47,8 +47,8 @@ FROM base AS libraries # EVMONE RUN set -ex; \ - wget -O /usr/src/evmone.tar.gz https://github.com/ethereum/evmone/releases/download/v0.10.0/evmone-0.10.0-linux-x86_64.tar.gz; \ - test "$(sha256sum /usr/src/evmone.tar.gz)" = "6052b6a4eba6e81bb1bccfa9fec2caf220a25f5ff5c32c544984883ed95e6552 /usr/src/evmone.tar.gz"; \ + wget -O /usr/src/evmone.tar.gz https://github.com/ethereum/evmone/releases/download/v0.11.0/evmone-0.11.0-linux-x86_64.tar.gz; \ + test "$(sha256sum /usr/src/evmone.tar.gz)" = "051dbe523da165658ced63619ea2c05925516aac3061951da96d3f0962560719 /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.ubuntu2204 b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2204 index d5cfae924994..6908811f925f 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2204 +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2204 @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:jammy AS base -LABEL version="5" +LABEL version="6" ARG DEBIAN_FRONTEND=noninteractive @@ -48,7 +48,7 @@ FROM base AS libraries # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.10.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.11.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2204.clang b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2204.clang index 3e23cd055f19..a14bdc113817 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2204.clang +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2204.clang @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:jammy AS base -LABEL version="4" +LABEL version="5" ARG DEBIAN_FRONTEND=noninteractive @@ -50,7 +50,7 @@ ENV CXX clang++ # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.10.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.11.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ From 567e8ccc446cd18af5f4f0b1b65cf2d2cc0de03f Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 20 Dec 2023 13:33:32 +0100 Subject: [PATCH 045/189] Bump evmc to v11.0.0 --- test/evmc/README.md | 9 ++++-- test/evmc/evmc.h | 46 +++++++++++++++++++++++++++++- test/evmc/evmc.hpp | 59 ++++++++++++++++++++++++++++++++++----- test/evmc/mocked_host.hpp | 38 +++++++++++++++++++++++-- 4 files changed, 140 insertions(+), 12 deletions(-) diff --git a/test/evmc/README.md b/test/evmc/README.md index cdddb3444083..9381b4f53eda 100644 --- a/test/evmc/README.md +++ b/test/evmc/README.md @@ -1,5 +1,10 @@ # EVMC -This is an import of [EVMC](https://github.com/ethereum/evmc) version [10.1.0](https://github.com/ethereum/evmc/releases/tag/v10.1.0). +This is an import of [EVMC](https://github.com/ethereum/evmc) version [11.0.0](https://github.com/ethereum/evmc/releases/tag/v11.0.0). -Important: The `MockedAccount.storage` is changed to a `map` from `unordered_map` as ordering is important for fuzzing. +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) + - 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) +- `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. diff --git a/test/evmc/evmc.h b/test/evmc/evmc.h index 60c3aeecc978..48b03e554556 100644 --- a/test/evmc/evmc.h +++ b/test/evmc/evmc.h @@ -44,7 +44,7 @@ enum * * @see @ref versioning */ - EVMC_ABI_VERSION = 10 + EVMC_ABI_VERSION = 11 }; @@ -202,6 +202,9 @@ struct evmc_tx_context evmc_uint256be block_prev_randao; /**< The block previous RANDAO (EIP-4399). */ evmc_uint256be chain_id; /**< The blockchain's ChainID. */ evmc_uint256be block_base_fee; /**< The block base fee per gas (EIP-1559, EIP-3198). */ + evmc_uint256be blob_base_fee; /**< The blob base fee (EIP-7516). */ + const evmc_bytes32* blob_hashes; /**< The array of blob hashes (EIP-4844). */ + size_t blob_hashes_count; /**< The number of blob hashes (EIP-4844). */ }; /** @@ -503,6 +506,22 @@ typedef evmc_bytes32 (*evmc_get_storage_fn)(struct evmc_host_context* context, const evmc_address* address, const evmc_bytes32* key); +/** + * Get transient storage callback function. + * + * This callback function is used by a VM to query + * the given account transient storage (EIP-1153) entry. + * + * @param context The Host execution context. + * @param address The address of the account. + * @param key The index of the account's transient storage entry. + * @return The transient storage value at the given storage key or null bytes + * if the account does not exist. + */ +typedef evmc_bytes32 (*evmc_get_transient_storage_fn)(struct evmc_host_context* context, + const evmc_address* address, + const evmc_bytes32* key); + /** * The effect of an attempt to modify a contract storage item. @@ -620,6 +639,25 @@ typedef enum evmc_storage_status (*evmc_set_storage_fn)(struct evmc_host_context const evmc_bytes32* key, const evmc_bytes32* value); +/** + * Set transient storage callback function. + * + * This callback function is used by a VM to update + * the given account's transient storage (EIP-1153) entry. + * The VM MUST make sure that the account exists. This requirement is only a formality because + * VM implementations only modify storage of the account of the current execution context + * (i.e. referenced by evmc_message::recipient). + * + * @param context The pointer to the Host execution context. + * @param address The address of the account. + * @param key The index of the transient storage entry. + * @param value The value to be stored. + */ +typedef void (*evmc_set_transient_storage_fn)(struct evmc_host_context* context, + const evmc_address* address, + const evmc_bytes32* key, + const evmc_bytes32* value); + /** * Get balance callback function. * @@ -825,6 +863,12 @@ struct evmc_host_interface /** Access storage callback function. */ evmc_access_storage_fn access_storage; + + /** Get transient storage callback function. */ + evmc_get_transient_storage_fn get_transient_storage; + + /** Set transient storage callback function. */ + evmc_set_transient_storage_fn set_transient_storage; }; diff --git a/test/evmc/evmc.hpp b/test/evmc/evmc.hpp index a29f802d27a7..91386c46f857 100644 --- a/test/evmc/evmc.hpp +++ b/test/evmc/evmc.hpp @@ -493,6 +493,15 @@ class HostInterface /// @copydoc evmc_host_interface::access_storage virtual evmc_access_status access_storage(const address& addr, const bytes32& key) noexcept = 0; + + /// @copydoc evmc_host_interface::get_transient_storage + virtual bytes32 get_transient_storage(const address& addr, + const bytes32& key) const noexcept = 0; + + /// @copydoc evmc_host_interface::set_transient_storage + virtual void set_transient_storage(const address& addr, + const bytes32& key, + const bytes32& value) noexcept = 0; }; @@ -591,6 +600,18 @@ class HostContext : public HostInterface { return host->access_storage(context, &address, &key); } + + bytes32 get_transient_storage(const address& address, const bytes32& key) const noexcept final + { + return host->get_transient_storage(context, &address, &key); + } + + void set_transient_storage(const address& address, + const bytes32& key, + const bytes32& value) noexcept final + { + host->set_transient_storage(context, &address, &key, &value); + } }; @@ -844,18 +865,42 @@ inline evmc_access_status access_storage(evmc_host_context* h, { return Host::from_context(h)->access_storage(*addr, *key); } + +inline evmc_bytes32 get_transient_storage(evmc_host_context* h, + const evmc_address* addr, + const evmc_bytes32* key) noexcept +{ + return Host::from_context(h)->get_transient_storage(*addr, *key); +} + +inline void set_transient_storage(evmc_host_context* h, + const evmc_address* addr, + const evmc_bytes32* key, + const evmc_bytes32* value) noexcept +{ + Host::from_context(h)->set_transient_storage(*addr, *key, *value); +} } // namespace internal inline const evmc_host_interface& Host::get_interface() noexcept { static constexpr evmc_host_interface interface = { - ::evmc::internal::account_exists, ::evmc::internal::get_storage, - ::evmc::internal::set_storage, ::evmc::internal::get_balance, - ::evmc::internal::get_code_size, ::evmc::internal::get_code_hash, - ::evmc::internal::copy_code, ::evmc::internal::selfdestruct, - ::evmc::internal::call, ::evmc::internal::get_tx_context, - ::evmc::internal::get_block_hash, ::evmc::internal::emit_log, - ::evmc::internal::access_account, ::evmc::internal::access_storage, + ::evmc::internal::account_exists, + ::evmc::internal::get_storage, + ::evmc::internal::set_storage, + ::evmc::internal::get_balance, + ::evmc::internal::get_code_size, + ::evmc::internal::get_code_hash, + ::evmc::internal::copy_code, + ::evmc::internal::selfdestruct, + ::evmc::internal::call, + ::evmc::internal::get_tx_context, + ::evmc::internal::get_block_hash, + ::evmc::internal::emit_log, + ::evmc::internal::access_account, + ::evmc::internal::access_storage, + ::evmc::internal::get_transient_storage, + ::evmc::internal::set_transient_storage, }; return interface; } diff --git a/test/evmc/mocked_host.hpp b/test/evmc/mocked_host.hpp index 173b793ac7a6..205a136b3800 100644 --- a/test/evmc/mocked_host.hpp +++ b/test/evmc/mocked_host.hpp @@ -63,6 +63,9 @@ struct MockedAccount /// The account storage map. std::map storage; + /// The account transient storage. + std::unordered_map transient_storage; + /// Helper method for setting balance by numeric type. void set_balance(uint64_t x) noexcept { @@ -467,8 +470,7 @@ class MockedHost : public Host /// /// @param addr The account address. /// @param key The account's storage key. - /// @return The ::EVMC_ACCESS_WARM if the storage key has been accessed - /// before, + /// @return The ::EVMC_ACCESS_WARM if the storage key has been accessed before, /// the ::EVMC_ACCESS_COLD otherwise. evmc_access_status access_storage(const address& addr, const bytes32& key) noexcept override { @@ -477,5 +479,37 @@ class MockedHost : public Host value.access_status = EVMC_ACCESS_WARM; return access_status; } + + /// Get account's transient storage. + /// + /// @param addr The account address. + /// @param key The account's transient storage key. + /// @return The transient storage value. Null value in case the account does not exist. + bytes32 get_transient_storage(const address& addr, const bytes32& key) const noexcept override + { + record_account_access(addr); + + const auto account_iter = accounts.find(addr); + if (account_iter == accounts.end()) + return {}; + + const auto storage_iter = account_iter->second.transient_storage.find(key); + if (storage_iter != account_iter->second.transient_storage.end()) + return storage_iter->second; + return {}; + } + + /// Set account's transient storage. + /// + /// @param addr The account address. + /// @param key The account's transient storage key. + /// @param value The value to be stored. + void set_transient_storage(const address& addr, + const bytes32& key, + const bytes32& value) noexcept override + { + record_account_access(addr); + accounts[addr].transient_storage[key] = value; + } }; } // namespace evmc From 8fb37d4bdff44f73ebb024bf31dac5f57ceb5f3f Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Fri, 22 Dec 2023 09:49:39 +0100 Subject: [PATCH 046/189] Bump evmone to 11 in CI --- .circleci/osx_install_dependencies.sh | 4 ++-- scripts/install_evmone.ps1 | 2 +- test/Common.h | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh index 61ac4a106610..3e3f3b1bbd80 100755 --- a/.circleci/osx_install_dependencies.sh +++ b/.circleci/osx_install_dependencies.sh @@ -76,10 +76,10 @@ then rm -r "$z3_dir" # evmone - evmone_version="0.10.0" + evmone_version="0.11.0" evmone_package="evmone-${evmone_version}-darwin-x86_64.tar.gz" wget "https://github.com/ethereum/evmone/releases/download/v${evmone_version}/${evmone_package}" - validate_checksum "$evmone_package" 1b7773779287d7908baca6b8d556a98800cbd7d6e5c910b55fa507642bc0a15c + validate_checksum "$evmone_package" 83ed20676681d9a31bd30cac399ab7c615ccab8adb8087cc2c7e9cd22b4d2efc tar xzpf "$evmone_package" -C /usr/local rm "$evmone_package" diff --git a/scripts/install_evmone.ps1 b/scripts/install_evmone.ps1 index 0bf2fa1de37d..acbacdafbd01 100644 --- a/scripts/install_evmone.ps1 +++ b/scripts/install_evmone.ps1 @@ -3,7 +3,7 @@ $ErrorActionPreference = "Stop" # Needed for Invoke-WebRequest to work via CI. $progressPreference = "silentlyContinue" -Invoke-WebRequest -URI "https://github.com/ethereum/evmone/releases/download/v0.10.0/evmone-0.10.0-windows-amd64.zip" -OutFile "evmone.zip" +Invoke-WebRequest -URI "https://github.com/ethereum/evmone/releases/download/v0.11.0/evmone-0.11.0-windows-amd64.zip" -OutFile "evmone.zip" tar -xf evmone.zip "bin/evmone.dll" mkdir deps mv bin/evmone.dll deps diff --git a/test/Common.h b/test/Common.h index d82ff9c6b71f..28ec4e219939 100644 --- a/test/Common.h +++ b/test/Common.h @@ -33,13 +33,13 @@ namespace solidity::test #ifdef _WIN32 static constexpr auto evmoneFilename = "evmone.dll"; -static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.10.0/evmone-0.10.0-windows-amd64.zip"; +static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.11.0/evmone-0.11.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.10.0/evmone-0.10.0-darwin-x86_64.tar.gz"; +static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.11.0/evmone-0.11.0-darwin-x86_64.tar.gz"; #else static constexpr auto evmoneFilename = "libevmone.so"; -static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.10.0/evmone-0.10.0-linux-x86_64.tar.gz"; +static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.11.0/evmone-0.11.0-linux-x86_64.tar.gz"; #endif struct ConfigException: public util::Exception {}; From 4e7bb4eaa42509870cfc21ff753d9cf10ccca6ab Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Sat, 23 Dec 2023 10:55:09 +0100 Subject: [PATCH 047/189] Bump evmc and evmone images to v11 in CI --- .circleci/config.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e2e10267a732..8a771e69596f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,20 +9,20 @@ version: 2.1 parameters: ubuntu-2004-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-20 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:7a1e1b01eda0d1e20704279672bcfd53dbbc481898ff960958a225dea76345bd" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-21 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:6f64c7b35aabddb416d28f621a3c4bbae768c257d6866f6a7f05f8225acc94f0" ubuntu-2204-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu2204-5 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:4df420b7ccd96f540a4300a4fae0fcac2f4d3f23ffff9e3777c1f2d7c37ef901" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu2204-6 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:6eee10420382588b274374c6a18cb2e5d4f8a9fef5cf440b3a1acfc32bf52837" ubuntu-2204-clang-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu2204.clang-4 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:538596bf55961197f8b5670d8a6742d9bcd502b6a1045ae9d372cdf35ce69d93" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu2204.clang-5 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:e991421ce9f44d6476b29588fc355da1c3b3fedcc424fd12d844bbe310ad7851" ubuntu-clang-ossfuzz-docker-image: type: string - # solbuildpackpusher/solidity-buildpack-deps:ubuntu.clang.ossfuzz-3 - default: "solbuildpackpusher/solidity-buildpack-deps@sha256:46a6df64a21dc8914a8e4d43f50a1f56a942b01cbc1c30741ca28239e4ae081a" + # solbuildpackpusher/solidity-buildpack-deps:ubuntu.clang.ossfuzz-4 + default: "solbuildpackpusher/solidity-buildpack-deps@sha256:b122ef9dca71a8f90b74f3ba13cda4453681506e4a4ff047e7bc2130c76b0d1e" emscripten-docker-image: type: string # NOTE: Please remember to update the `build_emscripten.sh` whenever the hash of this image changes. From 373bc2bb5ccbc4c7522ad86f87b25a610c7f752a Mon Sep 17 00:00:00 2001 From: r0qs Date: Tue, 9 Jan 2024 15:30:01 -0300 Subject: [PATCH 048/189] Fix chai to version 4.4.0 in Brink external test --- test/externalTests/brink.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/externalTests/brink.sh b/test/externalTests/brink.sh index 44ea1a511ac6..1c3ad2e19a13 100755 --- a/test/externalTests/brink.sh +++ b/test/externalTests/brink.sh @@ -73,7 +73,9 @@ function brink_test yarn add hardhat-gas-reporter # TODO: Remove when https://github.com/brinktrade/brink-core/issues/48 is fixed. - yarn add chai + # TODO: Chai is ESM-only since version 5.x (see: https://github.com/chaijs/chai/issues/1561#issuecomment-1871134261), + # thus, we should stick to version 4.x until Brink and other dependencies also migrate to ESM. + yarn add chai@4.4.0 replace_version_pragmas From fbbac9c1969c054071d9d6456e5151cb4ae53c19 Mon Sep 17 00:00:00 2001 From: Sukey <35202440+sukey2008@users.noreply.github.com> Date: Wed, 10 Jan 2024 10:39:25 +0100 Subject: [PATCH 049/189] Fix several minor typo (#14629) * fix minor typo * fix minor typo * Change to suggested correct wording Update installing-solidity.rst --- docs/installing-solidity.rst | 2 +- docs/path-resolution.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index abafd0ad3810..2f048fe3c104 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -73,7 +73,7 @@ Docker ====== Docker images of Solidity builds are available using the ``solc`` image from the ``ethereum`` organization. -Use the ``stable`` tag for the latest released version, and ``nightly`` for potentially unstable changes in the develop branch. +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. For example, the command below pulls the stable version of the ``solc`` image (if you do not have it already), diff --git a/docs/path-resolution.rst b/docs/path-resolution.rst index 666d9b8dc9ca..ff4930bc9ded 100644 --- a/docs/path-resolution.rst +++ b/docs/path-resolution.rst @@ -41,7 +41,7 @@ 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, but one can be provided by the user. -This mechanism can be used to obtain source code from locations other then the local filesystem +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). For example the `Remix IDE `_ provides a versatile callback that lets you `import files from HTTP, IPFS and Swarm URLs or refer directly to packages in NPM registry From 47fc4f948d851e0357cf294ac80bd4a2c6a3ff12 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Fri, 12 Jan 2024 07:31:54 +0100 Subject: [PATCH 050/189] Yul fuzzer: Add Shanghai EVM version --- test/tools/ossfuzz/protoToYul.cpp | 2 ++ test/tools/ossfuzz/yulProto.proto | 1 + 2 files changed, 3 insertions(+) diff --git a/test/tools/ossfuzz/protoToYul.cpp b/test/tools/ossfuzz/protoToYul.cpp index 80768578934b..9b487daf5dba 100644 --- a/test/tools/ossfuzz/protoToYul.cpp +++ b/test/tools/ossfuzz/protoToYul.cpp @@ -116,6 +116,8 @@ EVMVersion ProtoConverter::evmVersionMapping(Program_Version const& _ver) return EVMVersion::london(); case Program::PARIS: return EVMVersion::paris(); + case Program::SHANGHAI: + return EVMVersion::shanghai(); } } diff --git a/test/tools/ossfuzz/yulProto.proto b/test/tools/ossfuzz/yulProto.proto index 76d85068d3ad..e78080058c7b 100644 --- a/test/tools/ossfuzz/yulProto.proto +++ b/test/tools/ossfuzz/yulProto.proto @@ -398,6 +398,7 @@ message Program { BERLIN = 7; LONDON = 8; PARIS = 9; + SHANGHAI = 10; } oneof program_oneof { Block block = 1; From bf59ff8bdf4fe104e558cb697abfc8f3b33daaba Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Mon, 25 Dec 2023 12:50:16 +0100 Subject: [PATCH 051/189] Introduce block.blobbasefee in Solidity and blobbasefee in Yul --- Changelog.md | 2 ++ docs/cheatsheet.rst | 3 ++- docs/grammar/SolidityLexer.g4 | 2 +- docs/units-and-global-variables.rst | 3 ++- docs/using-the-compiler.rst | 2 ++ docs/yul.rst | 6 ++++-- libevmasm/Instruction.cpp | 2 ++ libevmasm/Instruction.h | 1 + libevmasm/SemanticInformation.cpp | 1 + libevmasm/SimplificationRule.h | 1 + liblangutil/EVMVersion.cpp | 2 ++ liblangutil/EVMVersion.h | 1 + libsolidity/analysis/TypeChecker.cpp | 6 ++++++ libsolidity/ast/Types.cpp | 3 ++- libsolidity/codegen/ExpressionCompiler.cpp | 2 ++ .../codegen/ir/IRGeneratorForStatements.cpp | 2 ++ libyul/AsmAnalysis.cpp | 2 ++ libyul/backends/evm/EVMDialect.cpp | 13 +++++++++++- scripts/error_codes.py | 2 +- scripts/test_antlr_grammar.sh | 2 ++ test/EVMHost.cpp | 2 ++ test/libsolidity/ViewPureChecker.cpp | 20 ++++++++++++++++++ .../blobbasefee_shanghai_function.sol | 21 +++++++++++++++++++ .../semanticTests/state/block_blobbasefee.sol | 17 +++++++++++++++ .../blobbasefee_reserved_cancun.sol | 12 +++++++++++ .../types/magic_block_basefee_error.sol | 9 +++++++- .../types/magic_block_blobbasefee_cancun.sol | 13 ++++++++++++ .../types/magic_block_blobbasefee_error.sol | 16 ++++++++++++++ .../viewPureChecker/blobbasefee_not_pure.sol | 13 ++++++++++++ ...embly_instructions_allowed_view_cancun.sol | 10 +++++++++ ...ly_instructions_disallowed_pure_cancun.sol | 11 ++++++++++ .../yulInterpreterTests/blobbasefee.yul | 10 +++++++++ .../blobbasefee_identifier_pre_cancun.yul | 6 ++++++ ...asefee_reserved_identifier_post_cancun.yul | 7 +++++++ .../EVMInstructionInterpreter.cpp | 2 ++ test/tools/yulInterpreter/Interpreter.h | 2 ++ 36 files changed, 220 insertions(+), 9 deletions(-) create mode 100644 test/libsolidity/semanticTests/inlineAssembly/blobbasefee_shanghai_function.sol create mode 100644 test/libsolidity/semanticTests/state/block_blobbasefee.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/blobbasefee_reserved_cancun.sol create mode 100644 test/libsolidity/syntaxTests/types/magic_block_blobbasefee_cancun.sol create mode 100644 test/libsolidity/syntaxTests/types/magic_block_blobbasefee_error.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/blobbasefee_not_pure.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_cancun.sol create mode 100644 test/libyul/yulInterpreterTests/blobbasefee.yul create mode 100644 test/libyul/yulSyntaxTests/blobbasefee_identifier_pre_cancun.yul create mode 100644 test/libyul/yulSyntaxTests/blobbasefee_reserved_identifier_post_cancun.yul diff --git a/Changelog.md b/Changelog.md index f42e948ebbce..bfd04e7f7796 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,8 @@ ### 0.8.24 (unreleased) Language Features: + * Introduce global ``block.blobbasefee`` for retrieving the blob base fee of the current block. + * Yul: Introduce builtin ``blobbasefee()`` for retrieving the blob base fee of the current block. Compiler Features: diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index 2f0c1a251f4d..e6b632dae4b6 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -56,7 +56,7 @@ Members of ``address`` returns ``false`` on failure - ``
.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure -.. index:: blockhash, block, block;basefree, block;chainid, block;coinbase, block;difficulty, block;gaslimit, block;number, block;prevrandao, block;timestamp +.. index:: blockhash, 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 Block and Transaction Properties @@ -64,6 +64,7 @@ Block and Transaction Properties - ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks - ``block.basefee`` (``uint``): current block's base fee (`EIP-3198 `_ and `EIP-1559 `_) +- ``block.blobbasefee`` (``uint``): current block's blob base fee (`EIP-7516 `_ and `EIP-4844 `_) - ``block.chainid`` (``uint``): current chain id - ``block.coinbase`` (``address payable``): current block miner's address - ``block.difficulty`` (``uint``): current block difficulty (``EVM < Paris``). For other EVM versions it behaves as a deprecated alias for ``block.prevrandao`` that will be removed in the next breaking release diff --git a/docs/grammar/SolidityLexer.g4 b/docs/grammar/SolidityLexer.g4 index 0452f9fa0bb6..b46af906164b 100644 --- a/docs/grammar/SolidityLexer.g4 +++ b/docs/grammar/SolidityLexer.g4 @@ -305,7 +305,7 @@ YulEVMBuiltin: | 'delegatecall' | 'staticcall' | 'return' | 'revert' | 'selfdestruct' | 'invalid' | 'log0' | 'log1' | 'log2' | 'log3' | 'log4' | 'chainid' | 'origin' | 'gasprice' | 'blockhash' | 'coinbase' | 'timestamp' | 'number' | 'difficulty' | 'prevrandao' - | 'gaslimit' | 'basefee'; + | 'gaslimit' | 'basefee' | 'blobbasefee'; YulLBrace: '{' -> pushMode(YulMode); YulRBrace: '}' -> popMode; diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index d2256884eb48..f95c9c5290d8 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -67,7 +67,7 @@ There are special variables and functions which always exist in the global namespace and are mainly used to provide information about the blockchain or are general-use utility functions. -.. index:: abi, block, coinbase, difficulty, prevrandao, encode, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, gas price, origin +.. index:: abi, block, coinbase, difficulty, prevrandao, encode, number, block;number, timestamp, block;timestamp, block;basefee, block;blobbasefee, msg, data, gas, sender, value, gas price, origin Block and Transaction Properties @@ -75,6 +75,7 @@ Block and Transaction Properties - ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block when ``blocknumber`` is one of the 256 most recent blocks; otherwise returns zero - ``block.basefee`` (``uint``): current block's base fee (`EIP-3198 `_ and `EIP-1559 `_) +- ``block.blobbasefee`` (``uint``): current block's blob base fee (`EIP-7516 `_ and `EIP-4844 `_) - ``block.chainid`` (``uint``): current chain id - ``block.coinbase`` (``address payable``): current block miner's address - ``block.difficulty`` (``uint``): current block difficulty (``EVM < Paris``). For other EVM versions it behaves as a deprecated alias for ``block.prevrandao`` (`EIP-4399 `_ ) diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 0adcbf4132bb..60fbd0884a89 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -176,6 +176,8 @@ at each version. Backward compatibility is not guaranteed between each version. - Introduces ``prevrandao()`` and ``block.prevrandao``, and changes the semantics of the now deprecated ``block.difficulty``, disallowing ``difficulty()`` in inline assembly (see `EIP-4399 `_). - ``shanghai`` (**default**) - Smaller code size and gas savings due to the introduction of ``push0`` (see `EIP-3855 `_). +- ``cancun`` + - The block's blob base fee (`EIP-7516 `_ and `EIP-4844 `_) can be accessed via the global ``block.blobbasefee`` or ``blobbasefee()`` in inline assembly. .. index:: ! standard JSON, ! --standard-json .. _compiler-api: diff --git a/docs/yul.rst b/docs/yul.rst index 0e50d137761c..fd792999a993 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`` and ``P`` are present since Frontier, -Homestead, Byzantium, Constantinople, Istanbul, London or Paris respectively. +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. In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to but not including position ``b`` and ``storage[p]`` signifies the storage contents at slot ``p``. @@ -919,6 +919,8 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a +-------------------------+-----+---+-----------------------------------------------------------------+ | basefee() | | L | current block's base fee (EIP-3198 and EIP-1559) | +-------------------------+-----+---+-----------------------------------------------------------------+ +| blobbasefee() | | N | current block's blob base fee (EIP-7516 and EIP-4844) | ++-------------------------+-----+---+-----------------------------------------------------------------+ | origin() | | F | transaction sender | +-------------------------+-----+---+-----------------------------------------------------------------+ | gasprice() | | F | gas price of the transaction | diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index 174d12b5e355..23d2667318d9 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -81,6 +81,7 @@ std::map const solidity::evmasm::c_instructions = { "CHAINID", Instruction::CHAINID }, { "SELFBALANCE", Instruction::SELFBALANCE }, { "BASEFEE", Instruction::BASEFEE }, + { "BLOBBASEFEE", Instruction::BLOBBASEFEE }, { "POP", Instruction::POP }, { "MLOAD", Instruction::MLOAD }, { "MSTORE", Instruction::MSTORE }, @@ -230,6 +231,7 @@ static std::map const c_instructionInfo = { Instruction::CHAINID, { "CHAINID", 0, 0, 1, false, Tier::Base } }, { Instruction::SELFBALANCE, { "SELFBALANCE", 0, 0, 1, false, Tier::Low } }, { Instruction::BASEFEE, { "BASEFEE", 0, 0, 1, false, Tier::Base } }, + { Instruction::BLOBBASEFEE, { "BLOBBASEFEE", 0, 0, 1, false, Tier::Base } }, { Instruction::POP, { "POP", 0, 1, 0, false, Tier::Base } }, { Instruction::MLOAD, { "MLOAD", 0, 1, 1, true, Tier::VeryLow } }, { Instruction::MSTORE, { "MSTORE", 0, 2, 0, true, Tier::VeryLow } }, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index 2fddb11d80ea..cf4f3df1440f 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -89,6 +89,7 @@ enum class Instruction: uint8_t CHAINID, ///< get the config's chainid param SELFBALANCE, ///< get balance of the current account BASEFEE, ///< get the block's basefee + BLOBBASEFEE = 0x4a, ///< get the block's blob basefee POP = 0x50, ///< remove item from stack MLOAD, ///< load word from memory diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 1971d9d45bb8..fcd948e8b656 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -473,6 +473,7 @@ bool SemanticInformation::invalidInPureFunctions(Instruction _instruction) case Instruction::CALLVALUE: case Instruction::CHAINID: case Instruction::BASEFEE: + case Instruction::BLOBBASEFEE: case Instruction::GAS: case Instruction::GASPRICE: case Instruction::EXTCODESIZE: diff --git a/libevmasm/SimplificationRule.h b/libevmasm/SimplificationRule.h index e970068fbd7e..50b69d69abc0 100644 --- a/libevmasm/SimplificationRule.h +++ b/libevmasm/SimplificationRule.h @@ -127,6 +127,7 @@ struct EVMBuiltins static auto constexpr CHAINID = PatternGenerator{}; static auto constexpr SELFBALANCE = PatternGenerator{}; static auto constexpr BASEFEE = PatternGenerator{}; + static auto constexpr BLOBBASEFEE = PatternGenerator{}; static auto constexpr POP = PatternGenerator{}; static auto constexpr MLOAD = PatternGenerator{}; static auto constexpr MSTORE = PatternGenerator{}; diff --git a/liblangutil/EVMVersion.cpp b/liblangutil/EVMVersion.cpp index 24ef0673d8b9..78a98d2669cc 100644 --- a/liblangutil/EVMVersion.cpp +++ b/liblangutil/EVMVersion.cpp @@ -49,6 +49,8 @@ bool EVMVersion::hasOpcode(Instruction _opcode) const return hasSelfBalance(); case Instruction::BASEFEE: return hasBaseFee(); + case Instruction::BLOBBASEFEE: + return hasBlobBaseFee(); default: return true; } diff --git a/liblangutil/EVMVersion.h b/liblangutil/EVMVersion.h index b68e9546a1aa..02e4e34f3839 100644 --- a/liblangutil/EVMVersion.h +++ b/liblangutil/EVMVersion.h @@ -101,6 +101,7 @@ class EVMVersion: bool hasChainID() const { return *this >= istanbul(); } bool hasSelfBalance() const { return *this >= istanbul(); } bool hasBaseFee() const { return *this >= london(); } + bool hasBlobBaseFee() const { return *this >= cancun(); } bool hasPrevRandao() const { return *this >= paris(); } bool hasPush0() const { return *this >= shanghai(); } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 5c28d3cd2a78..11812878a5da 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -3409,6 +3409,12 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) _memberAccess.location(), "\"basefee\" is not supported by the VM version." ); + else if (memberName == "blobbasefee" && !m_evmVersion.hasBlobBaseFee()) + m_errorReporter.typeError( + 1006_error, + _memberAccess.location(), + "\"blobbasefee\" is not supported by the VM version." + ); else if (memberName == "prevrandao" && !m_evmVersion.hasPrevRandao()) m_errorReporter.warning( 9432_error, diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 7b3e42143804..77538f485b1c 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -4116,7 +4116,8 @@ MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const {"number", TypeProvider::uint256()}, {"gaslimit", TypeProvider::uint256()}, {"chainid", TypeProvider::uint256()}, - {"basefee", TypeProvider::uint256()} + {"basefee", TypeProvider::uint256()}, + {"blobbasefee", TypeProvider::uint256()} }); case Kind::Message: return MemberList::MemberMap({ diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 517f49c35331..c96d8bdea641 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1886,6 +1886,8 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) m_context << Instruction::CHAINID; else if (member == "basefee") m_context << Instruction::BASEFEE; + else if (member == "blobbasefee") + m_context << Instruction::BLOBBASEFEE; else if (member == "data") m_context << u256(0) << Instruction::CALLDATASIZE; else if (member == "sig") diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index a5196366fd30..deed7f80c18c 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -1895,6 +1895,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) define(_memberAccess) << "chainid()\n"; else if (member == "basefee") define(_memberAccess) << "basefee()\n"; + else if (member == "blobbasefee") + define(_memberAccess) << "blobbasefee()\n"; else if (member == "data") { IRVariable var(_memberAccess); diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index ed4187540e76..5fc5c55c8501 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -727,6 +727,8 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio errorForVM(7721_error, "only available for Istanbul-compatible"); else if (_instr == evmasm::Instruction::BASEFEE && !m_evmVersion.hasBaseFee()) errorForVM(5430_error, "only available for London-compatible"); + else if (_instr == evmasm::Instruction::BLOBBASEFEE && !m_evmVersion.hasBlobBaseFee()) + errorForVM(6679_error, "only available for Cancun-compatible"); else if (_instr == evmasm::Instruction::PC) m_errorReporter.error( 2450_error, diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 47130fd6c46d..12ffb0f649dd 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -120,6 +120,13 @@ std::set createReservedIdentifiers(langutil::EVMVersion _evmVersion) return _instr == evmasm::Instruction::BASEFEE && _evmVersion < langutil::EVMVersion::london(); }; + // TODO remove this in 0.9.0. We allow creating functions or identifiers in Yul with the name + // blobbasefee for VMs before cancun. + auto blobBaseFeeException = [&](evmasm::Instruction _instr) -> bool + { + return _instr == evmasm::Instruction::BLOBBASEFEE && _evmVersion < langutil::EVMVersion::cancun(); + }; + // TODO remove this in 0.9.0. We allow creating functions or identifiers in Yul with the name // prevrandao for VMs before paris. auto prevRandaoException = [&](std::string const& _instrName) -> bool @@ -132,7 +139,11 @@ std::set createReservedIdentifiers(langutil::EVMVersion _evmVersion) for (auto const& instr: evmasm::c_instructions) { std::string name = toLower(instr.first); - if (!baseFeeException(instr.second) && !prevRandaoException(name)) + if ( + !baseFeeException(instr.second) && + !blobBaseFeeException(instr.second) && + !prevRandaoException(name) + ) reserved.emplace(name); } reserved += std::vector{ diff --git a/scripts/error_codes.py b/scripts/error_codes.py index 4aff6b85e80d..d4e9e5486665 100755 --- a/scripts/error_codes.py +++ b/scripts/error_codes.py @@ -202,7 +202,7 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): "4591", # "There are more than 256 warnings. Ignoring the rest." # Due to 3805, the warning lists look different for different compiler builds. "1834", # Unimplemented feature error, as we do not test it anymore via cmdLineTests - "5430", # basefee being used in inline assembly for EVMVersion < london + "6679", # blobbasefee being used in inline assembly for EVMVersion < cancun "1180", # SMTChecker, covered by CL tests "2339", # SMTChecker, covered by CL tests "2961", # SMTChecker, covered by CL tests diff --git a/scripts/test_antlr_grammar.sh b/scripts/test_antlr_grammar.sh index 4dde11bf7dac..290a044eb847 100755 --- a/scripts/test_antlr_grammar.sh +++ b/scripts/test_antlr_grammar.sh @@ -121,6 +121,8 @@ done < <( grep -v -E 'revertStatement/non_called.sol' | # Skipping a test with "let basefee := ..." grep -v -E 'inlineAssembly/basefee_berlin_function.sol' | + # Skipping a test with "let blobbasefee := ..." + grep -v -E 'inlineAssembly/blobbasefee_shanghai_function.sol' | # Skipping tests with "let prevrandao := ..." grep -v -E 'inlineAssembly/prevrandao_allowed_function_pre_paris.sol' | grep -v -E 'inlineAssembly/prevrandao_disallowed_function_post_paris.sol' | diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 2bc1da96525e..813d28fdbf78 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -136,6 +136,8 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm): tx_context.chain_id = evmc::uint256be{1}; // The minimum value of basefee tx_context.block_base_fee = evmc::bytes32{7}; + // The minimum value of blobbasefee + tx_context.blob_base_fee = evmc::bytes32{1}; // Reserve space for recording calls. if (!recorded_calls.capacity()) diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index 03b2b421b4eb..a26511a0c944 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -92,6 +92,26 @@ BOOST_AUTO_TEST_CASE(environment_access) TypeError, "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\"" ); + + std::string baseFeeContract = "contract C { function f() view public { block.basefee; } }"; + if (!solidity::test::CommonOptions::get().evmVersion().hasBaseFee()) + CHECK_ERROR( + baseFeeContract, + TypeError, + "\"basefee\" is not supported by the VM version." + ); + else + CHECK_SUCCESS_NO_WARNINGS(baseFeeContract); + + std::string blobBaseFeeContract = "contract C { function f() view public { block.blobbasefee; } }"; + if (!solidity::test::CommonOptions::get().evmVersion().hasBlobBaseFee()) + CHECK_ERROR( + blobBaseFeeContract, + TypeError, + "\"blobbasefee\" is not supported by the VM version." + ); + else + CHECK_SUCCESS_NO_WARNINGS(blobBaseFeeContract); } BOOST_AUTO_TEST_CASE(address_staticcall) diff --git a/test/libsolidity/semanticTests/inlineAssembly/blobbasefee_shanghai_function.sol b/test/libsolidity/semanticTests/inlineAssembly/blobbasefee_shanghai_function.sol new file mode 100644 index 000000000000..d06457133e36 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/blobbasefee_shanghai_function.sol @@ -0,0 +1,21 @@ +contract C { + function f() public view returns (uint ret) { + assembly { + let blobbasefee := 999 + ret := blobbasefee + } + } + function g() public pure returns (uint ret) { + assembly { + function blobbasefee() -> r { + r := 1000 + } + ret := blobbasefee() + } + } +} +// ==== +// EVMVersion: <=shanghai +// ---- +// f() -> 999 +// g() -> 1000 diff --git a/test/libsolidity/semanticTests/state/block_blobbasefee.sol b/test/libsolidity/semanticTests/state/block_blobbasefee.sol new file mode 100644 index 000000000000..23da779e40f2 --- /dev/null +++ b/test/libsolidity/semanticTests/state/block_blobbasefee.sol @@ -0,0 +1,17 @@ +contract C { + function f() public view returns (uint) { + return block.blobbasefee; + } + function g() public view returns (uint ret) { + assembly { + ret := blobbasefee() + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// f() -> 1 +// g() -> 1 +// f() -> 1 +// g() -> 1 diff --git a/test/libsolidity/syntaxTests/inlineAssembly/blobbasefee_reserved_cancun.sol b/test/libsolidity/syntaxTests/inlineAssembly/blobbasefee_reserved_cancun.sol new file mode 100644 index 000000000000..0bab7e36c74e --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/blobbasefee_reserved_cancun.sol @@ -0,0 +1,12 @@ +contract C { + function f() public view returns (uint ret) { + assembly { + let blobbasefee := sload(0) + ret := blobbasefee + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// ParserError 5568: (98-109): Cannot use builtin function name "blobbasefee" as identifier name. diff --git a/test/libsolidity/syntaxTests/types/magic_block_basefee_error.sol b/test/libsolidity/syntaxTests/types/magic_block_basefee_error.sol index b723d0aeeb2b..3e1650ad88b9 100644 --- a/test/libsolidity/syntaxTests/types/magic_block_basefee_error.sol +++ b/test/libsolidity/syntaxTests/types/magic_block_basefee_error.sol @@ -2,8 +2,15 @@ contract C { function f() public view returns (uint) { return block.basefee; } + function g() public view returns (uint ret) { + assembly { + ret := basefee() + } + } } // ==== -// EVMVersion: <=berlin +// EVMVersion: =berlin // ---- // TypeError 5921: (74-87): "basefee" is not supported by the VM version. +// TypeError 5430: (183-190): The "basefee" instruction is only available for London-compatible VMs (you are currently compiling for "berlin"). +// DeclarationError 8678: (176-192): Variable count for assignment to "ret" does not match number of values (1 vs. 0) diff --git a/test/libsolidity/syntaxTests/types/magic_block_blobbasefee_cancun.sol b/test/libsolidity/syntaxTests/types/magic_block_blobbasefee_cancun.sol new file mode 100644 index 000000000000..a639abda71ee --- /dev/null +++ b/test/libsolidity/syntaxTests/types/magic_block_blobbasefee_cancun.sol @@ -0,0 +1,13 @@ +contract C { + function f() public view returns (uint) { + return block.blobbasefee; + } + function g() public view returns (uint ret) { + assembly { + ret := blobbasefee() + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- diff --git a/test/libsolidity/syntaxTests/types/magic_block_blobbasefee_error.sol b/test/libsolidity/syntaxTests/types/magic_block_blobbasefee_error.sol new file mode 100644 index 000000000000..11a5bd991ae4 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/magic_block_blobbasefee_error.sol @@ -0,0 +1,16 @@ +contract C { + function f() public view returns (uint) { + return block.blobbasefee; + } + function g() public view returns (uint ret) { + assembly { + ret := blobbasefee() + } + } +} +// ==== +// EVMVersion: =shanghai +// ---- +// TypeError 1006: (74-91): "blobbasefee" is not supported by the VM version. +// DeclarationError 4619: (187-198): Function "blobbasefee" not found. +// DeclarationError 8678: (180-200): Variable count for assignment to "ret" does not match number of values (1 vs. 0) diff --git a/test/libsolidity/syntaxTests/viewPureChecker/blobbasefee_not_pure.sol b/test/libsolidity/syntaxTests/viewPureChecker/blobbasefee_not_pure.sol new file mode 100644 index 000000000000..3bace4285c32 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/blobbasefee_not_pure.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + assembly { pop(blobbasefee()) } + } + function g() public pure returns (uint) { + return block.blobbasefee; + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// TypeError 2527: (67-80): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError 2527: (151-168): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol new file mode 100644 index 000000000000..37380b965df2 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol @@ -0,0 +1,10 @@ +contract C { + function f() public view { + assembly { + pop(blobbasefee()) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_cancun.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_cancun.sol new file mode 100644 index 000000000000..5e2265f16274 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_cancun.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + assembly { + pop(blobbasefee()) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// TypeError 2527: (79-92): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libyul/yulInterpreterTests/blobbasefee.yul b/test/libyul/yulInterpreterTests/blobbasefee.yul new file mode 100644 index 000000000000..d863b1d8265f --- /dev/null +++ b/test/libyul/yulInterpreterTests/blobbasefee.yul @@ -0,0 +1,10 @@ +{ + sstore(0, blobbasefee()) +} +// ==== +// EVMVersion: >=cancun +// ---- +// Trace: +// Memory dump: +// Storage dump: +// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000001 diff --git a/test/libyul/yulSyntaxTests/blobbasefee_identifier_pre_cancun.yul b/test/libyul/yulSyntaxTests/blobbasefee_identifier_pre_cancun.yul new file mode 100644 index 000000000000..42c1f2c0a680 --- /dev/null +++ b/test/libyul/yulSyntaxTests/blobbasefee_identifier_pre_cancun.yul @@ -0,0 +1,6 @@ +{ + function blobbasefee() {} +} +// ==== +// EVMVersion: <=shanghai +// ---- diff --git a/test/libyul/yulSyntaxTests/blobbasefee_reserved_identifier_post_cancun.yul b/test/libyul/yulSyntaxTests/blobbasefee_reserved_identifier_post_cancun.yul new file mode 100644 index 000000000000..61fa7d7e427a --- /dev/null +++ b/test/libyul/yulSyntaxTests/blobbasefee_reserved_identifier_post_cancun.yul @@ -0,0 +1,7 @@ +{ + function blobbasefee() {} +} +// ==== +// EVMVersion: >=cancun +// ---- +// ParserError 5568: (15-26): Cannot use builtin function name "blobbasefee" as identifier name. diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index 79dbc554a292..015924a3b61b 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -233,6 +233,8 @@ u256 EVMInstructionInterpreter::eval( return m_state.chainid; case Instruction::BASEFEE: return m_state.basefee; + case Instruction::BLOBBASEFEE: + return m_state.blobbasefee; case Instruction::EXTCODESIZE: return u256(keccak256(h256(arg[0]))) & 0xffffff; case Instruction::EXTCODEHASH: diff --git a/test/tools/yulInterpreter/Interpreter.h b/test/tools/yulInterpreter/Interpreter.h index 26a8b2701215..66082b5b30f6 100644 --- a/test/tools/yulInterpreter/Interpreter.h +++ b/test/tools/yulInterpreter/Interpreter.h @@ -99,6 +99,8 @@ struct InterpreterState u256 chainid = 0x01; /// The minimum value of basefee: 7 wei. u256 basefee = 0x07; + /// The minimum value of blobbasefee: 1 wei. + u256 blobbasefee = 0x01; /// Log of changes / effects. Sholud be structured data in the future. std::vector trace; /// This is actually an input parameter that more or less limits the runtime. From b8d0077ac2337db7e3799c40dbfa8d0ee7e8a1db Mon Sep 17 00:00:00 2001 From: GoodDaisy <90915921+GoodDaisy@users.noreply.github.com> Date: Tue, 9 Jan 2024 23:46:32 +0800 Subject: [PATCH 052/189] Fix typos --- libsolidity/codegen/ArrayUtils.cpp | 2 +- libsolidity/codegen/ArrayUtils.h | 2 +- .../semanticTests/externalContracts/_prbmath/PRBMathCommon.sol | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index afcc38fe8a11..e73ddfbae819 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -524,7 +524,7 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord { // memory_end_offset - start is the actual length (we want to compute the ceil of). // memory_offset - start is its next multiple of 32, but it might be off by 32. - // so we compute: memory_end_offset += (memory_offset - memory_end_offest) & 31 + // so we compute: memory_end_offset += (memory_offset - memory_end_offset) & 31 m_context << Instruction::DUP3 << Instruction::SWAP1 << Instruction::SUB; m_context << u256(31) << Instruction::AND; m_context << Instruction::DUP3 << Instruction::ADD; diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h index 7a47efad7b0b..326084cb63a8 100644 --- a/libsolidity/codegen/ArrayUtils.h +++ b/libsolidity/codegen/ArrayUtils.h @@ -51,7 +51,7 @@ class ArrayUtils /// place as required by the ABI encoding). Use CompilerUtils::convertType if you want real /// memory copies of nested arrays. /// Stack pre: memory_offset source_item - /// Stack post: memory_offest + length(padded) + /// Stack post: memory_offset + length(padded) void copyArrayToMemory(ArrayType const& _sourceType, bool _padToWordBoundaries = true) const; /// Clears the given dynamic or static array. /// Stack pre: storage_ref storage_byte_offset diff --git a/test/libsolidity/semanticTests/externalContracts/_prbmath/PRBMathCommon.sol b/test/libsolidity/semanticTests/externalContracts/_prbmath/PRBMathCommon.sol index 9ec351b6c4a0..a3b49f8d3c3c 100644 --- a/test/libsolidity/semanticTests/externalContracts/_prbmath/PRBMathCommon.sol +++ b/test/libsolidity/semanticTests/externalContracts/_prbmath/PRBMathCommon.sol @@ -255,7 +255,7 @@ library PRBMathCommon { /// /// Caveats: /// - The body is purposely left uncommented; see the NatSpec comments in "PRBMathCommon.mulDiv" to understand how this works. - /// - It is assumed that the result can never be type(uint256).max when x and y solve the following two queations: + /// - It is assumed that the result can never be type(uint256).max when x and y solve the following two equations: /// 1. x * y = type(uint256).max * SCALE /// 2. (x * y) % SCALE >= SCALE / 2 /// From 269951ee69e91cf2556f5057cc621053b9631fad Mon Sep 17 00:00:00 2001 From: r0qs Date: Thu, 21 Dec 2023 19:19:01 -0300 Subject: [PATCH 053/189] Add blobhash opcode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kamil Śliwak --- Changelog.md | 2 +- docs/grammar/SolidityLexer.g4 | 4 ++-- docs/yul.rst | 2 ++ libevmasm/Instruction.cpp | 2 ++ libevmasm/Instruction.h | 1 + libevmasm/SemanticInformation.cpp | 1 + libevmasm/SimplificationRule.h | 1 + liblangutil/EVMVersion.cpp | 2 ++ liblangutil/EVMVersion.h | 1 + libyul/AsmAnalysis.cpp | 7 +++++++ libyul/backends/evm/EVMDialect.cpp | 12 +++++++++-- scripts/test_antlr_grammar.sh | 2 ++ test/EVMHost.cpp | 7 +++++++ .../semanticTests/inlineAssembly/blobhash.sol | 11 ++++++++++ .../blobhash_index_exceeding_blob_count.sol | 14 +++++++++++++ .../inlineAssembly/blobhash_pre_cancun.sol | 21 +++++++++++++++++++ .../syntaxTests/inlineAssembly/blobhash.sol | 10 +++++++++ .../inlineAssembly/blobhash_pre_cancun.sol | 12 +++++++++++ .../blobhash_reserved_cancun.sol | 14 +++++++++++++ .../viewPureChecker/blobhash_not_pure.sol | 9 ++++++++ ...embly_instructions_allowed_view_cancun.sol | 1 + ...ly_instructions_disallowed_pure_cancun.sol | 4 +++- test/libyul/yulInterpreterTests/blobhash.yul | 13 ++++++++++++ test/libyul/yulSyntaxTests/blobhash.yul | 15 +++++++++++++ .../yulSyntaxTests/blobhash_pre_cancun.yul | 13 ++++++++++++ .../EVMInstructionInterpreter.cpp | 16 ++++++++++++++ .../EVMInstructionInterpreter.h | 4 ++++ test/tools/yulInterpreter/Interpreter.h | 5 +++++ 28 files changed, 200 insertions(+), 6 deletions(-) create mode 100644 test/libsolidity/semanticTests/inlineAssembly/blobhash.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/blobhash_index_exceeding_blob_count.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/blobhash_pre_cancun.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/blobhash.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/blobhash_pre_cancun.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/blobhash_reserved_cancun.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/blobhash_not_pure.sol create mode 100644 test/libyul/yulInterpreterTests/blobhash.yul create mode 100644 test/libyul/yulSyntaxTests/blobhash.yul create mode 100644 test/libyul/yulSyntaxTests/blobhash_pre_cancun.yul diff --git a/Changelog.md b/Changelog.md index bfd04e7f7796..741e64f3e2d9 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,7 +3,7 @@ Language Features: * Introduce global ``block.blobbasefee`` for retrieving the blob base fee of the current block. * Yul: Introduce builtin ``blobbasefee()`` for retrieving the blob base fee of the current block. - + * Yul: Introduce builtin ``blobhash()`` for retrieving versioned hashes of blobs associated with the transaction. Compiler Features: * EVM: Support for the EVM Version "Cancun". diff --git a/docs/grammar/SolidityLexer.g4 b/docs/grammar/SolidityLexer.g4 index b46af906164b..b553c3b79c17 100644 --- a/docs/grammar/SolidityLexer.g4 +++ b/docs/grammar/SolidityLexer.g4 @@ -304,8 +304,8 @@ YulEVMBuiltin: | 'returndatacopy' | 'extcodehash' | 'create' | 'create2' | 'call' | 'callcode' | 'delegatecall' | 'staticcall' | 'return' | 'revert' | 'selfdestruct' | 'invalid' | 'log0' | 'log1' | 'log2' | 'log3' | 'log4' | 'chainid' | 'origin' | 'gasprice' - | 'blockhash' | 'coinbase' | 'timestamp' | 'number' | 'difficulty' | 'prevrandao' - | 'gaslimit' | 'basefee' | 'blobbasefee'; + | 'blockhash' | 'blobhash' | 'coinbase' | 'timestamp' | 'number' | 'difficulty' + | 'prevrandao' | 'gaslimit' | 'basefee' | 'blobbasefee'; YulLBrace: '{' -> pushMode(YulMode); YulRBrace: '}' -> popMode; diff --git a/docs/yul.rst b/docs/yul.rst index fd792999a993..2f0dc19dc8b9 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -927,6 +927,8 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a +-------------------------+-----+---+-----------------------------------------------------------------+ | blockhash(b) | | F | hash of block nr b - only for last 256 blocks excluding current | +-------------------------+-----+---+-----------------------------------------------------------------+ +| blobhash(i) | | N | versioned hash of transaction's i-th blob | ++-------------------------+-----+---+-----------------------------------------------------------------+ | coinbase() | | F | current mining beneficiary | +-------------------------+-----+---+-----------------------------------------------------------------+ | timestamp() | | F | timestamp of the current block in seconds since the epoch | diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index 23d2667318d9..611b17647e97 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -72,6 +72,7 @@ std::map const solidity::evmasm::c_instructions = { "RETURNDATACOPY", Instruction::RETURNDATACOPY }, { "EXTCODEHASH", Instruction::EXTCODEHASH }, { "BLOCKHASH", Instruction::BLOCKHASH }, + { "BLOBHASH", Instruction::BLOBHASH }, { "COINBASE", Instruction::COINBASE }, { "TIMESTAMP", Instruction::TIMESTAMP }, { "NUMBER", Instruction::NUMBER }, @@ -223,6 +224,7 @@ static std::map const c_instructionInfo = { Instruction::RETURNDATACOPY, {"RETURNDATACOPY", 0, 3, 0, true, Tier::VeryLow } }, { Instruction::EXTCODEHASH, { "EXTCODEHASH", 0, 1, 1, false, Tier::Balance } }, { Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, Tier::Ext } }, + { Instruction::BLOBHASH, { "BLOBHASH", 0, 1, 1, false, Tier::VeryLow } }, { Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::Base } }, { Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, false, Tier::Base } }, { Instruction::NUMBER, { "NUMBER", 0, 0, 1, false, Tier::Base } }, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index cf4f3df1440f..f05dcb9f63f8 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -89,6 +89,7 @@ enum class Instruction: uint8_t CHAINID, ///< get the config's chainid param SELFBALANCE, ///< get balance of the current account BASEFEE, ///< get the block's basefee + BLOBHASH = 0x49, ///< get a versioned hash of one of the blobs associated with the transaction BLOBBASEFEE = 0x4a, ///< get the block's blob basefee POP = 0x50, ///< remove item from stack diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index fcd948e8b656..346dcb0d4d33 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -480,6 +480,7 @@ bool SemanticInformation::invalidInPureFunctions(Instruction _instruction) case Instruction::EXTCODECOPY: case Instruction::EXTCODEHASH: case Instruction::BLOCKHASH: + case Instruction::BLOBHASH: case Instruction::COINBASE: case Instruction::TIMESTAMP: case Instruction::NUMBER: diff --git a/libevmasm/SimplificationRule.h b/libevmasm/SimplificationRule.h index 50b69d69abc0..b6d906bc1439 100644 --- a/libevmasm/SimplificationRule.h +++ b/libevmasm/SimplificationRule.h @@ -119,6 +119,7 @@ struct EVMBuiltins static auto constexpr RETURNDATACOPY = PatternGenerator{}; static auto constexpr EXTCODEHASH = PatternGenerator{}; static auto constexpr BLOCKHASH = PatternGenerator{}; + static auto constexpr BLOBHASH = PatternGenerator{}; static auto constexpr COINBASE = PatternGenerator{}; static auto constexpr TIMESTAMP = PatternGenerator{}; static auto constexpr NUMBER = PatternGenerator{}; diff --git a/liblangutil/EVMVersion.cpp b/liblangutil/EVMVersion.cpp index 78a98d2669cc..6fe2bbb1f4f1 100644 --- a/liblangutil/EVMVersion.cpp +++ b/liblangutil/EVMVersion.cpp @@ -49,6 +49,8 @@ bool EVMVersion::hasOpcode(Instruction _opcode) const return hasSelfBalance(); case Instruction::BASEFEE: return hasBaseFee(); + case Instruction::BLOBHASH: + return hasBlobHash(); case Instruction::BLOBBASEFEE: return hasBlobBaseFee(); default: diff --git a/liblangutil/EVMVersion.h b/liblangutil/EVMVersion.h index 02e4e34f3839..e59c5d3aa12c 100644 --- a/liblangutil/EVMVersion.h +++ b/liblangutil/EVMVersion.h @@ -104,6 +104,7 @@ class EVMVersion: bool hasBlobBaseFee() const { return *this >= cancun(); } bool hasPrevRandao() const { return *this >= paris(); } bool hasPush0() const { return *this >= shanghai(); } + bool hasBlobHash() const { return *this >= cancun(); } bool hasOpcode(evmasm::Instruction _opcode) const; diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 5fc5c55c8501..834a7549a502 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -670,6 +670,7 @@ void AsmAnalyzer::expectType(YulString _expectedType, YulString _givenType, Sour bool AsmAnalyzer::validateInstructions(std::string const& _instructionIdentifier, langutil::SourceLocation const& _location) { + // NOTE: This function uses the default EVM version instead of the currently selected one. auto const builtin = EVMDialect::strictAssemblyForEVM(EVMVersion{}).builtin(YulString(_instructionIdentifier)); if (builtin && builtin->instruction.has_value()) return validateInstructions(builtin->instruction.value(), _location); @@ -705,6 +706,9 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio ); }; + // The errors below are meant to be issued when processing an undeclared identifier matching a builtin name + // present on the default EVM version but not on the currently selected one, + // since the other `validateInstructions()` overload uses the default EVM version. if (_instr == evmasm::Instruction::RETURNDATACOPY && !m_evmVersion.supportsReturndata()) errorForVM(7756_error, "only available for Byzantium-compatible"); else if (_instr == evmasm::Instruction::RETURNDATASIZE && !m_evmVersion.supportsReturndata()) @@ -729,6 +733,9 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio errorForVM(5430_error, "only available for London-compatible"); else if (_instr == evmasm::Instruction::BLOBBASEFEE && !m_evmVersion.hasBlobBaseFee()) errorForVM(6679_error, "only available for Cancun-compatible"); + else if (_instr == evmasm::Instruction::BLOBHASH && !m_evmVersion.hasBlobHash()) + // TODO: Change this assertion to an error, similar to the ones above, when Cancun becomes the default EVM version. + yulAssert(false); else if (_instr == evmasm::Instruction::PC) m_errorReporter.error( 2450_error, diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 12ffb0f649dd..6b34f60c74aa 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -135,14 +135,22 @@ std::set createReservedIdentifiers(langutil::EVMVersion _evmVersion) return _instrName == "prevrandao" && _evmVersion < langutil::EVMVersion::paris(); }; + // TODO remove this in 0.9.0. We allow creating functions or identifiers in Yul with the name + // blobhash for VMs before cancun. + auto blobHashException = [&](evmasm::Instruction _instr) -> bool + { + return _instr == evmasm::Instruction::BLOBHASH && _evmVersion < langutil::EVMVersion::cancun(); + }; + std::set reserved; for (auto const& instr: evmasm::c_instructions) { std::string name = toLower(instr.first); if ( !baseFeeException(instr.second) && - !blobBaseFeeException(instr.second) && - !prevRandaoException(name) + !prevRandaoException(name) && + !blobHashException(instr.second) && + !blobBaseFeeException(instr.second) ) reserved.emplace(name); } diff --git a/scripts/test_antlr_grammar.sh b/scripts/test_antlr_grammar.sh index 290a044eb847..6ed57f2db9f2 100755 --- a/scripts/test_antlr_grammar.sh +++ b/scripts/test_antlr_grammar.sh @@ -126,6 +126,8 @@ done < <( # Skipping tests with "let prevrandao := ..." grep -v -E 'inlineAssembly/prevrandao_allowed_function_pre_paris.sol' | grep -v -E 'inlineAssembly/prevrandao_disallowed_function_post_paris.sol' | + # Skipping a test with "let blobhash := ..." + grep -v -E 'inlineAssembly/blobhash_pre_cancun.sol' | # Skipping license error, unrelated to the grammar grep -v -E 'license/license_double5.sol' | grep -v -E 'license/license_hidden_unicode.sol' | diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 813d28fdbf78..84327a5b977f 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -139,6 +139,13 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm): // The minimum value of blobbasefee tx_context.blob_base_fee = evmc::bytes32{1}; + static evmc_bytes32 const blob_hashes_array[] = { + 0x0100000000000000000000000000000000000000000000000000000000000001_bytes32, + 0x0100000000000000000000000000000000000000000000000000000000000002_bytes32 + }; + tx_context.blob_hashes = blob_hashes_array; + tx_context.blob_hashes_count = sizeof(blob_hashes_array) / sizeof(blob_hashes_array[0]); + // Reserve space for recording calls. if (!recorded_calls.capacity()) recorded_calls.reserve(max_recorded_calls); diff --git a/test/libsolidity/semanticTests/inlineAssembly/blobhash.sol b/test/libsolidity/semanticTests/inlineAssembly/blobhash.sol new file mode 100644 index 000000000000..3beac1d26fdf --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/blobhash.sol @@ -0,0 +1,11 @@ +contract C { + function f() public view returns (bytes32 ret) { + assembly { + ret := blobhash(0) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// f() -> 0x0100000000000000000000000000000000000000000000000000000000000001 diff --git a/test/libsolidity/semanticTests/inlineAssembly/blobhash_index_exceeding_blob_count.sol b/test/libsolidity/semanticTests/inlineAssembly/blobhash_index_exceeding_blob_count.sol new file mode 100644 index 000000000000..7063978c5c1b --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/blobhash_index_exceeding_blob_count.sol @@ -0,0 +1,14 @@ +contract C { + function f() public view returns (bytes32 ret) { + assembly { + // EIP-4844 specifies that if `index < len(tx.blob_versioned_hashes)`, `blobhash(index)` should return 0. + // Thus, as we injected only two blob hashes in the transaction context in EVMHost, + // the return value of the function below MUST be zero. + ret := blobhash(2) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// f() -> 0x00 diff --git a/test/libsolidity/semanticTests/inlineAssembly/blobhash_pre_cancun.sol b/test/libsolidity/semanticTests/inlineAssembly/blobhash_pre_cancun.sol new file mode 100644 index 000000000000..8972958e1f80 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/blobhash_pre_cancun.sol @@ -0,0 +1,21 @@ +contract C { + function f() public pure returns (uint ret) { + assembly { + let blobhash := 1 + ret := blobhash + } + } + function g() public pure returns (uint ret) { + assembly { + function blobhash() -> r { + r := 1000 + } + ret := blobhash() + } + } +} +// ==== +// EVMVersion: <=shanghai +// ---- +// f() -> 1 +// g() -> 1000 diff --git a/test/libsolidity/syntaxTests/inlineAssembly/blobhash.sol b/test/libsolidity/syntaxTests/inlineAssembly/blobhash.sol new file mode 100644 index 000000000000..06eefc8aa960 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/blobhash.sol @@ -0,0 +1,10 @@ +contract C { + function f() public view returns (bytes32 ret) { + assembly { + ret := blobhash(1) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- diff --git a/test/libsolidity/syntaxTests/inlineAssembly/blobhash_pre_cancun.sol b/test/libsolidity/syntaxTests/inlineAssembly/blobhash_pre_cancun.sol new file mode 100644 index 000000000000..c0284b330e25 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/blobhash_pre_cancun.sol @@ -0,0 +1,12 @@ +contract C { + function f() pure external returns (bytes32 ret) { + assembly { + ret := blobhash() + } + } +} +// ==== +// EVMVersion: <=shanghai +// ---- +// DeclarationError 4619: (106-114): Function "blobhash" not found. +// DeclarationError 8678: (99-116): Variable count for assignment to "ret" does not match number of values (1 vs. 0) diff --git a/test/libsolidity/syntaxTests/inlineAssembly/blobhash_reserved_cancun.sol b/test/libsolidity/syntaxTests/inlineAssembly/blobhash_reserved_cancun.sol new file mode 100644 index 000000000000..39bd68e14e80 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/blobhash_reserved_cancun.sol @@ -0,0 +1,14 @@ +contract C { + function f() public pure returns (uint ret) { + assembly { + function blobhash() -> r { + r := 1000 + } + ret := blobhash() + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// ParserError 5568: (103-111): Cannot use builtin function name "blobhash" as identifier name. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/blobhash_not_pure.sol b/test/libsolidity/syntaxTests/viewPureChecker/blobhash_not_pure.sol new file mode 100644 index 000000000000..2e38ddedb8f8 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/blobhash_not_pure.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + assembly { pop(blobhash(0)) } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// TypeError 2527: (67-78): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol index 37380b965df2..f68c313286de 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol @@ -1,6 +1,7 @@ contract C { function f() public view { assembly { + pop(blobhash(0)) pop(blobbasefee()) } } diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_cancun.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_cancun.sol index 5e2265f16274..b3d429f0698f 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_cancun.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure_cancun.sol @@ -1,6 +1,7 @@ contract C { function f() public pure { assembly { + pop(blobhash(0)) pop(blobbasefee()) } } @@ -8,4 +9,5 @@ contract C { // ==== // EVMVersion: >=cancun // ---- -// TypeError 2527: (79-92): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError 2527: (79-90): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError 2527: (108-121): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libyul/yulInterpreterTests/blobhash.yul b/test/libyul/yulInterpreterTests/blobhash.yul new file mode 100644 index 000000000000..2d5ff80f03c8 --- /dev/null +++ b/test/libyul/yulInterpreterTests/blobhash.yul @@ -0,0 +1,13 @@ +{ + sstore(0, blobhash(0)) + sstore(1, blobhash(1)) + sstore(2, blobhash(2)) // should store 0 since EVMHost has only two blob hashes injected in the block the transaction is being executed. +} +// ==== +// EVMVersion: >=cancun +// ---- +// Trace: +// Memory dump: +// Storage dump: +// 0000000000000000000000000000000000000000000000000000000000000000: 014916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5 +// 0000000000000000000000000000000000000000000000000000000000000001: 0167d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2 diff --git a/test/libyul/yulSyntaxTests/blobhash.yul b/test/libyul/yulSyntaxTests/blobhash.yul new file mode 100644 index 000000000000..6337f22c1b29 --- /dev/null +++ b/test/libyul/yulSyntaxTests/blobhash.yul @@ -0,0 +1,15 @@ +{ + { + let blobhash := 1 + } + + { + function blobhash() {} + blobhash() + } +} + +// ==== +// EVMVersion: >=cancun +// ---- +// ParserError 5568: (20-28): Cannot use builtin function name "blobhash" as identifier name. diff --git a/test/libyul/yulSyntaxTests/blobhash_pre_cancun.yul b/test/libyul/yulSyntaxTests/blobhash_pre_cancun.yul new file mode 100644 index 000000000000..777024cda0eb --- /dev/null +++ b/test/libyul/yulSyntaxTests/blobhash_pre_cancun.yul @@ -0,0 +1,13 @@ +{ + { + let blobhash := 1 + } + + { + function blobhash() {} + blobhash() + } +} +// ==== +// EVMVersion: <=shanghai +// ---- diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index 015924a3b61b..3b0867f51de1 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -31,6 +31,7 @@ #include #include +#include #include @@ -233,6 +234,8 @@ u256 EVMInstructionInterpreter::eval( return m_state.chainid; case Instruction::BASEFEE: return m_state.basefee; + case Instruction::BLOBHASH: + return blobHash(arg[0]); case Instruction::BLOBBASEFEE: return m_state.blobbasefee; case Instruction::EXTCODESIZE: @@ -649,3 +652,16 @@ std::pair EVMInstructionInterpreter::isInputMemoryPtrModified( else return {false, 0}; } + +h256 EVMInstructionInterpreter::blobHash(u256 const& _index) +{ + yulAssert(m_evmVersion.hasBlobHash()); + if (_index >= m_state.blobCommitments.size()) + return util::FixedHash<32>{}; + + h256 hashedCommitment = h256(picosha2::hash256(toBigEndian(m_state.blobCommitments[static_cast(_index)]))); + yulAssert(m_state.blobHashVersion.size == 1); + hashedCommitment[0] = *m_state.blobHashVersion.data(); + yulAssert(hashedCommitment.size == 32); + return hashedCommitment; +} diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.h b/test/tools/yulInterpreter/EVMInstructionInterpreter.h index d37cae542f9d..bd56a0220a4b 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.h +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.h @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -90,6 +91,9 @@ class EVMInstructionInterpreter std::vector const& _evaluatedArguments ); + /// @returns the blob versioned hash + util::h256 blobHash(u256 const& _index); + private: /// Checks if the memory access is valid and adjusts msize accordingly. /// @returns true if memory access is valid, false otherwise diff --git a/test/tools/yulInterpreter/Interpreter.h b/test/tools/yulInterpreter/Interpreter.h index 66082b5b30f6..6a71d48a123d 100644 --- a/test/tools/yulInterpreter/Interpreter.h +++ b/test/tools/yulInterpreter/Interpreter.h @@ -113,6 +113,11 @@ struct InterpreterState /// Number of the current state instance, used for recursion protection size_t numInstance = 0; + // Blob commitment hash version + util::FixedHash<1> const blobHashVersion = util::FixedHash<1>(1); + // Blob commitments + std::array const blobCommitments = {0x01, 0x02}; + /// Prints execution trace and non-zero storage to @param _out. /// Flag @param _disableMemoryTrace, if set, does not produce a memory dump. This /// avoids false positives reports by the fuzzer when certain optimizer steps are From 9af30b56eb24c7239b5f9f2a2b92cea152db6160 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Fri, 12 Jan 2024 07:22:11 +0100 Subject: [PATCH 054/189] Yul fuzzer: Introduce basefee() and blobbasefee() builtins --- test/tools/ossfuzz/protoToYul.cpp | 18 ++++++++++++++++++ test/tools/ossfuzz/yulProto.proto | 3 +++ 2 files changed, 21 insertions(+) diff --git a/test/tools/ossfuzz/protoToYul.cpp b/test/tools/ossfuzz/protoToYul.cpp index 9b487daf5dba..6c8f291d29a3 100644 --- a/test/tools/ossfuzz/protoToYul.cpp +++ b/test/tools/ossfuzz/protoToYul.cpp @@ -118,6 +118,8 @@ EVMVersion ProtoConverter::evmVersionMapping(Program_Version const& _ver) return EVMVersion::paris(); case Program::SHANGHAI: return EVMVersion::shanghai(); + case Program::CANCUN: + return EVMVersion::cancun(); } } @@ -761,6 +763,22 @@ void ProtoConverter::visit(NullaryOp const& _x) else m_output << dictionaryToken(); break; + case NullaryOp::BASEFEE: + // Replace calls to basefee() on unsupported EVMs with a dictionary + // token. + if (m_evmVersion.hasBaseFee()) + m_output << "basefee()"; + else + m_output << dictionaryToken(); + break; + case NullaryOp::BLOBBASEFEE: + // Replace calls to blobbasefee() on unsupported EVMs with a dictionary + // token. + if (m_evmVersion.hasBlobBaseFee()) + m_output << "blobbasefee()"; + else + m_output << dictionaryToken(); + break; } } diff --git a/test/tools/ossfuzz/yulProto.proto b/test/tools/ossfuzz/yulProto.proto index e78080058c7b..7552fb23adde 100644 --- a/test/tools/ossfuzz/yulProto.proto +++ b/test/tools/ossfuzz/yulProto.proto @@ -219,6 +219,8 @@ message NullaryOp { GASLIMIT = 15; SELFBALANCE = 16; CHAINID = 17; + BASEFEE = 18; + BLOBBASEFEE = 19; } required NOp op = 1; } @@ -399,6 +401,7 @@ message Program { LONDON = 8; PARIS = 9; SHANGHAI = 10; + CANCUN = 11; } oneof program_oneof { Block block = 1; From 0da0bffb9e22261baadce454159efa390914e448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 11 Jan 2024 12:18:26 +0100 Subject: [PATCH 055/189] EVMInstructionInterpreter: Readability/style tweaks --- .../EVMInstructionInterpreter.cpp | 27 ++++++++++--------- .../EVMInstructionInterpreter.h | 7 +++-- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index 3b0867f51de1..0466d6f66984 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -74,16 +74,17 @@ u256 readZeroExtended(bytes const& _data, u256 const& _offset) namespace solidity::yul::test { -/// Copy @a _size bytes of @a _source at offset @a _sourceOffset to -/// @a _target at offset @a _targetOffset. Behaves as if @a _source would -/// continue with an infinite sequence of zero bytes beyond its end. + void copyZeroExtended( - std::map& _target, bytes const& _source, - size_t _targetOffset, size_t _sourceOffset, size_t _size + std::map& _target, + bytes const& _source, + size_t _targetOffset, + size_t _sourceOffset, + size_t _size ) { for (size_t i = 0; i < _size; ++i) - _target[_targetOffset + i] = _sourceOffset + i < _source.size() ? _source[_sourceOffset + i] : 0; + _target[_targetOffset + i] = (_sourceOffset + i < _source.size() ? _source[_sourceOffset + i] : 0); } } @@ -489,8 +490,8 @@ u256 EVMInstructionInterpreter::evalBuiltin( { // This is identical to codecopy. if ( - _evaluatedArguments.at(2) != 0 && - accessMemory(_evaluatedArguments.at(0), _evaluatedArguments.at(2)) + _evaluatedArguments.at(2) != 0 && + accessMemory(_evaluatedArguments.at(0), _evaluatedArguments.at(2)) ) copyZeroExtended( m_state.memory, @@ -513,17 +514,17 @@ bool EVMInstructionInterpreter::accessMemory(u256 const& _offset, u256 const& _s { if (_size == 0) return true; - else if (((_offset + _size) >= _offset) && ((_offset + _size + 0x1f) >= (_offset + _size))) + + if (_offset <= (_offset + _size) && (_offset + _size) <= (_offset + _size + 0x1f)) { - u256 newSize = (_offset + _size + 0x1f) & ~u256(0x1f); - m_state.msize = std::max(m_state.msize, newSize); + u256 newMSize = (_offset + _size + 0x1f) & ~u256(0x1f); + m_state.msize = std::max(m_state.msize, newMSize); // We only record accesses to contiguous memory chunks that are at most s_maxRangeSize bytes // in size and at an offset of at most numeric_limits::max() - s_maxRangeSize return _size <= s_maxRangeSize && _offset <= u256(std::numeric_limits::max() - s_maxRangeSize); } - else - m_state.msize = u256(-1); + m_state.msize = u256(-1); return false; } diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.h b/test/tools/yulInterpreter/EVMInstructionInterpreter.h index bd56a0220a4b..2f50217ed107 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.h +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.h @@ -49,8 +49,11 @@ namespace solidity::yul::test /// @a _target at offset @a _targetOffset. Behaves as if @a _source would /// continue with an infinite sequence of zero bytes beyond its end. void copyZeroExtended( - std::map& _target, bytes const& _source, - size_t _targetOffset, size_t _sourceOffset, size_t _size + std::map& _target, + bytes const& _source, + size_t _targetOffset, + size_t _sourceOffset, + size_t _size ); struct InterpreterState; From 492c0eb00fdc2d6f96fda198323a3c473ec5abae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 19 Jan 2024 20:30:27 +0100 Subject: [PATCH 056/189] GasMeter tests: Skip tests with abicoder v2 only if they actually don't pass --- test/libsolidity/GasMeter.cpp | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index be222c400857..a0599f552a73 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -61,14 +61,9 @@ class GasMeterTestFramework: public SolidityExecutionFramework // costs for transaction gas += gasForTransaction(m_compiler.object(m_compiler.lastContractName()).bytecode, true); - // Skip the tests when we use ABIEncoderV2. - // TODO: We should enable this again once the yul optimizer is activated. - if (solidity::test::CommonOptions::get().useABIEncoderV1) - { - BOOST_REQUIRE(!gas.isInfinite); - BOOST_CHECK_LE(m_gasUsed, gas.value); - BOOST_CHECK_LE(gas.value - _tolerance, m_gasUsed); - } + BOOST_REQUIRE(!gas.isInfinite); + BOOST_CHECK_LE(m_gasUsed, gas.value); + BOOST_CHECK_LE(gas.value - _tolerance, m_gasUsed); } /// Compares the gas computed by PathGasMeter for the given signature (but unknown arguments) @@ -90,14 +85,9 @@ class GasMeterTestFramework: public SolidityExecutionFramework *m_compiler.runtimeAssemblyItems(m_compiler.lastContractName()), _sig ); - // Skip the tests when we use ABIEncoderV2. - // TODO: We should enable this again once the yul optimizer is activated. - if (solidity::test::CommonOptions::get().useABIEncoderV1) - { - BOOST_REQUIRE(!gas.isInfinite); - BOOST_CHECK_LE(m_gasUsed, gas.value); - BOOST_CHECK_LE(gas.value - _tolerance, m_gasUsed); - } + BOOST_REQUIRE(!gas.isInfinite); + BOOST_CHECK_LE(m_gasUsed, gas.value); + BOOST_CHECK_LE(gas.value - _tolerance, m_gasUsed); } static GasMeter::GasConsumption gasForTransaction(bytes const& _data, bool _isCreation) @@ -129,6 +119,9 @@ BOOST_AUTO_TEST_CASE(simple_contract) BOOST_AUTO_TEST_CASE(store_keccak256) { char const* sourceCode = R"( + // TODO: We should enable v2 again once the yul optimizer is activated. + pragma abicoder v1; + contract test { bytes32 public shaValue; constructor() { @@ -205,6 +198,9 @@ BOOST_AUTO_TEST_CASE(function_calls) BOOST_AUTO_TEST_CASE(multiple_external_functions) { char const* sourceCode = R"( + // TODO: We should enable v2 again once the yul optimizer is activated. + pragma abicoder v1; + contract test { uint data; uint data2; @@ -236,6 +232,9 @@ BOOST_AUTO_TEST_CASE(multiple_external_functions) BOOST_AUTO_TEST_CASE(exponent_size) { char const* sourceCode = R"( + // TODO: We should enable v2 again once the yul optimizer is activated. + pragma abicoder v1; + contract A { function f(uint x) public returns (uint) { unchecked { return x ** 0; } @@ -302,6 +301,9 @@ BOOST_AUTO_TEST_CASE(complex_control_flow) // Now we do not follow branches if they start out with lower gas costs than the ones // we previously considered. This of course reduces accuracy. char const* sourceCode = R"( + // TODO: We should enable v2 again once the yul optimizer is activated. + pragma abicoder v1; + contract log { function ln(int128 x) public pure returns (int128 result) { unchecked { From 387671795b6716ae6057eec9d07e5bff1aa43462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 19 Jan 2024 21:33:06 +0100 Subject: [PATCH 057/189] test/Common: minEVMVersionCheck() predicate for skipping boost tests --- test/Common.cpp | 7 +++++++ test/Common.h | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/test/Common.cpp b/test/Common.cpp index 72e4d13e8f6f..6a3c5a2a00ad 100644 --- a/test/Common.cpp +++ b/test/Common.cpp @@ -289,6 +289,13 @@ bool isValidSemanticTestPath(boost::filesystem::path const& _testPath) return true; } +boost::unit_test::precondition::predicate_t minEVMVersionCheck(langutil::EVMVersion _minEVMVersion) +{ + return [_minEVMVersion](boost::unit_test::test_unit_id) { + return test::CommonOptions::get().evmVersion() >= _minEVMVersion; + }; +} + bool loadVMs(CommonOptions const& _options) { if (_options.disableSemanticTests) diff --git a/test/Common.h b/test/Common.h index 28ec4e219939..b5963c88750a 100644 --- a/test/Common.h +++ b/test/Common.h @@ -27,6 +27,7 @@ #include #include +#include namespace solidity::test { @@ -100,6 +101,11 @@ struct CommonOptions /// Note: @p _testPath can be relative but must include at least the `/test/libsolidity/semanticTests/` part bool isValidSemanticTestPath(boost::filesystem::path const& _testPath); +/// Helper that can be used to skip tests when the EVM version selected on the command line +/// is older than @p _minEVMVersion. +/// @return A predicate (function) that can be passed into @a boost::unit_test::precondition(). +boost::unit_test::precondition::predicate_t minEVMVersionCheck(langutil::EVMVersion _minEVMVersion); + bool loadVMs(CommonOptions const& _options); /** From 9512f417c3cf4a569a7d37e6417f95d32fd871f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 19 Jan 2024 23:52:10 +0100 Subject: [PATCH 058/189] Make tests based on Analysis Framework properly use line prefix also when printing errors --- test/libsolidity/FunctionDependencyGraphTest.cpp | 5 ++++- test/libsolidity/GasTest.cpp | 3 ++- test/libsolidity/MemoryGuardTest.cpp | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/test/libsolidity/FunctionDependencyGraphTest.cpp b/test/libsolidity/FunctionDependencyGraphTest.cpp index 914fae06f065..c7992d2a2c36 100644 --- a/test/libsolidity/FunctionDependencyGraphTest.cpp +++ b/test/libsolidity/FunctionDependencyGraphTest.cpp @@ -20,9 +20,12 @@ #include #include + #include #include +#include + #include #include @@ -41,7 +44,7 @@ TestCase::TestResult FunctionDependencyGraphTest::run(std::ostream& _stream, std compiler().setOptimiserSettings(OptimiserSettings::none()); if (!compiler().compile(CompilerStack::AnalysisSuccessful)) { - _stream << formatErrors(filteredErrors(), _formatted); + printPrefixed(_stream, formatErrors(filteredErrors(), _formatted), _linePrefix); return TestResult::FatalError; } diff --git a/test/libsolidity/GasTest.cpp b/test/libsolidity/GasTest.cpp index 73aa780e4d32..4786a2a86327 100644 --- a/test/libsolidity/GasTest.cpp +++ b/test/libsolidity/GasTest.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -123,7 +124,7 @@ TestCase::TestResult GasTest::run(std::ostream& _stream, std::string const& _lin { if (!runFramework(withPreamble(m_source), PipelineStage::Compilation)) { - _stream << formatErrors(filteredErrors(), _formatted); + util::printPrefixed(_stream, formatErrors(filteredErrors(), _formatted), _linePrefix); return TestResult::FatalError; } diff --git a/test/libsolidity/MemoryGuardTest.cpp b/test/libsolidity/MemoryGuardTest.cpp index 2d8cc564bd07..3e399dc043a9 100644 --- a/test/libsolidity/MemoryGuardTest.cpp +++ b/test/libsolidity/MemoryGuardTest.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -48,7 +49,7 @@ TestCase::TestResult MemoryGuardTest::run(std::ostream& _stream, std::string con { if (!runFramework(m_source, PipelineStage::Compilation)) { - _stream << formatErrors(filteredErrors(), _formatted); + printPrefixed(_stream, formatErrors(filteredErrors(), _formatted), _linePrefix); return TestResult::FatalError; } From d1447c06235f9d1555b17567b09c20f03df2509c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 19 Jan 2024 23:53:26 +0100 Subject: [PATCH 059/189] MemoryGuardTest: Don't hide errors that occur at the IR parsing stage --- test/libsolidity/MemoryGuardTest.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/libsolidity/MemoryGuardTest.cpp b/test/libsolidity/MemoryGuardTest.cpp index 3e399dc043a9..e5eb7752f2f1 100644 --- a/test/libsolidity/MemoryGuardTest.cpp +++ b/test/libsolidity/MemoryGuardTest.cpp @@ -65,7 +65,8 @@ TestCase::TestResult MemoryGuardTest::run(std::ostream& _stream, std::string con if (!object || !analysisInfo || Error::containsErrors(errors)) { - AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing IR." << std::endl; + AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing IR:" << std::endl; + printPrefixed(_stream, formatErrors(filterErrors(errors), _formatted), _linePrefix); return TestResult::FatalError; } From 7f79cd87a1e14a9ee9ba4553ef8666c7d9b61acc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 19 Jan 2024 23:58:14 +0100 Subject: [PATCH 060/189] MemoryGuardTest: Respect EVM version selected on the command line --- test/libsolidity/MemoryGuardTest.cpp | 4 +++- test/libsolidity/MemoryGuardTest.h | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/test/libsolidity/MemoryGuardTest.cpp b/test/libsolidity/MemoryGuardTest.cpp index e5eb7752f2f1..7f0914cc4ef4 100644 --- a/test/libsolidity/MemoryGuardTest.cpp +++ b/test/libsolidity/MemoryGuardTest.cpp @@ -18,6 +18,7 @@ #include +#include #include #include #include @@ -35,6 +36,7 @@ using namespace solidity::util::formatting; using namespace solidity::langutil; using namespace solidity::frontend; using namespace solidity::frontend::test; +using namespace solidity::test; using namespace yul; void MemoryGuardTest::setupCompiler(CompilerStack& _compiler) @@ -59,7 +61,7 @@ TestCase::TestResult MemoryGuardTest::run(std::ostream& _stream, std::string con ErrorList errors; auto [object, analysisInfo] = yul::test::parse( compiler().yulIR(contractName), - EVMDialect::strictAssemblyForEVMObjects({}), + EVMDialect::strictAssemblyForEVMObjects(CommonOptions::get().evmVersion()), errors ); diff --git a/test/libsolidity/MemoryGuardTest.h b/test/libsolidity/MemoryGuardTest.h index ec6fe5e5b907..e8e5f0ea4e11 100644 --- a/test/libsolidity/MemoryGuardTest.h +++ b/test/libsolidity/MemoryGuardTest.h @@ -34,14 +34,14 @@ namespace solidity::frontend::test using solidity::test::SyntaxTestError; -class MemoryGuardTest: public AnalysisFramework, public TestCase +class MemoryGuardTest: public AnalysisFramework, public EVMVersionRestrictedTestCase { public: static std::unique_ptr create(Config const& _config) { return std::make_unique(_config.filename); } - MemoryGuardTest(std::string const& _filename): TestCase(_filename) + MemoryGuardTest(std::string const& _filename): EVMVersionRestrictedTestCase(_filename) { m_source = m_reader.source(); m_expectation = m_reader.simpleExpectations(); From 81268e336573721819e39fbb3fefbc9344ad176c Mon Sep 17 00:00:00 2001 From: r0qs Date: Wed, 27 Dec 2023 17:50:28 -0300 Subject: [PATCH 061/189] Add blobhash high-level global function. --- Changelog.md | 1 + docs/cheatsheet.rst | 5 +++- docs/units-and-global-variables.rst | 3 +++ docs/using-the-compiler.rst | 1 + libsolidity/analysis/GlobalContext.cpp | 15 ++++++++--- libsolidity/analysis/GlobalContext.h | 3 ++- libsolidity/ast/Types.cpp | 1 + libsolidity/ast/Types.h | 1 + libsolidity/codegen/ExpressionCompiler.cpp | 6 ++++- .../codegen/ir/IRGeneratorForStatements.cpp | 2 ++ libsolidity/interface/CompilerStack.cpp | 5 +++- scripts/test_antlr_grammar.sh | 1 + test/libsolidity/Assembly.cpp | 2 +- .../SolidityExpressionCompiler.cpp | 25 ++++++++++++++++++- test/libsolidity/ViewPureChecker.cpp | 2 ++ .../builtinFunctions/blobhash.sol | 18 +++++++++++++ .../blobhash_shadow_resolution.sol | 12 +++++++++ .../semanticTests/state/blobhash.sol | 14 +++++++++++ .../semanticTests/state/uncalled_blobhash.sol | 12 +++++++++ .../blobhash_function_pre_cancun.sol | 11 ++++++++ .../blobhash_function_shadow_warning.sol | 12 +++++++++ .../globalFunctions/blobhash_no_call.sol | 10 ++++++++ .../blobhash_not_declared_pre_cancun.sol | 10 ++++++++ .../blobhash_var_pre_cancun.sol | 9 +++++++ .../blobhash_var_shadow_warning.sol | 11 ++++++++ ...l => blobhash_pre_cancun_not_declared.sol} | 0 .../blobhash_pre_cancun_not_reserved.sol | 19 ++++++++++++++ .../syntaxTests/viewPureChecker/blobhash.sol | 13 ++++++++++ .../viewPureChecker/blobhash_not_pure.sol | 12 ++++++--- .../builtin_blobhash_restrict_warning.sol | 10 ++++++++ 30 files changed, 234 insertions(+), 12 deletions(-) create mode 100644 test/libsolidity/semanticTests/builtinFunctions/blobhash.sol create mode 100644 test/libsolidity/semanticTests/builtinFunctions/blobhash_shadow_resolution.sol create mode 100644 test/libsolidity/semanticTests/state/blobhash.sol create mode 100644 test/libsolidity/semanticTests/state/uncalled_blobhash.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/blobhash_function_pre_cancun.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/blobhash_function_shadow_warning.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/blobhash_no_call.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/blobhash_not_declared_pre_cancun.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/blobhash_var_pre_cancun.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/blobhash_var_shadow_warning.sol rename test/libsolidity/syntaxTests/inlineAssembly/{blobhash_pre_cancun.sol => blobhash_pre_cancun_not_declared.sol} (100%) create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/blobhash_pre_cancun_not_reserved.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/blobhash.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/builtin_blobhash_restrict_warning.sol diff --git a/Changelog.md b/Changelog.md index 741e64f3e2d9..0cfa621e802a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Language Features: * Introduce global ``block.blobbasefee`` for retrieving the blob base fee of the current block. + * Introduce global function ``blobhash(uint)`` for retrieving versioned hashes of blobs, akin to the homonymous Yul builtin. * Yul: Introduce builtin ``blobbasefee()`` for retrieving the blob base fee of the current block. * Yul: Introduce builtin ``blobhash()`` for retrieving versioned hashes of blobs associated with the transaction. diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index e6b632dae4b6..2b68d7311982 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -56,13 +56,16 @@ Members of ``address`` returns ``false`` on failure - ``
.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure -.. index:: blockhash, block, block;basefee, block;blobbasefee, block;chainid, block;coinbase, block;difficulty, block;gaslimit, block;number, block;prevrandao, block;timestamp +.. 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 Block and Transaction Properties ================================ - ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks +- ``blobhash(uint index) returns (bytes32)``: versioned hash of the ``index``-th blob associated with the current transaction. + A versioned hash consists of a single byte representing the version (currently ``0x01``), followed by the last 31 bytes + of the SHA256 hash of the KZG commitment (`EIP-4844 `_). - ``block.basefee`` (``uint``): current block's base fee (`EIP-3198 `_ and `EIP-1559 `_) - ``block.blobbasefee`` (``uint``): current block's blob base fee (`EIP-7516 `_ and `EIP-4844 `_) - ``block.chainid`` (``uint``): current chain id diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index f95c9c5290d8..a0422c32dfa9 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -74,6 +74,9 @@ Block and Transaction Properties -------------------------------- - ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block when ``blocknumber`` is one of the 256 most recent blocks; otherwise returns zero +- ``blobhash(uint index) returns (bytes32)``: versioned hash of the ``index``-th blob associated with the current transaction. + A versioned hash consists of a single byte representing the version (currently ``0x01``), followed by the last 31 bytes + of the SHA256 hash of the KZG commitment (`EIP-4844 `_). - ``block.basefee`` (``uint``): current block's base fee (`EIP-3198 `_ and `EIP-1559 `_) - ``block.blobbasefee`` (``uint``): current block's blob base fee (`EIP-7516 `_ and `EIP-4844 `_) - ``block.chainid`` (``uint``): current chain id diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 60fbd0884a89..93bdff8661bf 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -178,6 +178,7 @@ at each version. Backward compatibility is not guaranteed between each version. - Smaller code size and gas savings due to the introduction of ``push0`` (see `EIP-3855 `_). - ``cancun`` - The block's blob base fee (`EIP-7516 `_ and `EIP-4844 `_) can be accessed via the global ``block.blobbasefee`` or ``blobbasefee()`` in inline assembly. + - Introduces ``blobhash()`` in inline assembly and a corresponding global function to retrieve versioned hashes of blobs associated with the transaction (see `EIP-4844 `_). .. index:: ! standard JSON, ! --standard-json .. _compiler-api: diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 8f0ce963499b..628b903617de 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -59,17 +59,18 @@ int magicVariableToID(std::string const& _name) else if (_name == "tx") return -26; else if (_name == "type") return -27; else if (_name == "this") return -28; + else if (_name == "blobhash") return -29; else solAssert(false, "Unknown magic variable: \"" + _name + "\"."); } -inline std::vector> constructMagicVariables() +inline std::vector> constructMagicVariables(langutil::EVMVersion _evmVersion) { static auto const magicVarDecl = [](std::string const& _name, Type const* _type) { return std::make_shared(magicVariableToID(_name), _name, _type); }; - return { + std::vector> magicVariableDeclarations = { magicVarDecl("abi", TypeProvider::magic(MagicType::Kind::ABI)), magicVarDecl("addmod", TypeProvider::function(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod, StateMutability::Pure)), magicVarDecl("assert", TypeProvider::function(strings{"bool"}, strings{}, FunctionType::Kind::Assert, StateMutability::Pure)), @@ -101,11 +102,19 @@ inline std::vector> constructMag FunctionType::Options::withArbitraryParameters() )), }; + + if (_evmVersion >= langutil::EVMVersion::cancun()) + magicVariableDeclarations.push_back( + magicVarDecl("blobhash", TypeProvider::function(strings{"uint256"}, strings{"bytes32"}, FunctionType::Kind::BlobHash, StateMutability::View)) + ); + + return magicVariableDeclarations; } } -GlobalContext::GlobalContext(): m_magicVariables{constructMagicVariables()} +GlobalContext::GlobalContext(langutil::EVMVersion _evmVersion): + m_magicVariables{constructMagicVariables(_evmVersion)} { } diff --git a/libsolidity/analysis/GlobalContext.h b/libsolidity/analysis/GlobalContext.h index 69344537cae7..3aa5e76e8966 100644 --- a/libsolidity/analysis/GlobalContext.h +++ b/libsolidity/analysis/GlobalContext.h @@ -23,6 +23,7 @@ #pragma once +#include #include #include #include @@ -47,7 +48,7 @@ class GlobalContext GlobalContext(GlobalContext const&) = delete; GlobalContext& operator=(GlobalContext const&) = delete; - GlobalContext(); + GlobalContext(langutil::EVMVersion _evmVersion); void setCurrentContract(ContractDefinition const& _contract); void resetCurrentContract() { m_currentContract = nullptr; } MagicVariableDeclaration const* currentThis() const; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 77538f485b1c..0af9f105fb1b 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3075,6 +3075,7 @@ std::string FunctionType::richIdentifier() const case Kind::ABIEncodeCall: id += "abiencodecall"; break; case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break; case Kind::ABIDecode: id += "abidecode"; break; + case Kind::BlobHash: id += "blobhash"; break; case Kind::MetaType: id += "metatype"; break; } id += "_" + stateMutabilityToString(m_stateMutability); diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index fd1b6a7d3df3..7bbd2f075875 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -1270,6 +1270,7 @@ class FunctionType: public Type SetGas, ///< modify the default gas value for the function call SetValue, ///< modify the default value transfer for the function call BlockHash, ///< BLOCKHASH + BlobHash, ///< BLOBHASH AddMod, ///< ADDMOD MulMod, ///< MULMOD ArrayPush, ///< .push() to a dynamically sized array in storage diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index c96d8bdea641..494d106c833f 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1035,9 +1035,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) break; } case FunctionType::Kind::BlockHash: + case FunctionType::Kind::BlobHash: { acceptAndConvert(*arguments[0], *function.parameterTypes()[0], true); - m_context << Instruction::BLOCKHASH; + if (function.kind() == FunctionType::Kind::BlockHash) + m_context << Instruction::BLOCKHASH; + else + m_context << Instruction::BLOBHASH; break; } case FunctionType::Kind::AddMod: diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index deed7f80c18c..57414da93ba1 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -1498,11 +1498,13 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) case FunctionType::Kind::GasLeft: case FunctionType::Kind::Selfdestruct: case FunctionType::Kind::BlockHash: + case FunctionType::Kind::BlobHash: { static std::map functions = { {FunctionType::Kind::GasLeft, "gas"}, {FunctionType::Kind::Selfdestruct, "selfdestruct"}, {FunctionType::Kind::BlockHash, "blockhash"}, + {FunctionType::Kind::BlobHash, "blobhash"}, }; solAssert(functions.find(functionType->kind()) != functions.end()); diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index b4e3cf7f389a..9b5c87818bf2 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -235,6 +235,9 @@ void CompilerStack::setEVMVersion(langutil::EVMVersion _version) if (m_stackState >= ParsedAndImported) solThrow(CompilerError, "Must set EVM version before parsing."); m_evmVersion = _version; + // GlobalContext depends on evmVersion since the Cancun hardfork. + // Therefore, we reset it whenever we set a new EVM version, ensuring that the context is never reused with a mismatched version. + m_globalContext.reset(); } void CompilerStack::setEOFVersion(std::optional _version) @@ -469,7 +472,7 @@ bool CompilerStack::analyze() if (source->ast && !syntaxChecker.checkSyntax(*source->ast)) noErrors = false; - m_globalContext = std::make_shared(); + m_globalContext = std::make_shared(m_evmVersion); // We need to keep the same resolver during the whole process. NameAndTypeResolver resolver(*m_globalContext, m_evmVersion, m_errorReporter, experimentalSolidity); for (Source const* source: m_sourceOrder) diff --git a/scripts/test_antlr_grammar.sh b/scripts/test_antlr_grammar.sh index 6ed57f2db9f2..ea00cc0f6e0e 100755 --- a/scripts/test_antlr_grammar.sh +++ b/scripts/test_antlr_grammar.sh @@ -128,6 +128,7 @@ done < <( grep -v -E 'inlineAssembly/prevrandao_disallowed_function_post_paris.sol' | # 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 license error, unrelated to the grammar grep -v -E 'license/license_double5.sol' | grep -v -E 'license/license_hidden_unicode.sol' | diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index ccad7d49d12e..7def76e69fe2 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -63,7 +63,7 @@ evmasm::AssemblyItems compileContract(std::shared_ptr _sourceCode) Scoper::assignScopes(*sourceUnit); BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit)); - GlobalContext globalContext; + 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()); solAssert(!Error::containsErrors(errorReporter.errors()), ""); diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index a171a5be0571..1df1d9430086 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -42,6 +42,7 @@ using namespace solidity::evmasm; using namespace solidity::langutil; +using namespace solidity::test; namespace solidity::frontend::test { @@ -124,7 +125,7 @@ bytes compileFirstExpression( ErrorList errors; ErrorReporter errorReporter(errors); - GlobalContext globalContext; + GlobalContext globalContext(solidity::test::CommonOptions::get().evmVersion()); Scoper::assignScopes(*sourceUnit); BOOST_REQUIRE(SyntaxChecker(errorReporter, false).checkSyntax(*sourceUnit)); NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter, false); @@ -656,6 +657,28 @@ BOOST_AUTO_TEST_CASE(blockhash) BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } +BOOST_AUTO_TEST_CASE( + blobhash, + *boost::unit_test::precondition(minEVMVersionCheck(EVMVersion::cancun())) +) +{ + char const* sourceCode = R"( + contract test { + function f() public { + blobhash(3); + } + } + )"; + + bytes code = compileFirstExpression(sourceCode, {}, {}); + + bytes expectation({ + uint8_t(Instruction::PUSH1), 0x03, + uint8_t(Instruction::BLOBHASH) + }); + BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); +} + BOOST_AUTO_TEST_CASE(gas_left) { char const* sourceCode = R"( diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index a26511a0c944..3d0e024da83b 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -53,6 +53,8 @@ BOOST_AUTO_TEST_CASE(environment_access) }; if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) view.emplace_back("address(0x4242).staticcall(\"\")"); + if (solidity::test::CommonOptions::get().evmVersion().hasBlobHash()) + view.emplace_back("blobhash(7)"); // ``block.blockhash`` and ``blockhash`` are tested separately below because their usage will // produce warnings that can't be handled in a generic way. diff --git a/test/libsolidity/semanticTests/builtinFunctions/blobhash.sol b/test/libsolidity/semanticTests/builtinFunctions/blobhash.sol new file mode 100644 index 000000000000..445a011bda54 --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/blobhash.sol @@ -0,0 +1,18 @@ +contract C { + function f() public view returns(bytes32) { + return blobhash(0); + } + function g() public view returns(bytes32) { + return blobhash(1); + } + function h() public view returns(bytes32) { + // NOTE: blobhash(2) should return 0 since EVMHost has only two blob hashes injected in the block the transaction is being executed. + return blobhash(2); + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// f() -> 0x0100000000000000000000000000000000000000000000000000000000000001 +// g() -> 0x0100000000000000000000000000000000000000000000000000000000000002 +// h() -> 0x00 diff --git a/test/libsolidity/semanticTests/builtinFunctions/blobhash_shadow_resolution.sol b/test/libsolidity/semanticTests/builtinFunctions/blobhash_shadow_resolution.sol new file mode 100644 index 000000000000..6a141cb970fd --- /dev/null +++ b/test/libsolidity/semanticTests/builtinFunctions/blobhash_shadow_resolution.sol @@ -0,0 +1,12 @@ +contract C { + function blobhash(uint256 index) public pure returns(bytes32) { + return bytes32(index); + } + function f() public pure returns(bytes32) { + return blobhash(3); + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// f() -> 0x03 diff --git a/test/libsolidity/semanticTests/state/blobhash.sol b/test/libsolidity/semanticTests/state/blobhash.sol new file mode 100644 index 000000000000..f27b4072be25 --- /dev/null +++ b/test/libsolidity/semanticTests/state/blobhash.sol @@ -0,0 +1,14 @@ +contract C { + function f(uint _index) public returns (bytes32) { + return blobhash(_index); + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// f(uint256): 0 -> 0x0100000000000000000000000000000000000000000000000000000000000001 +// f(uint256): 1 -> 0x0100000000000000000000000000000000000000000000000000000000000002 +// f(uint256): 2 -> 0x00 +// f(uint256): 255 -> 0x00 +// f(uint256): 256 -> 0x00 +// f(uint256): 257 -> 0x00 diff --git a/test/libsolidity/semanticTests/state/uncalled_blobhash.sol b/test/libsolidity/semanticTests/state/uncalled_blobhash.sol new file mode 100644 index 000000000000..df81a46918eb --- /dev/null +++ b/test/libsolidity/semanticTests/state/uncalled_blobhash.sol @@ -0,0 +1,12 @@ +contract C { + function f() public returns (bytes32) { + // NOTE: The `tx_context.blob_hashes` is injected into EVMHost with the following hashes, indexed accordingly: + // 0 -> 0x0100000000000000000000000000000000000000000000000000000000000001 + // 1 -> 0x0100000000000000000000000000000000000000000000000000000000000002 + return (blobhash)(0); + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// f() -> 0x0100000000000000000000000000000000000000000000000000000000000001 diff --git a/test/libsolidity/syntaxTests/globalFunctions/blobhash_function_pre_cancun.sol b/test/libsolidity/syntaxTests/globalFunctions/blobhash_function_pre_cancun.sol new file mode 100644 index 000000000000..e5904f4e67bd --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/blobhash_function_pre_cancun.sol @@ -0,0 +1,11 @@ +contract C { + function blobhash(uint256 index) public pure returns(bytes32) { + return bytes32(index); + } + function f() public pure returns(bytes32) { + return blobhash(2); + } +} +// ==== +// EVMVersion: <=shanghai +// ---- diff --git a/test/libsolidity/syntaxTests/globalFunctions/blobhash_function_shadow_warning.sol b/test/libsolidity/syntaxTests/globalFunctions/blobhash_function_shadow_warning.sol new file mode 100644 index 000000000000..5469df9e6a03 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/blobhash_function_shadow_warning.sol @@ -0,0 +1,12 @@ +contract C { + function blobhash(uint256 index) public pure returns(bytes32) { + return bytes32(index); + } + function f() public pure returns(bytes32) { + return blobhash(2); + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// Warning 2319: (17-117): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/globalFunctions/blobhash_no_call.sol b/test/libsolidity/syntaxTests/globalFunctions/blobhash_no_call.sol new file mode 100644 index 000000000000..369ea047806f --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/blobhash_no_call.sol @@ -0,0 +1,10 @@ +contract C +{ + function f() public pure { + blobhash; + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// Warning 6133: (52-60): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/globalFunctions/blobhash_not_declared_pre_cancun.sol b/test/libsolidity/syntaxTests/globalFunctions/blobhash_not_declared_pre_cancun.sol new file mode 100644 index 000000000000..a62f830150f4 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/blobhash_not_declared_pre_cancun.sol @@ -0,0 +1,10 @@ +contract C +{ + function f() public pure { + blobhash; + } +} +// ==== +// EVMVersion: <=shanghai +// ---- +// DeclarationError 7576: (52-60): Undeclared identifier. Did you mean "blockhash"? diff --git a/test/libsolidity/syntaxTests/globalFunctions/blobhash_var_pre_cancun.sol b/test/libsolidity/syntaxTests/globalFunctions/blobhash_var_pre_cancun.sol new file mode 100644 index 000000000000..42b54dc666a8 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/blobhash_var_pre_cancun.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure returns (bool) { + bool blobhash = true; + return blobhash; + } +} +// ==== +// EVMVersion: <=shanghai +// ---- diff --git a/test/libsolidity/syntaxTests/globalFunctions/blobhash_var_shadow_warning.sol b/test/libsolidity/syntaxTests/globalFunctions/blobhash_var_shadow_warning.sol new file mode 100644 index 000000000000..f60378e8df31 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/blobhash_var_shadow_warning.sol @@ -0,0 +1,11 @@ +contract C +{ + function f() public pure returns (bool) { + bool blobhash = true; + return blobhash; + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// Warning 2319: (67-80): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/blobhash_pre_cancun.sol b/test/libsolidity/syntaxTests/inlineAssembly/blobhash_pre_cancun_not_declared.sol similarity index 100% rename from test/libsolidity/syntaxTests/inlineAssembly/blobhash_pre_cancun.sol rename to test/libsolidity/syntaxTests/inlineAssembly/blobhash_pre_cancun_not_declared.sol diff --git a/test/libsolidity/syntaxTests/inlineAssembly/blobhash_pre_cancun_not_reserved.sol b/test/libsolidity/syntaxTests/inlineAssembly/blobhash_pre_cancun_not_reserved.sol new file mode 100644 index 000000000000..1ac24cc6497a --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/blobhash_pre_cancun_not_reserved.sol @@ -0,0 +1,19 @@ +contract C { + function f() public pure returns (uint ret) { + assembly { + let blobhash := 1 + ret := blobhash + } + } + function g() public pure returns (uint ret) { + assembly { + function blobhash() -> r { + r := 1000 + } + ret := blobhash() + } + } +} +// ==== +// EVMVersion: <=shanghai +// ---- diff --git a/test/libsolidity/syntaxTests/viewPureChecker/blobhash.sol b/test/libsolidity/syntaxTests/viewPureChecker/blobhash.sol new file mode 100644 index 000000000000..4de6929aa84f --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/blobhash.sol @@ -0,0 +1,13 @@ +contract C { + function f() public view returns (bytes32 ret){ + assembly { + ret := blobhash(0) + } + } + function g() public view returns (bytes32) { + return blobhash(0); + } +} +// ==== +// EVMVersion: >=cancun +// ---- diff --git a/test/libsolidity/syntaxTests/viewPureChecker/blobhash_not_pure.sol b/test/libsolidity/syntaxTests/viewPureChecker/blobhash_not_pure.sol index 2e38ddedb8f8..d03e336a6009 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/blobhash_not_pure.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/blobhash_not_pure.sol @@ -1,9 +1,15 @@ contract C { - function f() public pure { - assembly { pop(blobhash(0)) } + function f() public pure returns (bytes32 ret){ + assembly { + ret := blobhash(0) + } + } + function g() public pure returns (bytes32) { + return blobhash(0); } } // ==== // EVMVersion: >=cancun // ---- -// TypeError 2527: (67-78): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError 2527: (103-114): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError 2527: (195-206): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libsolidity/syntaxTests/viewPureChecker/builtin_blobhash_restrict_warning.sol b/test/libsolidity/syntaxTests/viewPureChecker/builtin_blobhash_restrict_warning.sol new file mode 100644 index 000000000000..18a0f58641da --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/builtin_blobhash_restrict_warning.sol @@ -0,0 +1,10 @@ +contract C { + function g() public { + bytes32 w = blobhash(0); + w; + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// Warning 2018: (17-88): Function state mutability can be restricted to view From ce98680ee3bd5d906a0837e4769165493f8f60d7 Mon Sep 17 00:00:00 2001 From: pgebal Date: Wed, 24 Jan 2024 08:50:28 +0100 Subject: [PATCH 062/189] Support bytes concat in formal verification (#14685) --- Changelog.md | 3 +- libsolidity/formal/CHC.cpp | 24 +++-- libsolidity/formal/CHC.h | 2 + libsolidity/formal/Predicate.cpp | 42 ++++---- libsolidity/formal/Predicate.h | 13 ++- libsolidity/formal/PredicateInstance.cpp | 62 ++++++++++-- libsolidity/formal/PredicateInstance.h | 7 ++ libsolidity/formal/PredicateSort.cpp | 23 ++++- libsolidity/formal/PredicateSort.h | 4 +- libsolidity/formal/SMTEncoder.cpp | 99 +++++++++++++++---- libsolidity/formal/SMTEncoder.h | 10 ++ libsolidity/formal/SymbolicState.cpp | 89 ++++++++++++++++- libsolidity/formal/SymbolicState.h | 25 ++++- .../smtCheckerTests/bytes_concat/equals.sol | 52 ++++++++++ .../bytes_concat/one_arg_call.sol | 10 ++ .../smtCheckerTests/bytes_concat/simple.sol | 35 +++++++ .../deployment/deploy_trusted_flow.sol | 4 +- .../operators/index_access_side_effect.sol | 4 +- ...rator_matches_equivalent_function_call.sol | 9 +- ...unction_type_to_function_type_internal.sol | 20 ++-- .../types/array_mapping_aliasing_2.sol | 3 +- 21 files changed, 460 insertions(+), 80 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/bytes_concat/equals.sol create mode 100644 test/libsolidity/smtCheckerTests/bytes_concat/one_arg_call.sol create mode 100644 test/libsolidity/smtCheckerTests/bytes_concat/simple.sol diff --git a/Changelog.md b/Changelog.md index 0cfa621e802a..19b71804a2fe 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,7 +7,8 @@ Language Features: * Yul: Introduce builtin ``blobhash()`` for retrieving versioned hashes of blobs associated with the transaction. Compiler Features: - * EVM: Support for the EVM Version "Cancun". +* EVM: Support for the EVM Version "Cancun". +* SMTChecker: Support `bytes.concat` except when string literals are passed as arguments. Bugfixes: * AST import: Fix bug when importing inline assembly with empty ``let`` variable declaration. diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 3cd1524599df..2c9075abcc4a 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -940,8 +940,6 @@ void CHC::nondetCall(ContractDefinition const& _contract, VariableDeclaration co for (auto const* var: _contract.stateVariables()) m_context.variable(*var)->increaseIndex(); - auto error = errorFlag().increaseIndex(); - Predicate const& callPredicate = *createSymbolicBlock( nondetInterfaceSort(_contract, state()), "nondet_call_" + uniquePrefix(), @@ -950,7 +948,7 @@ void CHC::nondetCall(ContractDefinition const& _contract, VariableDeclaration co m_currentContract ); auto postCallState = std::vector{state().state()} + currentStateVariables(_contract); - std::vector stateExprs{error, address, state().abi(), state().crypto()}; + std::vector stateExprs = commonStateExpressions(errorFlag().increaseIndex(), address); auto nondet = (*m_nondetInterfaces.at(&_contract))(stateExprs + preCallState + postCallState); auto nondetCall = callPredicate(stateExprs + preCallState + postCallState); @@ -1024,8 +1022,6 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall) m_context.variable(*var)->increaseIndex(); } - auto error = errorFlag().increaseIndex(); - Predicate const& callPredicate = *createSymbolicBlock( nondetInterfaceSort(*m_currentContract, state()), "nondet_call_" + uniquePrefix(), @@ -1033,7 +1029,7 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall) &_funCall ); auto postCallState = std::vector{state().state()} + currentStateVariables(); - std::vector stateExprs{error, state().thisAddress(), state().abi(), state().crypto()}; + std::vector stateExprs = commonStateExpressions(errorFlag().increaseIndex(), state().thisAddress()); auto nondet = (*m_nondetInterfaces.at(m_currentContract))(stateExprs + preCallState + postCallState); auto nondetCall = callPredicate(stateExprs + preCallState + postCallState); @@ -1464,7 +1460,9 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source) auto errorPost = errorFlag().increaseIndex(); auto nondetPost = smt::nondetInterface(iface, *contract, m_context, 0, 2); - std::vector args{errorPost, state().thisAddress(), state().abi(), state().crypto(), state().tx(), state().state(1)}; + std::vector args = + commonStateExpressions(errorPost, state().thisAddress()) + + std::vector{state().tx(), state().state(1)}; args += state1 + applyMap(function->parameters(), [this](auto _var) { return valueAtIndex(*_var, 0); }) + std::vector{state().state(2)} + @@ -1829,7 +1827,9 @@ smtutil::Expression CHC::predicate( errorFlag().increaseIndex(); - std::vector args{errorFlag().currentValue(), _contractAddressValue, state().abi(), state().crypto(), state().tx(), state().state()}; + std::vector args = + commonStateExpressions(errorFlag().currentValue(), _contractAddressValue) + + std::vector{state().tx(), state().state()}; auto const* contract = _funDef->annotation().contract; auto const& hierarchy = m_currentContract->annotation().linearizedBaseContracts; @@ -2311,7 +2311,6 @@ std::optional CHC::generateCounterexample(CHCSolverInterface::CexGr path.emplace_back("State: " + modelMsg); } } - std::string txCex = summaryPredicate->formatSummaryCall(summaryArgs, m_charStreamProvider); std::list calls; @@ -2486,3 +2485,10 @@ void CHC::decreaseBalanceFromOptionsValue(Expression const& _value) { state().addBalance(state().thisAddress(), 0 - expr(_value)); } + +std::vector CHC::commonStateExpressions(smtutil::Expression const& error, smtutil::Expression const& address) +{ + if (state().hasBytesConcatFunction()) + return {error, address, state().abi(), state().bytesConcat(), state().crypto()}; + return {error, address, state().abi(), state().crypto()}; +} diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index fe7387bd3568..be150baf1b54 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -362,6 +362,8 @@ class CHC: public SMTEncoder /// Adds constraints that decrease the balance of the caller by _value. void decreaseBalanceFromOptionsValue(Expression const& _value); + + std::vector commonStateExpressions(smtutil::Expression const& error, smtutil::Expression const& address); //@} /// Predicates. diff --git a/libsolidity/formal/Predicate.cpp b/libsolidity/formal/Predicate.cpp index 2ab494fc4d28..58a5a5a3e721 100644 --- a/libsolidity/formal/Predicate.cpp +++ b/libsolidity/formal/Predicate.cpp @@ -55,13 +55,15 @@ Predicate const* Predicate::create( return &m_predicates.emplace( std::piecewise_construct, std::forward_as_tuple(functorName), - std::forward_as_tuple(std::move(predicate), _type, _node, _contractContext, std::move(_scopeStack)) + std::forward_as_tuple(std::move(predicate), _type, _context.state().hasBytesConcatFunction(), + _node, _contractContext, std::move(_scopeStack)) ).first->second; } Predicate::Predicate( smt::SymbolicFunctionVariable&& _predicate, PredicateType _type, + bool _bytesConcatFunctionInContext, ASTNode const* _node, ContractDefinition const* _contractContext, std::vector _scopeStack @@ -70,7 +72,8 @@ Predicate::Predicate( m_type(_type), m_node(_node), m_contractContext(_contractContext), - m_scopeStack(_scopeStack) + m_scopeStack(_scopeStack), + m_bytesConcatFunctionInContext(_bytesConcatFunctionInContext) { } @@ -229,7 +232,7 @@ std::string Predicate::formatSummaryCall( return {}; } - /// The signature of a function summary predicate is: summary(error, this, abiFunctions, cryptoFunctions, txData, preBlockChainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars). + /// The signature of a function summary predicate is: summary(error, this, abiFunctions, (optionally) bytesConcatFunctions, cryptoFunctions, txData, preBlockChainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars). /// Here we are interested in preInputVars to format the function call. std::string txModel; @@ -285,7 +288,7 @@ std::string Predicate::formatSummaryCall( } // Here we are interested in txData from the summary predicate. - auto txValues = readTxVars(_args.at(4)); + auto txValues = readTxVars(_args.at(txValuesIndex())); std::vector values; for (auto const& _var: txVars) if (auto v = txValues.at(_var)) @@ -303,7 +306,7 @@ std::string Predicate::formatSummaryCall( auto const* fun = programFunction(); solAssert(fun, ""); - auto first = _args.begin() + 6 + static_cast(stateVars->size()); + auto first = _args.begin() + static_cast(firstArgIndex()) + static_cast(stateVars->size()); auto last = first + static_cast(fun->parameters().size()); solAssert(first >= _args.begin() && first <= _args.end(), ""); solAssert(last >= _args.begin() && last <= _args.end(), ""); @@ -337,8 +340,8 @@ std::string Predicate::formatSummaryCall( std::vector> Predicate::summaryStateValues(std::vector const& _args) const { - /// The signature of a function summary predicate is: summary(error, this, abiFunctions, cryptoFunctions, txData, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars). - /// The signature of the summary predicate of a contract without constructor is: summary(error, this, abiFunctions, cryptoFunctions, txData, preBlockchainState, postBlockchainState, preStateVars, postStateVars). + /// The signature of a function summary predicate is: summary(error, this, abiFunctions, (optionally) bytesConcatFunctions, cryptoFunctions, txData, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars). + /// The signature of the summary predicate of a contract without constructor is: summary(error, this, abiFunctions, (optionally) bytesConcatFunctions, cryptoFunctions, txData, preBlockchainState, postBlockchainState, preStateVars, postStateVars). /// Here we are interested in postStateVars. auto stateVars = stateVariables(); solAssert(stateVars.has_value(), ""); @@ -347,12 +350,12 @@ std::vector> Predicate::summaryStateValues(std::vecto std::vector::const_iterator stateLast; if (auto const* function = programFunction()) { - stateFirst = _args.begin() + 6 + static_cast(stateVars->size()) + static_cast(function->parameters().size()) + 1; + stateFirst = _args.begin() + static_cast(firstArgIndex()) + static_cast(stateVars->size()) + static_cast(function->parameters().size()) + 1; stateLast = stateFirst + static_cast(stateVars->size()); } else if (programContract()) { - stateFirst = _args.begin() + 7 + static_cast(stateVars->size()); + stateFirst = _args.begin() + static_cast(firstStateVarIndex()) + static_cast(stateVars->size()); stateLast = stateFirst + static_cast(stateVars->size()); } else if (programVariable()) @@ -371,7 +374,7 @@ std::vector> Predicate::summaryStateValues(std::vecto std::vector> Predicate::summaryPostInputValues(std::vector const& _args) const { - /// The signature of a function summary predicate is: summary(error, this, abiFunctions, cryptoFunctions, txData, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars). + /// The signature of a function summary predicate is: summary(error, this, abiFunctions, (optionally) bytesConcatFunctions, cryptoFunctions, txData, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars). /// Here we are interested in postInputVars. auto const* function = programFunction(); solAssert(function, ""); @@ -381,7 +384,7 @@ std::vector> Predicate::summaryPostInputValues(std::v auto const& inParams = function->parameters(); - auto first = _args.begin() + 6 + static_cast(stateVars->size()) * 2 + static_cast(inParams.size()) + 1; + auto first = _args.begin() + static_cast(firstArgIndex()) + static_cast(stateVars->size()) * 2 + static_cast(inParams.size()) + 1; auto last = first + static_cast(inParams.size()); solAssert(first >= _args.begin() && first <= _args.end(), ""); @@ -395,7 +398,7 @@ std::vector> Predicate::summaryPostInputValues(std::v std::vector> Predicate::summaryPostOutputValues(std::vector const& _args) const { - /// The signature of a function summary predicate is: summary(error, this, abiFunctions, cryptoFunctions, txData, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars). + /// The signature of a function summary predicate is: summary(error, this, abiFunctions, (optionally) bytesConcatFunctions, cryptoFunctions, txData, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars). /// Here we are interested in outputVars. auto const* function = programFunction(); solAssert(function, ""); @@ -405,7 +408,7 @@ std::vector> Predicate::summaryPostOutputValues(std:: auto const& inParams = function->parameters(); - auto first = _args.begin() + 6 + static_cast(stateVars->size()) * 2 + static_cast(inParams.size()) * 2 + 1; + auto first = _args.begin() + static_cast(firstArgIndex()) + static_cast(stateVars->size()) * 2 + static_cast(inParams.size()) * 2 + 1; solAssert(first >= _args.begin() && first <= _args.end(), ""); @@ -418,7 +421,7 @@ std::vector> Predicate::summaryPostOutputValues(std:: std::pair>, std::vector> Predicate::localVariableValues(std::vector const& _args) const { /// The signature of a local block predicate is: - /// block(error, this, abiFunctions, cryptoFunctions, txData, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars, localVars). + /// block(error, this, abiFunctions, (optionally) bytesConcatFunctions, cryptoFunctions, txData, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars, localVars). /// Here we are interested in localVars. auto const* function = programFunction(); solAssert(function, ""); @@ -452,19 +455,20 @@ std::map Predicate::expressionSubstitution(smtutil::Ex auto nArgs = _predExpr.arguments.size(); // The signature of an interface predicate is - // interface(this, abiFunctions, cryptoFunctions, blockchainState, stateVariables). + // interface(this, abiFunctions, (optionally) bytesConcatFunctions, cryptoFunctions, blockchainState, stateVariables). // An invariant for an interface predicate is a contract // invariant over its state, for example `x <= 0`. if (isInterface()) { + size_t shift = txValuesIndex(); solAssert(starts_with(predName, "interface"), ""); subst[_predExpr.arguments.at(0).name] = "address(this)"; - solAssert(nArgs == stateVars.size() + 4, ""); + solAssert(nArgs == stateVars.size() + shift, ""); for (size_t i = nArgs - stateVars.size(); i < nArgs; ++i) - subst[_predExpr.arguments.at(i).name] = stateVars.at(i - 4)->name(); + subst[_predExpr.arguments.at(i).name] = stateVars.at(i - shift)->name(); } // The signature of a nondet interface predicate is - // nondet_interface(error, this, abiFunctions, cryptoFunctions, blockchainState, stateVariables, blockchainState', stateVariables'). + // nondet_interface(error, this, abiFunctions, (optionally) bytesConcatFunctions, cryptoFunctions, blockchainState, stateVariables, blockchainState', stateVariables'). // An invariant for a nondet interface predicate is a reentrancy property // over the pre and post state variables of a contract, where pre state vars // are represented by the variable's name and post state vars are represented @@ -475,7 +479,7 @@ std::map Predicate::expressionSubstitution(smtutil::Ex solAssert(starts_with(predName, "nondet_interface"), ""); subst[_predExpr.arguments.at(0).name] = ""; subst[_predExpr.arguments.at(1).name] = "address(this)"; - solAssert(nArgs == stateVars.size() * 2 + 6, ""); + solAssert(nArgs == stateVars.size() * 2 + firstArgIndex(), ""); for (size_t i = nArgs - stateVars.size(), s = 0; i < nArgs; ++i, ++s) subst[_predExpr.arguments.at(i).name] = stateVars.at(s)->name() + "'"; for (size_t i = nArgs - (stateVars.size() * 2 + 1), s = 0; i < nArgs - (stateVars.size() + 1); ++i, ++s) diff --git a/libsolidity/formal/Predicate.h b/libsolidity/formal/Predicate.h index d7ac0da35223..638354f921dc 100644 --- a/libsolidity/formal/Predicate.h +++ b/libsolidity/formal/Predicate.h @@ -71,6 +71,7 @@ class Predicate Predicate( smt::SymbolicFunctionVariable&& _predicate, PredicateType _type, + bool _bytesConcatFunctionInContext, ASTNode const* _node = nullptr, ContractDefinition const* _contractContext = nullptr, std::vector _scopeStack = {} @@ -179,7 +180,7 @@ class Predicate /// @returns a substitution map from the arguments of _predExpr /// to a Solidity-like expression. - std::map expressionSubstitution(smtutil::Expression const& _predExpr) const; + std::map expressionSubstitution(smtutil::Expression const& _predExprs) const; private: /// @returns the formatted version of the given SMT expressions. Those expressions must be SMT constants. @@ -196,6 +197,13 @@ class Predicate std::map> readTxVars(smtutil::Expression const& _tx) const; + /// @returns index at which transaction values start in args list + size_t txValuesIndex() const { return m_bytesConcatFunctionInContext ? 5 : 4; } + /// @returns index at which function arguments start in args list + size_t firstArgIndex() const { return m_bytesConcatFunctionInContext ? 7 : 6; } + /// @returns index at which state variables values start in args list + size_t firstStateVarIndex() const { return m_bytesConcatFunctionInContext ? 8 : 7; } + /// The actual SMT expression. smt::SymbolicFunctionVariable m_predicate; @@ -219,6 +227,9 @@ class Predicate /// The scope stack when the predicate was created. /// Used to identify the subset of variables in scope. std::vector const m_scopeStack; + + /// True iff there is a bytes concat function in contract scope + bool m_bytesConcatFunctionInContext; }; struct PredicateCompare diff --git a/libsolidity/formal/PredicateInstance.cpp b/libsolidity/formal/PredicateInstance.cpp index 0bf352dd4777..b5b148cd48ed 100644 --- a/libsolidity/formal/PredicateInstance.cpp +++ b/libsolidity/formal/PredicateInstance.cpp @@ -29,14 +29,14 @@ namespace solidity::frontend::smt smtutil::Expression interfacePre(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context) { auto& state = _context.state(); - std::vector stateExprs{state.thisAddress(0), state.abi(0), state.crypto(0), state.state(0)}; + std::vector stateExprs = getStateExpressionsForInterfacePre(state); return _pred(stateExprs + initialStateVariables(_contract, _context)); } smtutil::Expression interface(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context) { auto const& state = _context.state(); - std::vector stateExprs{state.thisAddress(0), state.abi(0), state.crypto(0), state.state()}; + std::vector stateExprs = getStateExpressionsForInterface(state); return _pred(stateExprs + currentStateVariables(_contract, _context)); } @@ -48,7 +48,7 @@ smtutil::Expression nondetInterface( unsigned _postIdx) { auto const& state = _context.state(); - std::vector stateExprs{state.errorFlag().currentValue(), state.thisAddress(), state.abi(), state.crypto()}; + std::vector stateExprs = getStateExpressionsForNondetInterface(state); return _pred( stateExprs + std::vector{_context.state().state(_preIdx)} + @@ -65,7 +65,7 @@ smtutil::Expression constructor(Predicate const& _pred, EncodingContext& _contex return _pred(currentFunctionVariablesForDefinition(*constructor, &contract, _context)); auto& state = _context.state(); - std::vector stateExprs{state.errorFlag().currentValue(), state.thisAddress(0), state.abi(0), state.crypto(0), state.tx(0), state.state(0), state.state()}; + std::vector stateExprs = getStateExpressionsForConstructor(state); return _pred(stateExprs + initialStateVariables(contract, _context) + currentStateVariables(contract, _context)); } @@ -76,7 +76,7 @@ smtutil::Expression constructorCall(Predicate const& _pred, EncodingContext& _co return _pred(currentFunctionVariablesForCall(*constructor, &contract, _context, _internal)); auto& state = _context.state(); - std::vector stateExprs{state.errorFlag().currentValue(), _internal ? state.thisAddress(0) : state.thisAddress(), state.abi(0), state.crypto(0), _internal ? state.tx(0) : state.tx(), state.state()}; + std::vector stateExprs = getStateExpressionsForCall(state, _internal); state.newState(); stateExprs += std::vector{state.state()}; stateExprs += currentStateVariables(contract, _context); @@ -152,7 +152,7 @@ std::vector currentFunctionVariablesForDefinition( ) { auto& state = _context.state(); - std::vector exprs{state.errorFlag().currentValue(), state.thisAddress(0), state.abi(0), state.crypto(0), state.tx(0), state.state(0)}; + std::vector exprs = getStateExpressionsForDefinition(state); exprs += _contract ? initialStateVariables(*_contract, _context) : std::vector{}; exprs += applyMap(_function.parameters(), [&](auto _var) { return _context.variable(*_var)->valueAtIndex(0); }); exprs += std::vector{state.state()}; @@ -170,7 +170,7 @@ std::vector currentFunctionVariablesForCall( ) { auto& state = _context.state(); - std::vector exprs{state.errorFlag().currentValue(), _internal ? state.thisAddress(0) : state.thisAddress(), state.abi(0), state.crypto(0), _internal ? state.tx(0) : state.tx(), state.state()}; + std::vector exprs = getStateExpressionsForCall(state, _internal); exprs += _contract ? currentStateVariables(*_contract, _context) : std::vector{}; exprs += applyMap(_function.parameters(), [&](auto _var) { return _context.variable(*_var)->currentValue(); }); @@ -192,4 +192,52 @@ std::vector currentBlockVariables(FunctionDefinition const& ); } +std::vector getStateExpressionsForInterfacePre(SymbolicState const& _state) +{ + if (_state.hasBytesConcatFunction()) + return {_state.thisAddress(0), + _state.abi(0), _state.bytesConcat(0), _state.crypto(0), _state.state(0)}; +return {_state.thisAddress(0), _state.abi(0), _state.crypto(0), _state.state(0)}; +} + +std::vector getStateExpressionsForInterface(SymbolicState const& _state) +{ + if (_state.hasBytesConcatFunction()) + return {_state.thisAddress(0), _state.abi(0), _state.bytesConcat(0), _state.crypto(0), _state.state()}; + return {_state.thisAddress(0), _state.abi(0), _state.crypto(0), _state.state()}; +} + +std::vector getStateExpressionsForNondetInterface(SymbolicState const& _state) +{ + if (_state.hasBytesConcatFunction()) + return {_state.errorFlag().currentValue(), _state.thisAddress(), _state.abi(), _state.bytesConcat(), _state.crypto()}; + return {_state.errorFlag().currentValue(), _state.thisAddress(), _state.abi(), _state.crypto()}; +} + +std::vector getStateExpressionsForConstructor(SymbolicState const& _state) +{ + if (_state.hasBytesConcatFunction()) + return {_state.errorFlag().currentValue(), _state.thisAddress(0), _state.abi(0), + _state.bytesConcat(0), _state.crypto(0), _state.tx(0), _state.state(0), _state.state()}; + return {_state.errorFlag().currentValue(), _state.thisAddress(0), _state.abi(0), + _state.crypto(0), _state.tx(0), _state.state(0), _state.state()}; +} + +std::vector getStateExpressionsForCall(SymbolicState const& _state, bool _internal) +{ + if (_state.hasBytesConcatFunction()) + return {_state.errorFlag().currentValue(), _internal ? _state.thisAddress(0) : _state.thisAddress(), + _state.abi(0), _state.bytesConcat(0), _state.crypto(0), _internal ? _state.tx(0) : _state.tx(), _state.state()}; + return {_state.errorFlag().currentValue(), _internal ? _state.thisAddress(0) : _state.thisAddress(), + _state.abi(0), _state.crypto(0), _internal ? _state.tx(0) : _state.tx(), _state.state()}; +} + +std::vector getStateExpressionsForDefinition(SymbolicState const& _state) +{ + if (_state.hasBytesConcatFunction()) + return {_state.errorFlag().currentValue(), _state.thisAddress(0), _state.abi(0), + _state.bytesConcat(0), _state.crypto(0), _state.tx(0), _state.state(0)}; + return {_state.errorFlag().currentValue(), _state.thisAddress(0), _state.abi(0), + _state.crypto(0), _state.tx(0), _state.state(0)}; +} } diff --git a/libsolidity/formal/PredicateInstance.h b/libsolidity/formal/PredicateInstance.h index 42a9e2040b36..33c8a8a0dde9 100644 --- a/libsolidity/formal/PredicateInstance.h +++ b/libsolidity/formal/PredicateInstance.h @@ -19,6 +19,7 @@ #pragma once #include +#include namespace solidity::frontend::smt { @@ -94,4 +95,10 @@ std::vector currentBlockVariables( EncodingContext& _context ); +std::vector getStateExpressionsForInterfacePre(SymbolicState const& _state); +std::vector getStateExpressionsForInterface(SymbolicState const& _state); +std::vector getStateExpressionsForNondetInterface(SymbolicState const& _state); +std::vector getStateExpressionsForConstructor(SymbolicState const& _state); +std::vector getStateExpressionsForCall(SymbolicState const& _state, bool _internal); +std::vector getStateExpressionsForDefinition(SymbolicState const& _state); } diff --git a/libsolidity/formal/PredicateSort.cpp b/libsolidity/formal/PredicateSort.cpp index 2e95a29a531d..0f9eae82c423 100644 --- a/libsolidity/formal/PredicateSort.cpp +++ b/libsolidity/formal/PredicateSort.cpp @@ -30,7 +30,10 @@ namespace solidity::frontend::smt SortPointer interfaceSort(ContractDefinition const& _contract, SymbolicState& _state) { return std::make_shared( - std::vector{_state.thisAddressSort(), _state.abiSort(), _state.cryptoSort(), _state.stateSort()} + stateSorts(_contract), + std::vector{_state.thisAddressSort()} + + getBuiltInFunctionsSorts(_state) + + std::vector{_state.stateSort()} + + stateSorts(_contract), SortProvider::boolSort ); } @@ -40,7 +43,8 @@ SortPointer nondetInterfaceSort(ContractDefinition const& _contract, SymbolicSta auto varSorts = stateSorts(_contract); std::vector stateSort{_state.stateSort()}; return std::make_shared( - std::vector{_state.errorFlagSort(), _state.thisAddressSort(), _state.abiSort(), _state.cryptoSort()} + + std::vector{_state.errorFlagSort(), _state.thisAddressSort()} + + getBuiltInFunctionsSorts(_state) + stateSort + varSorts + stateSort + @@ -57,7 +61,10 @@ SortPointer constructorSort(ContractDefinition const& _contract, SymbolicState& auto varSorts = stateSorts(_contract); std::vector stateSort{_state.stateSort()}; return std::make_shared( - std::vector{_state.errorFlagSort(), _state.thisAddressSort(), _state.abiSort(), _state.cryptoSort(), _state.txSort(), _state.stateSort(), _state.stateSort()} + varSorts + varSorts, + std::vector{_state.errorFlagSort(), _state.thisAddressSort()} + + getBuiltInFunctionsSorts(_state) + + std::vector{_state.txSort(), _state.stateSort(), _state.stateSort()} + + varSorts + varSorts, SortProvider::boolSort ); } @@ -69,7 +76,9 @@ SortPointer functionSort(FunctionDefinition const& _function, ContractDefinition auto inputSorts = applyMap(_function.parameters(), smtSort); auto outputSorts = applyMap(_function.returnParameters(), smtSort); return std::make_shared( - std::vector{_state.errorFlagSort(), _state.thisAddressSort(), _state.abiSort(), _state.cryptoSort(), _state.txSort(), _state.stateSort()} + + std::vector{_state.errorFlagSort(), _state.thisAddressSort()} + + getBuiltInFunctionsSorts(_state) + + std::vector{_state.txSort(), _state.stateSort()} + varSorts + inputSorts + std::vector{_state.stateSort()} + @@ -110,4 +119,10 @@ std::vector stateSorts(ContractDefinition const& _contract) ); } +std::vector getBuiltInFunctionsSorts(SymbolicState& _state) +{ + if (_state.hasBytesConcatFunction()) + return {_state.abiSort(), _state.bytesConcatSort(), _state.cryptoSort()}; + return {_state.abiSort(), _state.cryptoSort()}; +} } diff --git a/libsolidity/formal/PredicateSort.h b/libsolidity/formal/PredicateSort.h index 5638582546e9..c5f63f2a6965 100644 --- a/libsolidity/formal/PredicateSort.h +++ b/libsolidity/formal/PredicateSort.h @@ -74,6 +74,8 @@ smtutil::SortPointer arity0FunctionSort(); /// Helpers -std::vector stateSorts(ContractDefinition const& _contract) ; +std::vector stateSorts(ContractDefinition const& _contract); + +std::vector getBuiltInFunctionsSorts(SymbolicState& _state); } diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index ae3a06869d19..61aad0e8f526 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -651,6 +651,9 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall) if (publicGetter(_funCall.expression())) visitPublicGetter(_funCall); break; + case FunctionType::Kind::BytesConcat: + visitBytesConcat(_funCall); + break; case FunctionType::Kind::ABIDecode: case FunctionType::Kind::ABIEncode: case FunctionType::Kind::ABIEncodePacked: @@ -780,6 +783,33 @@ void SMTEncoder::visitRequire(FunctionCall const& _funCall) addPathImpliedExpression(expr(*args.front())); } +void SMTEncoder::visitBytesConcat(FunctionCall const& _funCall) +{ + auto const& args = _funCall.sortedArguments(); + + // bytes.concat call with no arguments returns an empty array + if (args.size() == 0) + { + defineExpr(_funCall, smt::zeroValue(TypeProvider::bytesMemory())); + return; + } + + // bytes.concat with single argument of type bytes memory + if (args.size() == 1 && args.front()->annotation().type->category() == frontend::Type::Category::Array) + { + defineExpr(_funCall, expr(*args.front(), TypeProvider::bytesMemory())); + return; + } + + auto const& [name, inTypes, outType] = state().bytesConcatFunctionTypes(&_funCall); + solAssert(inTypes.size() == args.size(), ""); + + auto symbFunction = state().bytesConcatFunction(&_funCall); + auto out = createSelectExpressionForFunction(symbFunction, args, inTypes, args.size()); + + defineExpr(_funCall, out); +} + void SMTEncoder::visitABIFunction(FunctionCall const& _funCall) { auto symbFunction = state().abiFunction(&_funCall); @@ -796,24 +826,8 @@ void SMTEncoder::visitABIFunction(FunctionCall const& _funCall) defineExpr(_funCall, smt::zeroValue(TypeProvider::bytesMemory())); return; } - std::vector symbArgs; - for (unsigned i = 0; i < argsActualLength; ++i) - if (args.at(i)) - symbArgs.emplace_back(expr(*args.at(i), inTypes.at(i))); - std::optional arg; - if (inTypes.size() == 1) - arg = expr(*args.at(0), inTypes.at(0)); - else - { - auto inputSort = dynamic_cast(*symbFunction.sort).domain; - arg = smtutil::Expression::tuple_constructor( - smtutil::Expression(std::make_shared(inputSort), ""), - symbArgs - ); - } - - auto out = smtutil::Expression::select(symbFunction, *arg); + auto out = createSelectExpressionForFunction(symbFunction, args, inTypes, argsActualLength); if (outTypes.size() == 1) defineExpr(_funCall, out); else @@ -909,7 +923,6 @@ void SMTEncoder::visitObjectCreation(FunctionCall const& _funCall) smtutil::Expression arraySize = expr(*args.front()); setSymbolicUnknownValue(arraySize, TypeProvider::uint256(), m_context); - auto symbArray = std::dynamic_pointer_cast(m_context.expression(_funCall)); solAssert(symbArray, ""); smt::setSymbolicZeroValue(*symbArray, m_context); @@ -3112,6 +3125,29 @@ std::set> SMTEncoder::collectA return ABIFunctions(_node).abiCalls; } +std::set> SMTEncoder::collectBytesConcatCalls(ASTNode const* _node) +{ + struct BytesConcatFunctions: public ASTConstVisitor + { + BytesConcatFunctions(ASTNode const* _node) { _node->accept(*this); } + void endVisit(FunctionCall const& _funCall) + { + if (*_funCall.annotation().kind == FunctionCallKind::FunctionCall) + switch (dynamic_cast(*_funCall.expression().annotation().type).kind()) + { + case FunctionType::Kind::BytesConcat: + bytesConcatCalls.insert(&_funCall); + break; + default: {} + } + } + + std::set> bytesConcatCalls; + }; + + return BytesConcatFunctions(_node).bytesConcatCalls; +} + std::set SMTEncoder::sourceDependencies(SourceUnit const& _source) { std::set sources; @@ -3216,3 +3252,30 @@ smt::SymbolicState& SMTEncoder::state() { return m_context.state(); } + +smtutil::Expression SMTEncoder::createSelectExpressionForFunction( + smtutil::Expression symbFunction, + std::vector> const& args, + frontend::TypePointers const& inTypes, + unsigned long argsActualLength +) +{ + solAssert(argsActualLength <= args.size() && inTypes.size() == argsActualLength); + if (inTypes.size() == 1) + { + smtutil::Expression arg = expr(*args.at(0), inTypes.at(0)); + return smtutil::Expression::select(symbFunction, arg); + } + + std::vector symbArgs; + for (unsigned i = 0; i < argsActualLength; ++i) + if (args.at(i)) + symbArgs.emplace_back(expr(*args.at(i), inTypes.at(i))); + + auto inputSort = dynamic_cast(*symbFunction.sort).domain; + smtutil::Expression arg = smtutil::Expression::tuple_constructor( + smtutil::Expression(std::make_shared(inputSort), ""), + symbArgs + ); + return smtutil::Expression::select(symbFunction, arg); +} diff --git a/libsolidity/formal/SMTEncoder.h b/libsolidity/formal/SMTEncoder.h index 69010fb1cf54..be676930cab1 100644 --- a/libsolidity/formal/SMTEncoder.h +++ b/libsolidity/formal/SMTEncoder.h @@ -122,6 +122,7 @@ class SMTEncoder: public ASTConstVisitor static RationalNumberType const* isConstant(Expression const& _expr); static std::set> collectABICalls(ASTNode const* _node); + static std::set> collectBytesConcatCalls(ASTNode const* _node); /// @returns all the sources that @param _source depends on, /// including itself. @@ -211,6 +212,7 @@ class SMTEncoder: public ASTConstVisitor void visitAssert(FunctionCall const& _funCall); void visitRequire(FunctionCall const& _funCall); void visitABIFunction(FunctionCall const& _funCall); + void visitBytesConcat(FunctionCall const& _funCall); void visitCryptoFunction(FunctionCall const& _funCall); void visitGasLeft(FunctionCall const& _funCall); virtual void visitAddMulMod(FunctionCall const& _funCall); @@ -500,6 +502,14 @@ class SMTEncoder: public ASTConstVisitor langutil::CharStreamProvider const& m_charStreamProvider; smt::SymbolicState& state(); + +private: + smtutil::Expression createSelectExpressionForFunction( + smtutil::Expression symbFunction, + std::vector> const& args, + frontend::TypePointers const& inTypes, + unsigned long argsActualLength + ); }; } diff --git a/libsolidity/formal/SymbolicState.cpp b/libsolidity/formal/SymbolicState.cpp index 3859e99c6243..bf835b801d21 100644 --- a/libsolidity/formal/SymbolicState.cpp +++ b/libsolidity/formal/SymbolicState.cpp @@ -268,16 +268,19 @@ void SymbolicState::prepareForSourceUnit(SourceUnit const& _source, bool _storag auto allSources = _source.referencedSourceUnits(true); allSources.insert(&_source); std::set> abiCalls; + std::set> bytesConcatCalls; std::set> contracts; for (auto const& source: allSources) { abiCalls += SMTEncoder::collectABICalls(source); + bytesConcatCalls += SMTEncoder::collectBytesConcatCalls(source); for (auto node: source->nodes()) if (auto contract = dynamic_cast(node.get())) contracts.insert(contract); } buildState(contracts, _storage); buildABIFunctions(abiCalls); + buildBytesConcatFunctions(bytesConcatCalls); } /// Private helpers. @@ -355,6 +358,80 @@ void SymbolicState::buildState(std::set> const& _bytesConcatCalls) +{ + std::map functions; + + for (auto const* funCall: _bytesConcatCalls) + { + auto t = dynamic_cast(funCall->expression().annotation().type); + solAssert(t->kind() == FunctionType::Kind::BytesConcat, "Expected bytes.concat function"); + + auto const& args = funCall->sortedArguments(); + + auto argTypes = [](auto const& _args) { + return util::applyMap(_args, [](auto arg) { return arg->annotation().type; }); + }; + + // bytes.concat : (bytes/literal/fixed bytes, ... ) -> bytes + std::vector inTypes = argTypes(args); + + auto replaceUserDefinedValueTypes = [](auto& _types) { + for (auto& t: _types) + if (auto userType = dynamic_cast(t)) + t = &userType->underlyingType(); + }; + auto replaceStringLiteralTypes = [](auto& _types) { + for (auto& t: _types) + if (t->category() == frontend::Type::Category::StringLiteral) + t = TypeProvider::bytesMemory(); + }; + replaceUserDefinedValueTypes(inTypes); + replaceStringLiteralTypes(inTypes); + + auto name = t->richIdentifier(); + for (auto paramType: inTypes) + name += "_" + paramType->richIdentifier(); + + frontend::Type const* outType = TypeProvider::bytesMemory(); + name += "_" + outType->richIdentifier(); + + m_bytesConcatMembers[funCall] = {name, inTypes, outType}; + + if (functions.count(name)) + continue; + + /// If there is only one parameter, we use that type directly. + /// Otherwise we create a tuple wrapping the necessary types. + auto typesToSort = [](auto const& _types, std::string const& _name) -> std::shared_ptr { + if (_types.size() == 1) + return smtSortAbstractFunction(*_types.front()); + + std::vector inNames; + std::vector sorts; + for (unsigned i = 0; i < _types.size(); ++i) + { + inNames.emplace_back(_name + "_input_" + std::to_string(i)); + sorts.emplace_back(smtSortAbstractFunction(*_types.at(i))); + } + return std::make_shared( + _name + "_input", + inNames, + sorts + ); + }; + + auto functionSort = std::make_shared( + typesToSort(inTypes, name), + smtSortAbstractFunction(*outType) + ); + + functions[name] = functionSort; + } + + m_bytesConcat = std::make_unique("bytesConcat", std::move(functions), m_context); +} + void SymbolicState::buildABIFunctions(std::set> const& _abiFunctions) { std::map functions; @@ -367,7 +444,6 @@ void SymbolicState::buildABIFunctions(std::setparameterTypes(); auto const& returnTypes = t->returnParameterTypes(); - auto argTypes = [](auto const& _args) { return util::applyMap(_args, [](auto arg) { return arg->annotation().type; }); }; @@ -493,3 +569,14 @@ SymbolicState::SymbolicABIFunction const& SymbolicState::abiFunctionTypes(Functi { return m_abiMembers.at(_funCall); } + +smtutil::Expression SymbolicState::bytesConcatFunction(frontend::FunctionCall const* _funCall) +{ + solAssert(m_bytesConcat, ""); + return m_bytesConcat->member(std::get<0>(m_bytesConcatMembers.at(_funCall))); +} + +SymbolicState::SymbolicBytesConcatFunction const& SymbolicState::bytesConcatFunctionTypes(FunctionCall const* _funCall) const +{ + return m_bytesConcatMembers.at(_funCall); +} diff --git a/libsolidity/formal/SymbolicState.h b/libsolidity/formal/SymbolicState.h index 7fe5b028d627..ac018df1acf8 100644 --- a/libsolidity/formal/SymbolicState.h +++ b/libsolidity/formal/SymbolicState.h @@ -182,7 +182,21 @@ class SymbolicState smtutil::Expression abi() const { solAssert(m_abi, ""); return m_abi->value(); } smtutil::Expression abi(unsigned _idx) const { solAssert(m_abi, ""); return m_abi->value(_idx); } smtutil::SortPointer const& abiSort() const { solAssert(m_abi, ""); return m_abi->sort(); } - void newABI() { solAssert(m_abi, ""); m_abi->newVar(); } + //@} + + /// bytes.concat functions. + //@{ + smtutil::Expression bytesConcatFunction(FunctionCall const* _funCall); + using SymbolicBytesConcatFunction = std::tuple< + std::string, + std::vector, + frontend::Type const* + >; + SymbolicBytesConcatFunction const& bytesConcatFunctionTypes(FunctionCall const* _funCall) const; + smtutil::Expression bytesConcat() const { solAssert(m_bytesConcat, ""); return m_bytesConcat->value(); } + smtutil::Expression bytesConcat(unsigned _idx) const { solAssert(m_bytesConcat, ""); return m_bytesConcat->value(_idx); } + smtutil::SortPointer const& bytesConcatSort() const { solAssert(m_bytesConcat, ""); return m_bytesConcat->sort(); } + bool hasBytesConcatFunction() const {solAssert(m_bytesConcat, ""); return !m_bytesConcatMembers.empty(); } //@} private: @@ -196,6 +210,9 @@ class SymbolicState /// Builds m_abi based on the abi.* calls _abiFunctions. void buildABIFunctions(std::set> const& _abiFunctions); + /// Builds m_bytesConcat based on the bytes.concat calls + void buildBytesConcatFunctions(std::set> const& _bytesConcatCalls); + EncodingContext& m_context; SymbolicIntVariable m_error{ @@ -278,6 +295,12 @@ class SymbolicState /// Maps ABI functions calls to their tuple names generated by /// `buildABIFunctions`. std::map m_abiMembers; + + /// Tuple containing all used bytes.concat functions. + std::unique_ptr m_bytesConcat; + /// Maps bytes.concat functions calls to their tuple names generated by + /// `buildBytesConcatFunctions`. + std::map m_bytesConcatMembers; }; } diff --git a/test/libsolidity/smtCheckerTests/bytes_concat/equals.sol b/test/libsolidity/smtCheckerTests/bytes_concat/equals.sol new file mode 100644 index 000000000000..334a2e0c5ff4 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bytes_concat/equals.sol @@ -0,0 +1,52 @@ +contract C { + + function concatCall(bytes8 a) public pure returns (bytes memory) { + return bytes.concat(a, a); + } + + function equalArguments1(bytes8 a, bytes8 b, bytes8 c, bytes8 d) public pure { + require(a == c); + require(b == d); + bytes memory concat1 = bytes.concat(a, b); + bytes memory concat2 = bytes.concat(c, d); + assert(keccak256(concat1) == keccak256(concat2)); + } + + function equalArguments2(bytes8 a, bytes8 c) public pure { + require(a == c); + bytes memory concat1 = bytes.concat(a, concatCall(a)); + bytes memory concat2 = bytes.concat(c, concatCall(c)); + assert(keccak256(concat1) == keccak256(concat2)); + } + + function equalLengthFixedBytes(bytes8 a, bytes8 b) public pure { + bytes memory concat1 = bytes.concat(a, b); + bytes memory concat2 = bytes.concat(a, b); + assert(concat1.length == concat2.length); + } + + function equalLengthMemoryBytes(bytes memory a, bytes memory b) public pure { + bytes memory concat1 = bytes.concat(a, b); + bytes memory concat2 = bytes.concat(a, b); + assert(concat1.length == concat2.length); + } + + function equalLengthMixed(bytes memory a, bytes2 b) public pure { + bytes memory concat1 = bytes.concat(a, b); + bytes memory concat2 = bytes.concat(a, b); + assert(concat1.length == concat2.length); + } + + function equalLengthLiterals() public pure { + bytes memory a = hex"aa"; + bytes1 b = bytes1(0xbb); + bytes memory c = "c"; + bytes memory concat1 = bytes.concat(a, b, c); + bytes memory concat2 = bytes.concat(a, b, c); + assert(concat1.length == concat2.length); + } +} +// ==== +// SMTEngine: all +// ---- +// Info 1391: CHC: 6 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/bytes_concat/one_arg_call.sol b/test/libsolidity/smtCheckerTests/bytes_concat/one_arg_call.sol new file mode 100644 index 000000000000..4f539f3184d0 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bytes_concat/one_arg_call.sol @@ -0,0 +1,10 @@ +contract C { + function oneArg(bytes memory a) public pure { + bytes memory concat = bytes.concat(a); + assert(keccak256(a) == keccak256(concat)); + } +} +// ==== +// SMTEngine: all +// ---- +// 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/bytes_concat/simple.sol b/test/libsolidity/smtCheckerTests/bytes_concat/simple.sol new file mode 100644 index 000000000000..834d89ee9b4e --- /dev/null +++ b/test/libsolidity/smtCheckerTests/bytes_concat/simple.sol @@ -0,0 +1,35 @@ +contract C { + function zeroArgs() public pure returns(bytes memory) { + bytes memory a = bytes.concat(); + assert(a.length == 0); // zero args call is an empty bytes array + return bytes.concat(); + } + + function oneArg(bytes memory a) public pure returns(bytes memory) { + return bytes.concat(a); + } + + function oneArgFixedBytes(bytes8 a) public pure returns(bytes memory) { + return bytes.concat(a); + } + + function fixedBytes(bytes8 a, bytes8 b) public pure returns(bytes memory) { + return bytes.concat(a, b); + } + + function memoryBytes(bytes memory a, bytes memory b) public pure returns(bytes memory) { + return bytes.concat(a, b); + } + + function mixed(bytes memory a, bytes2 b) public pure returns(bytes memory) { + return bytes.concat(a, b, "StringLiteral"); + } + + function functionCallAsArg(bytes memory a) public pure returns(bytes memory) { + return bytes.concat(oneArg(a), zeroArgs()); + } +} +// ==== +// SMTEngine: all +// ---- +// 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/deployment/deploy_trusted_flow.sol b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_flow.sol index eb0a0c0498ae..09f001719fbe 100644 --- a/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_flow.sol +++ b/test/libsolidity/smtCheckerTests/deployment/deploy_trusted_flow.sol @@ -21,8 +21,10 @@ contract C { // SMTIgnoreOS: macos // ---- // Warning 4984: (47-50): CHC: Overflow (resulting value larger than 2**256 - 1) might happen here. +// Warning 6328: (167-185): CHC: Assertion violation might happen here. // Warning 6328: (215-233): CHC: Assertion violation might happen here. // Warning 6328: (304-322): 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. +// Info 1391: CHC: 1 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. // Warning 2661: (47-50): BMC: Overflow (resulting value larger than 2**256 - 1) happens here. +// Warning 4661: (167-185): BMC: Assertion violation happens here. // Warning 4661: (215-233): BMC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/operators/index_access_side_effect.sol b/test/libsolidity/smtCheckerTests/operators/index_access_side_effect.sol index d18f605f7c5e..32c9780d1134 100644 --- a/test/libsolidity/smtCheckerTests/operators/index_access_side_effect.sol +++ b/test/libsolidity/smtCheckerTests/operators/index_access_side_effect.sol @@ -23,6 +23,4 @@ contract C { // SMTIgnoreCex: yes // SMTIgnoreOS: macos // ---- -// Warning 6328: (335-354): CHC: Assertion violation might happen here. -// Info 1391: CHC: 3 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. -// Warning 4661: (335-354): BMC: Assertion violation happens here. +// 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/operators/userDefined/user_defined_operator_matches_equivalent_function_call.sol b/test/libsolidity/smtCheckerTests/operators/userDefined/user_defined_operator_matches_equivalent_function_call.sol index a6cad9580f72..b734079c7df5 100644 --- a/test/libsolidity/smtCheckerTests/operators/userDefined/user_defined_operator_matches_equivalent_function_call.sol +++ b/test/libsolidity/smtCheckerTests/operators/userDefined/user_defined_operator_matches_equivalent_function_call.sol @@ -63,12 +63,15 @@ contract C { // Warning 4984: (953-982): CHC: Overflow (resulting value larger than 32767) might happen here. // Warning 4984: (1051-1080): CHC: Overflow (resulting value larger than 32767) might happen here. // Warning 4281: (1051-1080): CHC: Division by zero might happen here. -// Warning 4281: (1149-1178): CHC: Division by zero happens here. +// Warning 4281: (1149-1178): CHC: Division by zero might happen here. +// Warning 6328: (2069-2095): CHC: Assertion violation might happen here. // Warning 6328: (2105-2131): CHC: Assertion violation might happen here. // Warning 6328: (2141-2163): CHC: Assertion violation might happen here. +// Warning 6328: (2173-2199): CHC: Assertion violation might happen here. // Warning 6328: (2209-2235): CHC: Assertion violation might happen here. // Warning 6328: (2245-2271): CHC: Assertion violation might happen here. -// Info 1391: CHC: 12 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. +// Info 1391: CHC: 10 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. // Warning 3046: (1051-1080): BMC: Division by zero happens here. +// Warning 3046: (1149-1178): BMC: Division by zero happens here. // Warning 7812: (2245-2271): BMC: Assertion violation might happen here. -// Info 6002: BMC: 4 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. +// Info 6002: BMC: 6 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. diff --git a/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol index 2d3448ee7b0d..368e9c922f6c 100644 --- a/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol +++ b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol @@ -1,10 +1,10 @@ contract C { function(uint) returns (uint) a; function(uint) returns (uint) b; - function f(function(uint) returns (uint) g, function(uint) returns (uint) h) internal { + function f(function(uint) returns (uint) g, function(uint) returns (uint) h) internal { assert(g(2) == h(2)); assert(g == h); - } + } function g() public { f(a, b); } @@ -12,11 +12,11 @@ contract C { // ==== // SMTEngine: all // ---- -// Warning 2519: (96-127): This declaration shadows an existing declaration. -// Warning 6031: (182-186): Internal error: Expression undefined for SMT solver. -// Warning 6031: (190-194): Internal error: Expression undefined for SMT solver. -// Warning 7229: (206-212): Assertion checker does not yet implement the type function (uint256) returns (uint256) for comparisons -// Warning 5729: (182-186): BMC does not yet implement this type of function call. -// Warning 5729: (190-194): BMC does not yet implement this type of function call. -// Warning 6328: (175-195): CHC: Assertion violation happens here. -// Warning 6328: (199-213): CHC: Assertion violation happens here. +// Warning 2519: (93-124): This declaration shadows an existing declaration. +// Warning 6031: (179-183): Internal error: Expression undefined for SMT solver. +// Warning 6031: (187-191): Internal error: Expression undefined for SMT solver. +// Warning 7229: (203-209): Assertion checker does not yet implement the type function (uint256) returns (uint256) for comparisons +// Warning 5729: (179-183): BMC does not yet implement this type of function call. +// Warning 5729: (187-191): BMC does not yet implement this type of function call. +// Warning 6328: (172-192): CHC: Assertion violation happens here. +// Warning 6328: (196-210): CHC: Assertion violation happens here. diff --git a/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol b/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol index 5ac20c96f869..fce586987795 100644 --- a/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol +++ b/test/libsolidity/smtCheckerTests/types/array_mapping_aliasing_2.sol @@ -45,4 +45,5 @@ contract C // Warning 6368: (850-866): CHC: Out of bounds access happens here. // Warning 6368: (850-869): CHC: Out of bounds access happens here. // Warning 6328: (936-956): CHC: Assertion violation happens here. -// Info 1391: CHC: 4 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. +// Warning 6368: (1029-1043): CHC: Out of bounds access might happen here. +// Info 1391: CHC: 3 verification condition(s) proved safe! Enable the model checker option "show proved safe" to see all of them. From 1b75d489c56ace26ccc18d2208ba1b881cb1e6dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 23 Jan 2024 16:39:18 +0100 Subject: [PATCH 063/189] More basic tests for memory guard --- .../memoryGuardTests/marked_empty.sol | 12 ++++++++++++ .../marked_with_memory_access.sol | 16 ++++++++++++++++ .../memoryGuardTests/unmarked_empty.sol | 12 ++++++++++++ .../unmarked_with_memory_access.sol | 8 -------- .../unmarked_with_memory_access_mstore.sol | 16 ++++++++++++++++ 5 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 test/libsolidity/memoryGuardTests/marked_empty.sol create mode 100644 test/libsolidity/memoryGuardTests/marked_with_memory_access.sol create mode 100644 test/libsolidity/memoryGuardTests/unmarked_empty.sol delete mode 100644 test/libsolidity/memoryGuardTests/unmarked_with_memory_access.sol create mode 100644 test/libsolidity/memoryGuardTests/unmarked_with_memory_access_mstore.sol diff --git a/test/libsolidity/memoryGuardTests/marked_empty.sol b/test/libsolidity/memoryGuardTests/marked_empty.sol new file mode 100644 index 000000000000..faae617a2514 --- /dev/null +++ b/test/libsolidity/memoryGuardTests/marked_empty.sol @@ -0,0 +1,12 @@ +contract C { + constructor() { + assembly ("memory-safe") {} + } + + function f() public { + assembly ("memory-safe") {} + } +} +// ---- +// :C(creation) true +// :C(runtime) true diff --git a/test/libsolidity/memoryGuardTests/marked_with_memory_access.sol b/test/libsolidity/memoryGuardTests/marked_with_memory_access.sol new file mode 100644 index 000000000000..8ceb0b1a1f1f --- /dev/null +++ b/test/libsolidity/memoryGuardTests/marked_with_memory_access.sol @@ -0,0 +1,16 @@ +contract C { + constructor() { + assembly ("memory-safe") { + mstore(0, 1) + } + } + + function store() public { + assembly ("memory-safe") { + mstore(0, 1) + } + } +} +// ---- +// :C(creation) true +// :C(runtime) true diff --git a/test/libsolidity/memoryGuardTests/unmarked_empty.sol b/test/libsolidity/memoryGuardTests/unmarked_empty.sol new file mode 100644 index 000000000000..c87e88936a3c --- /dev/null +++ b/test/libsolidity/memoryGuardTests/unmarked_empty.sol @@ -0,0 +1,12 @@ +contract C { + constructor() { + assembly {} + } + + function f() public { + assembly {} + } +} +// ---- +// :C(creation) true +// :C(runtime) true diff --git a/test/libsolidity/memoryGuardTests/unmarked_with_memory_access.sol b/test/libsolidity/memoryGuardTests/unmarked_with_memory_access.sol deleted file mode 100644 index 88f826fdb944..000000000000 --- a/test/libsolidity/memoryGuardTests/unmarked_with_memory_access.sol +++ /dev/null @@ -1,8 +0,0 @@ -contract C { - function f() public pure { - assembly { mstore(0,0) } - } -} -// ---- -// :C(creation) true -// :C(runtime) false diff --git a/test/libsolidity/memoryGuardTests/unmarked_with_memory_access_mstore.sol b/test/libsolidity/memoryGuardTests/unmarked_with_memory_access_mstore.sol new file mode 100644 index 000000000000..3cf05a79169c --- /dev/null +++ b/test/libsolidity/memoryGuardTests/unmarked_with_memory_access_mstore.sol @@ -0,0 +1,16 @@ +contract C { + constructor() { + assembly { + mstore(0, 1) + } + } + + function store() public { + assembly { + mstore(0, 1) + } + } +} +// ---- +// :C(creation) false +// :C(runtime) false From 1a8020260d3598e525ae9c0640648f4fc506f024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 23 Jan 2024 16:39:13 +0100 Subject: [PATCH 064/189] A few extra optimizer tests for MSTORE --- .../equalStoreEliminator/mstore8_mstore8.yul | 21 ++++++++++++++ .../equalStoreEliminator/mstore_mstore.yul | 20 +++++++++++++ .../mstore_overwriting_mstore.yul | 28 +++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 test/libyul/yulOptimizerTests/equalStoreEliminator/mstore8_mstore8.yul create mode 100644 test/libyul/yulOptimizerTests/equalStoreEliminator/mstore_mstore.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mstore_overwriting_mstore.yul diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/mstore8_mstore8.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/mstore8_mstore8.yul new file mode 100644 index 000000000000..e907a3017048 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/mstore8_mstore8.yul @@ -0,0 +1,21 @@ +{ + let _0 := 0 + let _31 := 31 + + calldatacopy(0, 0, 0x40) + let mem31 := mload(_31) + + mstore8(_0, mem31) + mstore8(_0, mem31) // Redundant with previous MSTORE8. +} +// ---- +// step: equalStoreEliminator +// +// { +// let _0 := 0 +// let _31 := 31 +// calldatacopy(0, 0, 0x40) +// let mem31 := mload(_31) +// mstore8(_0, mem31) +// mstore8(_0, mem31) +// } diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/mstore_mstore.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/mstore_mstore.yul new file mode 100644 index 000000000000..b133a68f6687 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/mstore_mstore.yul @@ -0,0 +1,20 @@ +{ + let _0 := 0 + let _32 := 32 + + calldatacopy(0, 0, 0x40) + let mem32 := mload(_32) + + mstore(_0, mem32) // Redundant. Overwritten by MSTORE. + mstore(_0, mem32) +} +// ---- +// step: equalStoreEliminator +// +// { +// let _0 := 0 +// let _32 := 32 +// calldatacopy(0, 0, 0x40) +// let mem32 := mload(_32) +// mstore(_0, mem32) +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mstore_overwriting_mstore.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mstore_overwriting_mstore.yul new file mode 100644 index 000000000000..83208b8eede7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mstore_overwriting_mstore.yul @@ -0,0 +1,28 @@ +{ + calldatacopy(0, 0, 0x20) + + let _0 := 0 + let _42 := 42 + let _123 := 123 + + mstore(_0, _42) // Redundant. MSTORE overwrites it. + mstore(_0, _123) + + return(0, 0x20) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// let _1 := 0x20 +// let _2 := 0 +// let _3 := 0 +// let _0 := 0 +// let _42 := 42 +// mstore(_0, 123) +// return(0, 0x20) +// } +// } From bd762785eb568d533bfc0a8d898e4bc2b95486b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 8 Jan 2024 19:02:04 +0100 Subject: [PATCH 065/189] Yul builtin for MCOPY --- Changelog.md | 1 + docs/grammar/SolidityLexer.g4 | 2 +- docs/using-the-compiler.rst | 1 + docs/yul.rst | 2 + libevmasm/GasMeter.cpp | 10 ++ libevmasm/Instruction.cpp | 2 + libevmasm/Instruction.h | 1 + libevmasm/SemanticInformation.cpp | 22 +++- libevmasm/SimplificationRule.h | 1 + liblangutil/EVMVersion.cpp | 2 + liblangutil/EVMVersion.h | 1 + libyul/AsmAnalysis.cpp | 3 + libyul/backends/evm/EVMDialect.cpp | 10 +- libyul/optimiser/UnusedStoreEliminator.cpp | 3 + scripts/test_antlr_grammar.sh | 2 + test/libsolidity/GasMeter.cpp | 108 ++++++++++++++++++ .../unmarked_with_memory_access_mcopy.sol | 18 +++ ...ked_with_memory_access_mcopy_zero_size.sol | 18 +++ .../semanticTests/inlineAssembly/mcopy.sol | 14 +++ .../mcopy_as_identifier_pre_cancun.sol | 22 ++++ .../inlineAssembly/mcopy_empty.sol | 18 +++ .../inlineAssembly/mcopy_overlap.sol | 39 +++++++ .../syntaxTests/inlineAssembly/mcopy.sol | 10 ++ .../inlineAssembly/mcopy_pre_cancun.sol | 11 ++ .../inlineAssembly/mcopy_reserved_cancun.sol | 21 ++++ ...embly_instructions_allowed_pure_cancun.sol | 9 ++ ...embly_instructions_allowed_view_cancun.sol | 1 + .../viewPureChecker/mcopy_pure.sol | 9 ++ test/libyul/yulInterpreterTests/mcopy.yul | 34 ++++++ .../mcopy_memory_access_out_of_range.yul | 21 ++++ .../mcopy_memory_expansion_on_read.yul | 12 ++ .../mcopy_memory_expansion_on_write.yul | 13 +++ .../mcopy_memory_expansion_zero_size.yul | 13 +++ .../yulInterpreterTests/mcopy_overlap.yul | 21 ++++ .../equalStoreEliminator/mcopy.yul | 19 +++ .../equalStoreEliminator/mcopy_mstore.yul | 23 ++++ .../mcopy_mstore_overlap.yul | 29 +++++ .../equalStoreEliminator/mcopy_same_area.yul | 19 +++ .../mcopy_same_area_mcopy.yul | 21 ++++ .../mcopy_same_read_area_mcopy.yul | 23 ++++ .../mcopy_same_read_area_mstore.yul | 23 ++++ .../mcopy_same_read_area_mstore8_mstore8.yul | 27 +++++ .../mcopy_same_read_area_mstore_mstore.yul | 25 ++++ ...y_same_read_area_mstore_mstore_overlap.yul | 27 +++++ .../mcopy_same_write_area_mcopy.yul | 23 ++++ .../equalStoreEliminator/mstore_mcopy.yul | 23 ++++ .../mcopy_non_zero_size.sol | 22 ++++ .../expressionSimplifier/mcopy_zero_size.sol | 21 ++++ .../yulOptimizerTests/fullSuite/mcopy.sol | 23 ++++ .../fullSuite/mcopy_redundant_overwritten.sol | 21 ++++ .../fullSuite/mcopy_redundant_same_area.sol | 19 +++ .../fullSuite/mcopy_redundant_zero_size.sol | 19 +++ .../mcopy_mcopy_no_overlap.yul | 31 +++++ .../mcopy_mstore_no_overlap.yul | 31 +++++ .../mcopy_non_overlapping_areas.yul | 24 ++++ .../mcopy_overlapping_areas.yul | 25 ++++ .../mcopy_overwriting_mcopy.yul | 26 +++++ .../mcopy_overwriting_mstore.yul | 27 +++++ .../mcopy_overwriting_mstore_overlap.yul | 32 ++++++ .../mcopy_reading_mcopy.yul | 31 +++++ .../mcopy_reading_mstore.yul | 32 ++++++ .../mcopy_reading_mstore8.yul | 34 ++++++ .../mcopy_reading_mstore_overhang.yul | 34 ++++++ .../mcopy_reading_mstore_overlap.yul | 36 ++++++ .../mcopy_reading_mstore_underhang.yul | 36 ++++++ ...ading_writing_overlapping_areas_mstore.yul | 34 ++++++ .../mcopy_reading_writing_same_area_mcopy.yul | 30 +++++ ...mcopy_reading_writing_same_area_mstore.yul | 34 ++++++ ...copy_reading_writing_same_area_mstore8.yul | 32 ++++++ ...ading_writing_same_area_mstore_overlap.yul | 34 ++++++ .../unusedStoreEliminator/mcopy_same_area.yul | 23 ++++ ...copy_same_write_area_overwriting_mcopy.yul | 28 +++++ .../unusedStoreEliminator/mcopy_sstore.yul | 32 ++++++ .../mload_reading_mcopy.yul | 32 ++++++ .../mstore_overwriting_mcopy.yul | 28 +++++ test/libyul/yulSyntaxTests/mcopy.yul | 5 + .../yulSyntaxTests/mcopy_as_identifier.yul | 14 +++ .../mcopy_as_identifier_pre_cancun.yul | 12 ++ .../yulSyntaxTests/mcopy_pre_cancun.yul | 12 ++ .../yulSyntaxTests/mcopy_wrong_args.yul | 13 +++ .../EVMInstructionInterpreter.cpp | 27 +++++ .../EVMInstructionInterpreter.h | 13 +++ 82 files changed, 1681 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/memoryGuardTests/unmarked_with_memory_access_mcopy.sol create mode 100644 test/libsolidity/memoryGuardTests/unmarked_with_memory_access_mcopy_zero_size.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/mcopy.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/mcopy_as_identifier_pre_cancun.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/mcopy_empty.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/mcopy_overlap.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/mcopy.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/mcopy_pre_cancun.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/mcopy_reserved_cancun.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_pure_cancun.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/mcopy_pure.sol create mode 100644 test/libyul/yulInterpreterTests/mcopy.yul create mode 100644 test/libyul/yulInterpreterTests/mcopy_memory_access_out_of_range.yul create mode 100644 test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_read.yul create mode 100644 test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_write.yul create mode 100644 test/libyul/yulInterpreterTests/mcopy_memory_expansion_zero_size.yul create mode 100644 test/libyul/yulInterpreterTests/mcopy_overlap.yul create mode 100644 test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy.yul create mode 100644 test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_mstore.yul create mode 100644 test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_mstore_overlap.yul create mode 100644 test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_area.yul create mode 100644 test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_area_mcopy.yul create mode 100644 test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mcopy.yul create mode 100644 test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mstore.yul create mode 100644 test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mstore8_mstore8.yul create mode 100644 test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mstore_mstore.yul create mode 100644 test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mstore_mstore_overlap.yul create mode 100644 test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_write_area_mcopy.yul create mode 100644 test/libyul/yulOptimizerTests/equalStoreEliminator/mstore_mcopy.yul create mode 100644 test/libyul/yulOptimizerTests/expressionSimplifier/mcopy_non_zero_size.sol create mode 100644 test/libyul/yulOptimizerTests/expressionSimplifier/mcopy_zero_size.sol create mode 100644 test/libyul/yulOptimizerTests/fullSuite/mcopy.sol create mode 100644 test/libyul/yulOptimizerTests/fullSuite/mcopy_redundant_overwritten.sol create mode 100644 test/libyul/yulOptimizerTests/fullSuite/mcopy_redundant_same_area.sol create mode 100644 test/libyul/yulOptimizerTests/fullSuite/mcopy_redundant_zero_size.sol create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_mcopy_no_overlap.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_mstore_no_overlap.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_non_overlapping_areas.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_overlapping_areas.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_overwriting_mcopy.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_overwriting_mstore.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_overwriting_mstore_overlap.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mcopy.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore8.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore_overhang.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore_overlap.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore_underhang.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_overlapping_areas_mstore.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_same_area_mcopy.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_same_area_mstore.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_same_area_mstore8.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_same_area_mstore_overlap.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_same_area.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_same_write_area_overwriting_mcopy.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_sstore.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mload_reading_mcopy.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/mstore_overwriting_mcopy.yul create mode 100644 test/libyul/yulSyntaxTests/mcopy.yul create mode 100644 test/libyul/yulSyntaxTests/mcopy_as_identifier.yul create mode 100644 test/libyul/yulSyntaxTests/mcopy_as_identifier_pre_cancun.yul create mode 100644 test/libyul/yulSyntaxTests/mcopy_pre_cancun.yul create mode 100644 test/libyul/yulSyntaxTests/mcopy_wrong_args.yul diff --git a/Changelog.md b/Changelog.md index 19b71804a2fe..ea9cd13676c0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Language Features: * Introduce global function ``blobhash(uint)`` for retrieving versioned hashes of blobs, akin to the homonymous Yul builtin. * Yul: Introduce builtin ``blobbasefee()`` for retrieving the blob base fee of the current block. * Yul: Introduce builtin ``blobhash()`` for retrieving versioned hashes of blobs associated with the transaction. + * Yul: Introduce builtin ``mcopy()`` for cheaply copying data between memory areas. Compiler Features: * EVM: Support for the EVM Version "Cancun". diff --git a/docs/grammar/SolidityLexer.g4 b/docs/grammar/SolidityLexer.g4 index b553c3b79c17..91d6b4177434 100644 --- a/docs/grammar/SolidityLexer.g4 +++ b/docs/grammar/SolidityLexer.g4 @@ -301,7 +301,7 @@ YulEVMBuiltin: | 'pop' | 'mload' | 'mstore' | 'mstore8' | 'sload' | 'sstore' | 'msize' | 'gas' | 'address' | 'balance' | 'selfbalance' | 'caller' | 'callvalue' | 'calldataload' | 'calldatasize' | 'calldatacopy' | 'extcodesize' | 'extcodecopy' | 'returndatasize' - | 'returndatacopy' | 'extcodehash' | 'create' | 'create2' | 'call' | 'callcode' + | 'returndatacopy' | 'mcopy' | 'extcodehash' | 'create' | 'create2' | 'call' | 'callcode' | 'delegatecall' | 'staticcall' | 'return' | 'revert' | 'selfdestruct' | 'invalid' | 'log0' | 'log1' | 'log2' | 'log3' | 'log4' | 'chainid' | 'origin' | 'gasprice' | 'blockhash' | 'blobhash' | 'coinbase' | 'timestamp' | 'number' | 'difficulty' diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 93bdff8661bf..bb495c8155c5 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -179,6 +179,7 @@ at each version. Backward compatibility is not guaranteed between each version. - ``cancun`` - The block's blob base fee (`EIP-7516 `_ and `EIP-4844 `_) can be accessed via the global ``block.blobbasefee`` or ``blobbasefee()`` in inline assembly. - 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 `_). .. index:: ! standard JSON, ! --standard-json .. _compiler-api: diff --git a/docs/yul.rst b/docs/yul.rst index 2f0dc19dc8b9..482e80ea134c 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -865,6 +865,8 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a +-------------------------+-----+---+-----------------------------------------------------------------+ | returndatacopy(t, f, s) | `-` | B | copy s bytes from returndata at position f to mem at position t | +-------------------------+-----+---+-----------------------------------------------------------------+ +| mcopy(t, f, s) | `-` | N | copy s bytes from mem at position f to mem at position t | ++-------------------------+-----+---+-----------------------------------------------------------------+ | extcodehash(a) | | C | code hash of address a | +-------------------------+-----+---+-----------------------------------------------------------------+ | create(v, p, n) | | F | create new contract with code mem[p...(p+n)) and send v wei | diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index 271a6ce2de3c..6594d1ff6c2f 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -115,6 +115,16 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ gas += memoryGas(0, -2); gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2)); break; + case Instruction::MCOPY: + { + GasConsumption memoryGasFromRead = memoryGas(-1, -2); + GasConsumption memoryGasFromWrite = memoryGas(0, -2); + + gas = runGas(_item.instruction(), m_evmVersion); + gas += (memoryGasFromRead < memoryGasFromWrite ? memoryGasFromWrite : memoryGasFromRead); + gas += wordGas(GasCosts::copyGas, m_state->relativeStackElement(-2)); + break; + } case Instruction::EXTCODESIZE: gas = GasCosts::extCodeGas(m_evmVersion); break; diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index 611b17647e97..c00bc3542151 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -70,6 +70,7 @@ std::map const solidity::evmasm::c_instructions = { "EXTCODECOPY", Instruction::EXTCODECOPY }, { "RETURNDATASIZE", Instruction::RETURNDATASIZE }, { "RETURNDATACOPY", Instruction::RETURNDATACOPY }, + { "MCOPY", Instruction::MCOPY }, { "EXTCODEHASH", Instruction::EXTCODEHASH }, { "BLOCKHASH", Instruction::BLOCKHASH }, { "BLOBHASH", Instruction::BLOBHASH }, @@ -222,6 +223,7 @@ static std::map const c_instructionInfo = { Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, true, Tier::ExtCode } }, { Instruction::RETURNDATASIZE, {"RETURNDATASIZE", 0, 0, 1, false, Tier::Base } }, { Instruction::RETURNDATACOPY, {"RETURNDATACOPY", 0, 3, 0, true, Tier::VeryLow } }, + { Instruction::MCOPY, { "MCOPY", 0, 3, 0, true, Tier::VeryLow } }, { Instruction::EXTCODEHASH, { "EXTCODEHASH", 0, 1, 1, false, Tier::Balance } }, { Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, Tier::Ext } }, { Instruction::BLOBHASH, { "BLOBHASH", 0, 1, 1, false, Tier::VeryLow } }, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index f05dcb9f63f8..4e7ac8b97ff0 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -104,6 +104,7 @@ enum class Instruction: uint8_t MSIZE, ///< get the size of active memory GAS, ///< get the amount of available gas JUMPDEST, ///< set a potential jump destination + MCOPY = 0x5e, ///< copy between memory areas PUSH0 = 0x5f, ///< place the value 0 on stack PUSH1 = 0x60, ///< place 1 byte item on stack diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 346dcb0d4d33..5c8b986be90d 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -104,6 +104,25 @@ std::vector SemanticInformation::readWriteOperat op.lengthParameter = 2; return {op}; } + case Instruction::MCOPY: + { + assertThrow(memory(_instruction) != Effect::None, OptimizerException, ""); + assertThrow(storage(_instruction) == Effect::None, OptimizerException, ""); + + Operation readOperation; + readOperation.effect = Read; + readOperation.location = Location::Memory; + readOperation.startParameter = 1; + readOperation.lengthParameter = 2; + + Operation writeOperation; + writeOperation.effect = Write; + writeOperation.location = Location::Memory; + writeOperation.startParameter = 0; + writeOperation.lengthParameter = 2; + + return {readOperation, writeOperation}; + } case Instruction::STATICCALL: case Instruction::CALL: case Instruction::CALLCODE: @@ -188,7 +207,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool )) return false; //@todo: We do not handle the following memory instructions for now: - // calldatacopy, codecopy, extcodecopy, mstore8, + // calldatacopy, codecopy, extcodecopy, mcopy, mstore8, // msize (note that msize also depends on memory read access) // the second requirement will be lifted once it is implemented @@ -363,6 +382,7 @@ SemanticInformation::Effect SemanticInformation::memory(Instruction _instruction case Instruction::CODECOPY: case Instruction::EXTCODECOPY: case Instruction::RETURNDATACOPY: + case Instruction::MCOPY: case Instruction::MSTORE: case Instruction::MSTORE8: case Instruction::CALL: diff --git a/libevmasm/SimplificationRule.h b/libevmasm/SimplificationRule.h index b6d906bc1439..746dfa4c67d1 100644 --- a/libevmasm/SimplificationRule.h +++ b/libevmasm/SimplificationRule.h @@ -117,6 +117,7 @@ struct EVMBuiltins static auto constexpr EXTCODECOPY = PatternGenerator{}; static auto constexpr RETURNDATASIZE = PatternGenerator{}; static auto constexpr RETURNDATACOPY = PatternGenerator{}; + static auto constexpr MCOPY = PatternGenerator{}; static auto constexpr EXTCODEHASH = PatternGenerator{}; static auto constexpr BLOCKHASH = PatternGenerator{}; static auto constexpr BLOBHASH = PatternGenerator{}; diff --git a/liblangutil/EVMVersion.cpp b/liblangutil/EVMVersion.cpp index 6fe2bbb1f4f1..34996dc6b98c 100644 --- a/liblangutil/EVMVersion.cpp +++ b/liblangutil/EVMVersion.cpp @@ -53,6 +53,8 @@ bool EVMVersion::hasOpcode(Instruction _opcode) const return hasBlobHash(); case Instruction::BLOBBASEFEE: return hasBlobBaseFee(); + case Instruction::MCOPY: + return hasMcopy(); default: return true; } diff --git a/liblangutil/EVMVersion.h b/liblangutil/EVMVersion.h index e59c5d3aa12c..5b4e73b15e7d 100644 --- a/liblangutil/EVMVersion.h +++ b/liblangutil/EVMVersion.h @@ -105,6 +105,7 @@ class EVMVersion: bool hasPrevRandao() const { return *this >= paris(); } bool hasPush0() const { return *this >= shanghai(); } bool hasBlobHash() const { return *this >= cancun(); } + bool hasMcopy() const { return *this >= cancun(); } bool hasOpcode(evmasm::Instruction _opcode) const; diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 834a7549a502..c4dade689830 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -736,6 +736,9 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio else if (_instr == evmasm::Instruction::BLOBHASH && !m_evmVersion.hasBlobHash()) // TODO: Change this assertion to an error, similar to the ones above, when Cancun becomes the default EVM version. yulAssert(false); + else if (_instr == evmasm::Instruction::MCOPY && !m_evmVersion.hasMcopy()) + // TODO: Change this assertion to an error, similar to the ones above, when Cancun becomes the default EVM version. + yulAssert(false); else if (_instr == evmasm::Instruction::PC) m_errorReporter.error( 2450_error, diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 6b34f60c74aa..36e6f1d0078d 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -127,6 +127,13 @@ std::set createReservedIdentifiers(langutil::EVMVersion _evmVersion) return _instr == evmasm::Instruction::BLOBBASEFEE && _evmVersion < langutil::EVMVersion::cancun(); }; + // TODO remove this in 0.9.0. We allow creating functions or identifiers in Yul with the name + // mcopy for VMs before london. + auto mcopyException = [&](evmasm::Instruction _instr) -> bool + { + return _instr == evmasm::Instruction::MCOPY && _evmVersion < langutil::EVMVersion::cancun(); + }; + // TODO remove this in 0.9.0. We allow creating functions or identifiers in Yul with the name // prevrandao for VMs before paris. auto prevRandaoException = [&](std::string const& _instrName) -> bool @@ -150,7 +157,8 @@ std::set createReservedIdentifiers(langutil::EVMVersion _evmVersion) !baseFeeException(instr.second) && !prevRandaoException(name) && !blobHashException(instr.second) && - !blobBaseFeeException(instr.second) + !blobBaseFeeException(instr.second) && + !mcopyException(instr.second) ) reserved.emplace(name); } diff --git a/libyul/optimiser/UnusedStoreEliminator.cpp b/libyul/optimiser/UnusedStoreEliminator.cpp index d88b07f6b158..95296369b77d 100644 --- a/libyul/optimiser/UnusedStoreEliminator.cpp +++ b/libyul/optimiser/UnusedStoreEliminator.cpp @@ -172,9 +172,12 @@ void UnusedStoreEliminator::visit(Statement const& _statement) *instruction == Instruction::CODECOPY || *instruction == Instruction::CALLDATACOPY || *instruction == Instruction::RETURNDATACOPY || + // TODO: Removing MCOPY is complicated because it's not just a store but also a load. + //*instruction == Instruction::MCOPY || *instruction == Instruction::MSTORE || *instruction == Instruction::MSTORE8; bool isCandidateForRemoval = + *instruction != Instruction::MCOPY && SemanticInformation::otherState(*instruction) != SemanticInformation::Write && ( SemanticInformation::storage(*instruction) == SemanticInformation::Write || (!m_ignoreMemory && SemanticInformation::memory(*instruction) == SemanticInformation::Write) diff --git a/scripts/test_antlr_grammar.sh b/scripts/test_antlr_grammar.sh index ea00cc0f6e0e..d21228c5be3f 100755 --- a/scripts/test_antlr_grammar.sh +++ b/scripts/test_antlr_grammar.sh @@ -123,6 +123,8 @@ done < <( grep -v -E 'inlineAssembly/basefee_berlin_function.sol' | # Skipping a test with "let blobbasefee := ..." grep -v -E 'inlineAssembly/blobbasefee_shanghai_function.sol' | + # Skipping a test with "let mcopy := ..." + grep -v -E 'inlineAssembly/mcopy_as_identifier_pre_cancun.sol' | # Skipping tests with "let prevrandao := ..." grep -v -E 'inlineAssembly/prevrandao_allowed_function_pre_paris.sol' | grep -v -E 'inlineAssembly/prevrandao_disallowed_function_post_paris.sol' | diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index a0599f552a73..1afebac5c5c8 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -32,6 +32,7 @@ using namespace solidity::langutil; using namespace solidity::evmasm; using namespace solidity::frontend; using namespace solidity::frontend::test; +using namespace solidity::test; namespace solidity::frontend::test { @@ -339,6 +340,113 @@ BOOST_AUTO_TEST_CASE(complex_control_flow) testRunTimeGas("ln(int128)", std::vector{encodeArgs(0), encodeArgs(10), encodeArgs(105), encodeArgs(30000)}); } +BOOST_AUTO_TEST_CASE( + mcopy_memory_expansion_gas, + *boost::unit_test::precondition(minEVMVersionCheck(EVMVersion::cancun())) +) +{ + char const* sourceCode = R"( + contract C { + function no_expansion() public { + assembly { + mstore(0xffe0, 1) // expand memory before using mcopy + mcopy(0, 0xffff, 1) + return(0, 1) + } + } + + function expansion_on_write() public { + assembly { + mcopy(0xffff, 0, 1) + return(0xffff, 1) + } + } + + function expansion_on_read() public { + assembly { + mcopy(0, 0xffff, 1) + return(0, 1) + } + } + + function expansion_on_read_write() public { + assembly { + mcopy(0xffff, 0xffff, 1) + return(0, 1) + } + } + + function expansion_on_zero_size() public { + assembly { + mcopy(0xffff, 0xffff, 0) + return(0, 1) + } + } + + function expansion_on_0_0_0() public { + assembly { + mcopy(0, 0, 0) + return(0, 1) + } + } + } + )"; + testCreationTimeGas(sourceCode); + testRunTimeGas("no_expansion()", {encodeArgs()}); + testRunTimeGas("expansion_on_write()", {encodeArgs()}); + testRunTimeGas("expansion_on_read()", {encodeArgs()}); + testRunTimeGas("expansion_on_read_write()", {encodeArgs()}); + testRunTimeGas("expansion_on_zero_size()", {encodeArgs()}); + testRunTimeGas("expansion_on_0_0_0()", {encodeArgs()}); +} + +BOOST_AUTO_TEST_CASE( + mcopy_word_gas, + *boost::unit_test::precondition(minEVMVersionCheck(EVMVersion::cancun())) +) +{ + char const* sourceCode = R"( + contract C { + function no_overlap() public { + assembly { + mstore(0xffe0, 1) // expand memory before using mcopy + mcopy(0x4000, 0x2000, 0x2000) + return(0, 0x10000) + } + } + + function overlap_right() public { + assembly { + mstore(0xffe0, 1) // expand memory before using mcopy + mcopy(0x3000, 0x2000, 0x2000) + return(0, 0x10000) + } + } + + function overlap_left() public { + assembly { + mstore(0xffe0, 1) // expand memory before using mcopy + mcopy(0x1000, 0x2000, 0x2000) + return(0, 0x10000) + } + } + + function overlap_full() public { + assembly { + mstore(0xffe0, 1) // expand memory before using mcopy + mcopy(0x2000, 0x2000, 0x2000) + return(0, 0x10000) + } + } + } + )"; + testCreationTimeGas(sourceCode); + testRunTimeGas("no_overlap()", {encodeArgs()}); + testRunTimeGas("overlap_right()", {encodeArgs()}); + testRunTimeGas("overlap_left()", {encodeArgs()}); + testRunTimeGas("overlap_full()", {encodeArgs()}); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/memoryGuardTests/unmarked_with_memory_access_mcopy.sol b/test/libsolidity/memoryGuardTests/unmarked_with_memory_access_mcopy.sol new file mode 100644 index 000000000000..075cfb6ff560 --- /dev/null +++ b/test/libsolidity/memoryGuardTests/unmarked_with_memory_access_mcopy.sol @@ -0,0 +1,18 @@ +contract C { + constructor() { + assembly { + mcopy(1000, 2000, 1000) + } + } + + function copy() public { + assembly { + mcopy(1000, 2000, 1000) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// :C(creation) false +// :C(runtime) false diff --git a/test/libsolidity/memoryGuardTests/unmarked_with_memory_access_mcopy_zero_size.sol b/test/libsolidity/memoryGuardTests/unmarked_with_memory_access_mcopy_zero_size.sol new file mode 100644 index 000000000000..5ef77fcc6925 --- /dev/null +++ b/test/libsolidity/memoryGuardTests/unmarked_with_memory_access_mcopy_zero_size.sol @@ -0,0 +1,18 @@ +contract C { + constructor() { + assembly { + mcopy(1000, 2000, 0) + } + } + + function copy() public { + assembly { + mcopy(1000, 2000, 0) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// :C(creation) false +// :C(runtime) false diff --git a/test/libsolidity/semanticTests/inlineAssembly/mcopy.sol b/test/libsolidity/semanticTests/inlineAssembly/mcopy.sol new file mode 100644 index 000000000000..e98409f3d4e1 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/mcopy.sol @@ -0,0 +1,14 @@ +contract C { + function f(bytes memory src) public pure returns (bytes memory dst) { + assembly { + mcopy(add(dst, 0x1f), add(src, 0x1f), 0x01) // Copy over src length byte to dst + mcopy(add(dst, 0x20), add(src, 0x00), 0x08) // Copy 8 zero bytes to dst + mcopy(add(dst, 0x28), add(src, 0x28), 0x10) // Copy 16 bytes from the middle of src to dst + mcopy(add(dst, 0x38), add(src, 0x00), 0x08) // Copy 8 zero bytes to dst + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// f(bytes): 0x20, 0x20, 0xffeeddccbbaa9988776655443322110000112233445566778899aabbccddeeff -> 0x20, 0x20, 0x0000000000000000776655443322110000112233445566770000000000000000 diff --git a/test/libsolidity/semanticTests/inlineAssembly/mcopy_as_identifier_pre_cancun.sol b/test/libsolidity/semanticTests/inlineAssembly/mcopy_as_identifier_pre_cancun.sol new file mode 100644 index 000000000000..4f50d1ddc263 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/mcopy_as_identifier_pre_cancun.sol @@ -0,0 +1,22 @@ +contract C { + function f() public pure returns (uint result) { + assembly { + let mcopy := 1 + result := mcopy + } + } + + function g() public pure returns (uint result) { + assembly { + function mcopy() -> r { + r := 1000 + } + result := mcopy() + } + } +} +// ==== +// EVMVersion: 1 +// g() -> 1000 diff --git a/test/libsolidity/semanticTests/inlineAssembly/mcopy_empty.sol b/test/libsolidity/semanticTests/inlineAssembly/mcopy_empty.sol new file mode 100644 index 000000000000..e34e443b3459 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/mcopy_empty.sol @@ -0,0 +1,18 @@ +contract C { + function mcopy_zero(bytes memory pattern) public pure returns (bytes memory out) { + out = pattern; + + // This should have no effect on output + assembly { + mcopy(add(out, 0x20), add(out, 0x30), 0) + mcopy(add(out, 0x30), add(out, 0x30), 0) + mcopy(add(out, 0x40), add(out, 0x30), 0) + + mcopy(add(out, 0x21), 0, 0) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// mcopy_zero(bytes): 0x20, 0x20, 0xffeeddccbbaa9988776655443322110000112233445566778899aabbccddeeff -> 0x20, 0x20, 0xffeeddccbbaa9988776655443322110000112233445566778899aabbccddeeff diff --git a/test/libsolidity/semanticTests/inlineAssembly/mcopy_overlap.sol b/test/libsolidity/semanticTests/inlineAssembly/mcopy_overlap.sol new file mode 100644 index 000000000000..95f517daa9e1 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/mcopy_overlap.sol @@ -0,0 +1,39 @@ +function copy(uint dstOffset, uint srcOffset, uint length) pure returns (bytes memory out) { + out = + hex"2222222222222222333333333333333344444444444444445555555555555555" + hex"6666666666666666777777777777777788888888888888889999999999999999" + hex"aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccccccdddddddddddddddd"; + assembly { + mcopy(add(add(out, 0x20), dstOffset), add(add(out, 0x20), srcOffset), length) + } +} + +contract C { + function mcopy_to_right_overlap() public pure returns (bytes memory) { + return copy(0x20, 0x10, 0x30); + } + + function mcopy_to_left_overlap() public pure returns (bytes memory) { + return copy(0x10, 0x20, 0x30); + } + + function mcopy_in_place() public pure returns (bytes memory) { + return copy(0x10, 0x10, 0x40); + } + + function mcopy_to_right_no_overlap() public pure returns (bytes memory) { + return copy(0x30, 0x10, 0x20); + } + + function mcopy_to_left_no_overlap() public pure returns (bytes memory) { + return copy(0x10, 0x30, 0x20); + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// mcopy_to_right_overlap() -> 0x20, 0x60, 0x2222222222222222333333333333333344444444444444445555555555555555, 0x4444444444444444555555555555555566666666666666667777777777777777, 0x88888888888888889999999999999999ccccccccccccccccdddddddddddddddd +// mcopy_to_left_overlap() -> 0x20, 0x60, 0x2222222222222222333333333333333366666666666666667777777777777777, 0x88888888888888889999999999999999aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb, 0xaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccccccdddddddddddddddd +// mcopy_in_place() -> 0x20, 0x60, 0x2222222222222222333333333333333344444444444444445555555555555555, 0x6666666666666666777777777777777788888888888888889999999999999999, 0xaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccccccdddddddddddddddd +// mcopy_to_right_no_overlap() -> 0x20, 0x60, 0x2222222222222222333333333333333344444444444444445555555555555555, 0x6666666666666666777777777777777744444444444444445555555555555555, 0x66666666666666667777777777777777ccccccccccccccccdddddddddddddddd +// mcopy_to_left_no_overlap() -> 0x20, 0x60, 0x2222222222222222333333333333333388888888888888889999999999999999, 0xaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb88888888888888889999999999999999, 0xaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccccccdddddddddddddddd diff --git a/test/libsolidity/syntaxTests/inlineAssembly/mcopy.sol b/test/libsolidity/syntaxTests/inlineAssembly/mcopy.sol new file mode 100644 index 000000000000..c300160ed313 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/mcopy.sol @@ -0,0 +1,10 @@ +contract C { + function f() public pure { + assembly { + mcopy(0, 0, 0) + mcopy(0x1000, 0x2000, 100) + } + } +} +// ==== +// EVMVersion: >=cancun diff --git a/test/libsolidity/syntaxTests/inlineAssembly/mcopy_pre_cancun.sol b/test/libsolidity/syntaxTests/inlineAssembly/mcopy_pre_cancun.sol new file mode 100644 index 000000000000..090e2f62862c --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/mcopy_pre_cancun.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + assembly { + mcopy() + } + } +} +// ==== +// EVMVersion: r { + r := 1000 + } + result := mcopy() + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// ParserError 5568: (101-106): Cannot use builtin function name "mcopy" as identifier name. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_pure_cancun.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_pure_cancun.sol new file mode 100644 index 000000000000..23a7f4c45949 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_pure_cancun.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + assembly { + mcopy(1, 2, 3) + } + } +} +// ==== +// EVMVersion: >=cancun diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol index f68c313286de..8aacecdec7ed 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol @@ -3,6 +3,7 @@ contract C { assembly { pop(blobhash(0)) pop(blobbasefee()) + mcopy(1, 2, 3) } } } diff --git a/test/libsolidity/syntaxTests/viewPureChecker/mcopy_pure.sol b/test/libsolidity/syntaxTests/viewPureChecker/mcopy_pure.sol new file mode 100644 index 000000000000..e89209dcbc84 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/mcopy_pure.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + assembly { + mcopy(0x1000, 0x2000, 100) + } + } +} +// ==== +// EVMVersion: >=cancun diff --git a/test/libyul/yulInterpreterTests/mcopy.yul b/test/libyul/yulInterpreterTests/mcopy.yul new file mode 100644 index 000000000000..7331544b8b1b --- /dev/null +++ b/test/libyul/yulInterpreterTests/mcopy.yul @@ -0,0 +1,34 @@ +{ + mstore(0x00, 0x1111111111111111111111111111111122222222222222222222222222222222) + mstore(0x20, 0x3333333333333333333333333333333344444444444444444444444444444444) + mstore(0x40, 0x5555555555555555555555555555555566666666666666666666666666666666) + + mcopy(0, 0, 32) // No-op + mcopy(0x60, 0, 32) // Append a duplicate of the first word past msize + mcopy(0x90, 0x30, 1) // Copy the 0x44 byte from the middle of second word past msize + mcopy(0, 0, 0) // No-op + mcopy(0x2f, 0x90, 2) // Copy the 0x4400 straddling msize back into the the middle of second word + mcopy(0xa0, 0, 160) // Duplicate the whole thing +} +// ==== +// EVMVersion: >=cancun +// ---- +// Trace: +// MCOPY(0, 0, 32) +// MCOPY(96, 0, 32) +// MCOPY(144, 48, 1) +// MCOPY(0, 0, 0) +// MCOPY(47, 144, 2) +// MCOPY(160, 0, 160) +// Memory dump: +// 0: 1111111111111111111111111111111122222222222222222222222222222222 +// 20: 3333333333333333333333333333334400444444444444444444444444444444 +// 40: 5555555555555555555555555555555566666666666666666666666666666666 +// 60: 1111111111111111111111111111111122222222222222222222222222222222 +// 80: 0000000000000000000000000000000044000000000000000000000000000000 +// A0: 1111111111111111111111111111111122222222222222222222222222222222 +// C0: 3333333333333333333333333333334400444444444444444444444444444444 +// E0: 5555555555555555555555555555555566666666666666666666666666666666 +// 100: 1111111111111111111111111111111122222222222222222222222222222222 +// 120: 0000000000000000000000000000000044000000000000000000000000000000 +// Storage dump: diff --git a/test/libyul/yulInterpreterTests/mcopy_memory_access_out_of_range.yul b/test/libyul/yulInterpreterTests/mcopy_memory_access_out_of_range.yul new file mode 100644 index 000000000000..45599109b5a3 --- /dev/null +++ b/test/libyul/yulInterpreterTests/mcopy_memory_access_out_of_range.yul @@ -0,0 +1,21 @@ +{ + mstore8(0xffffffffffffffff, 1) + + // Interpreter ignores memory copies with very large offset and/or size. + // None of these will show up in memory dump. + mcopy(0, 0xffffffffffffffff, 1) + mcopy(0xffffffffffffffff, 0, 1) + mcopy(0, 1, 0xffffffffffffffff) + mcopy(0xffffffff00000000, 0xffffffffffffffff, 0xffffffffffffffff) +} +// ==== +// EVMVersion: >=cancun +// ---- +// Trace: +// MCOPY(0, 0xffffffffffffffff, 1) +// MCOPY(0xffffffffffffffff, 0, 1) +// MCOPY(0, 1, 0xffffffffffffffff) +// MCOPY(0xffffffff00000000, 0xffffffffffffffff, 0xffffffffffffffff) +// Memory dump: +// FFFFFFFFFFFFFFE0: 0000000000000000000000000000000000000000000000000000000000000001 +// Storage dump: diff --git a/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_read.yul b/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_read.yul new file mode 100644 index 000000000000..b0b2b561e747 --- /dev/null +++ b/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_read.yul @@ -0,0 +1,12 @@ +{ + // Should not affect msize + mcopy(0x30, 0x30, 0) + sstore(0, msize()) +} +// ==== +// EVMVersion: >=cancun +// ---- +// Trace: +// MCOPY(48, 48, 0) +// Memory dump: +// Storage dump: diff --git a/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_write.yul b/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_write.yul new file mode 100644 index 000000000000..0448b20c0669 --- /dev/null +++ b/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_write.yul @@ -0,0 +1,13 @@ +{ + // Should expand memory to two full words (0x40 bytes) + mcopy(0x30, 0, 1) + sstore(0, msize()) +} +// ==== +// EVMVersion: >=cancun +// ---- +// Trace: +// MCOPY(48, 0, 1) +// Memory dump: +// Storage dump: +// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000040 diff --git a/test/libyul/yulInterpreterTests/mcopy_memory_expansion_zero_size.yul b/test/libyul/yulInterpreterTests/mcopy_memory_expansion_zero_size.yul new file mode 100644 index 000000000000..0448b20c0669 --- /dev/null +++ b/test/libyul/yulInterpreterTests/mcopy_memory_expansion_zero_size.yul @@ -0,0 +1,13 @@ +{ + // Should expand memory to two full words (0x40 bytes) + mcopy(0x30, 0, 1) + sstore(0, msize()) +} +// ==== +// EVMVersion: >=cancun +// ---- +// Trace: +// MCOPY(48, 0, 1) +// Memory dump: +// Storage dump: +// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000040 diff --git a/test/libyul/yulInterpreterTests/mcopy_overlap.yul b/test/libyul/yulInterpreterTests/mcopy_overlap.yul new file mode 100644 index 000000000000..2ac2460d7fe3 --- /dev/null +++ b/test/libyul/yulInterpreterTests/mcopy_overlap.yul @@ -0,0 +1,21 @@ +{ + mstore(0x00, 0x0000000000000000000000000000000000000000000000000000000000000000) + mstore(0x20, 0x1111111122222222333333334444444455555555666666667777777788888888) + mstore(0x40, 0x1111111122222222333333334444444455555555666666667777777788888888) + mstore(0x60, 0x0000000000000000000000000000000000000000000000000000000000000000) + + mcopy(0x08, 0x20, 0x20) + mcopy(0x58, 0x40, 0x20) +} +// ==== +// EVMVersion: >=cancun +// ---- +// Trace: +// MCOPY(8, 32, 32) +// MCOPY(88, 64, 32) +// Memory dump: +// 0: 0000000000000000111111112222222233333333444444445555555566666666 +// 20: 7777777788888888333333334444444455555555666666667777777788888888 +// 40: 1111111122222222333333334444444455555555666666661111111122222222 +// 60: 3333333344444444555555556666666677777777888888880000000000000000 +// Storage dump: diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy.yul new file mode 100644 index 000000000000..aaa36047a448 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy.yul @@ -0,0 +1,19 @@ +{ + calldatacopy(0, 0, 0x40) + + let _0 := 0 + let _32 := 32 + + mcopy(_0, _32, _32) // Not redundant. Read/write between different areas. +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: equalStoreEliminator +// +// { +// calldatacopy(0, 0, 0x40) +// let _0 := 0 +// let _32 := 32 +// mcopy(_0, _32, _32) +// } diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_mstore.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_mstore.yul new file mode 100644 index 000000000000..cb6364b83b9d --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_mstore.yul @@ -0,0 +1,23 @@ +{ + let _0 := 0 + let _32 := 32 + + calldatacopy(0, 0, 0x40) + let mem32 := mload(_32) + + mstore(_0, mem32) + mcopy(_0, _32, _32) // Redundant with MSTORE. +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: equalStoreEliminator +// +// { +// let _0 := 0 +// let _32 := 32 +// calldatacopy(0, 0, 0x40) +// let mem32 := mload(_32) +// mstore(_0, mem32) +// mcopy(_0, _32, _32) +// } diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_mstore_overlap.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_mstore_overlap.yul new file mode 100644 index 000000000000..4dbe80fe7dfe --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_mstore_overlap.yul @@ -0,0 +1,29 @@ +{ + let _0 := 0 + let _1 := 1 + let _31 := 31 + let _32 := 32 + let _33 := 33 + + calldatacopy(0, 0, 0x40) + let mem32 := mload(_32) + + mstore(_0, mem32) + mcopy(_1, _33, _31) // Not redundant. MCOPY does not copy all of it. +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: equalStoreEliminator +// +// { +// let _0 := 0 +// let _1 := 1 +// let _31 := 31 +// let _32 := 32 +// let _33 := 33 +// calldatacopy(0, 0, 0x40) +// let mem32 := mload(_32) +// mstore(_0, mem32) +// mcopy(_1, _33, _31) +// } diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_area.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_area.yul new file mode 100644 index 000000000000..d7e672f190c5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_area.yul @@ -0,0 +1,19 @@ +{ + let _0 := 0 + let _32 := 32 + + calldatacopy(0, 0, 0x20) + + mcopy(_0, _0, _32) // Redundant (no-op) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: equalStoreEliminator +// +// { +// let _0 := 0 +// let _32 := 32 +// calldatacopy(0, 0, 0x20) +// mcopy(_0, _0, _32) +// } diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_area_mcopy.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_area_mcopy.yul new file mode 100644 index 000000000000..2b11528fba72 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_area_mcopy.yul @@ -0,0 +1,21 @@ +{ + let _0 := 0 + let _32 := 32 + + calldatacopy(0, 0, 0x40) + + mcopy(_0, _32, _32) + mcopy(_0, _32, _32) // Redundant with previous MCOPY. +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: equalStoreEliminator +// +// { +// let _0 := 0 +// let _32 := 32 +// calldatacopy(0, 0, 0x40) +// mcopy(_0, _32, _32) +// mcopy(_0, _32, _32) +// } diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mcopy.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mcopy.yul new file mode 100644 index 000000000000..12cf27c8666b --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mcopy.yul @@ -0,0 +1,23 @@ +{ + let _0 := 0 + let _32 := 32 + let _64 := 64 + + calldatacopy(0, 0, 0x60) + + mcopy(_0, _64, _32) + mcopy(_32, _64, _32) // Not redundant. Writing to a different area. +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: equalStoreEliminator +// +// { +// let _0 := 0 +// let _32 := 32 +// let _64 := 64 +// calldatacopy(0, 0, 0x60) +// mcopy(_0, _64, _32) +// mcopy(_32, _64, _32) +// } diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mstore.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mstore.yul new file mode 100644 index 000000000000..9802bfb517b0 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mstore.yul @@ -0,0 +1,23 @@ +{ + let _0 := 0 + let _32 := 32 + + calldatacopy(0, 0, 0x40) + let mem32 := mload(_32) + + mstore(_0, mem32) + mcopy(_0, _0, _32) // Redundant (no-op) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: equalStoreEliminator +// +// { +// let _0 := 0 +// let _32 := 32 +// calldatacopy(0, 0, 0x40) +// let mem32 := mload(_32) +// mstore(_0, mem32) +// mcopy(_0, _0, _32) +// } diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mstore8_mstore8.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mstore8_mstore8.yul new file mode 100644 index 000000000000..8db2b800ce7e --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mstore8_mstore8.yul @@ -0,0 +1,27 @@ +{ + let _0 := 0 + let _1 := 1 + let _31 := 31 + + calldatacopy(0, 0, 0x40) + let mem31 := mload(_31) + + mstore8(_0, mem31) // Redundant. Overwritten by MSTORE. + mcopy(_0, _0, _1) // Redundant (no-op) + mstore8(_0, mem31) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: equalStoreEliminator +// +// { +// let _0 := 0 +// let _1 := 1 +// let _31 := 31 +// calldatacopy(0, 0, 0x40) +// let mem31 := mload(_31) +// mstore8(_0, mem31) +// mcopy(_0, _0, _1) +// mstore8(_0, mem31) +// } diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mstore_mstore.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mstore_mstore.yul new file mode 100644 index 000000000000..cc080803f15c --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mstore_mstore.yul @@ -0,0 +1,25 @@ +{ + let _0 := 0 + let _32 := 32 + + calldatacopy(0, 0, 0x40) + let mem32 := mload(_32) + + mstore(_0, mem32) + mcopy(_0, _0, _32) // Redundant (no-op) + mstore(_0, mem32) // Redundant with previous MSTORE. +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: equalStoreEliminator +// +// { +// let _0 := 0 +// let _32 := 32 +// calldatacopy(0, 0, 0x40) +// let mem32 := mload(_32) +// mstore(_0, mem32) +// mcopy(_0, _0, _32) +// mstore(_0, mem32) +// } diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mstore_mstore_overlap.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mstore_mstore_overlap.yul new file mode 100644 index 000000000000..639d026604f6 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_read_area_mstore_mstore_overlap.yul @@ -0,0 +1,27 @@ +{ + let _0 := 0 + let _1 := 1 + let _32 := 32 + + calldatacopy(0, 0, 0x20) + let mem32 := mload(_32) + + mstore(_0, mem32) + mcopy(_1, _1, _32) // Redundant (no-op) + mstore(_0, mem32) // Redundant with previous MSTORE. +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: equalStoreEliminator +// +// { +// let _0 := 0 +// let _1 := 1 +// let _32 := 32 +// calldatacopy(0, 0, 0x20) +// let mem32 := mload(_32) +// mstore(_0, mem32) +// mcopy(_1, _1, _32) +// mstore(_0, mem32) +// } diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_write_area_mcopy.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_write_area_mcopy.yul new file mode 100644 index 000000000000..e083572b4772 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/mcopy_same_write_area_mcopy.yul @@ -0,0 +1,23 @@ +{ + let _0 := 0 + let _32 := 32 + let _64 := 64 + + calldatacopy(0, 0, 0x60) + + mcopy(_0, _32, _32) + mcopy(_0, _64, _32) // Not redundant. Copying from a different spot. +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: equalStoreEliminator +// +// { +// let _0 := 0 +// let _32 := 32 +// let _64 := 64 +// calldatacopy(0, 0, 0x60) +// mcopy(_0, _32, _32) +// mcopy(_0, _64, _32) +// } diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/mstore_mcopy.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/mstore_mcopy.yul new file mode 100644 index 000000000000..fd35befcc823 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/mstore_mcopy.yul @@ -0,0 +1,23 @@ +{ + let _0 := 0 + let _32 := 32 + + calldatacopy(0, 0, 0x40) + let mem32 := mload(_32) + + mcopy(_0, _32, _32) + mstore(_0, mem32) // Redundant with MCOPY. +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: equalStoreEliminator +// +// { +// let _0 := 0 +// let _32 := 32 +// calldatacopy(0, 0, 0x40) +// let mem32 := mload(_32) +// mcopy(_0, _32, _32) +// mstore(_0, mem32) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/mcopy_non_zero_size.sol b/test/libyul/yulOptimizerTests/expressionSimplifier/mcopy_non_zero_size.sol new file mode 100644 index 000000000000..410b7df536b3 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/mcopy_non_zero_size.sol @@ -0,0 +1,22 @@ +{ + calldatacopy(0, 0, 0x60) + + mcopy(0x20, 0x40, 0x20) + + return(0, 0x60) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: expressionSimplifier +// +// { +// { +// let _1 := 0x60 +// let _2 := 0 +// calldatacopy(_2, _2, _1) +// let _4 := 0x20 +// mcopy(_4, 0x40, _4) +// return(_2, _1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/mcopy_zero_size.sol b/test/libyul/yulOptimizerTests/expressionSimplifier/mcopy_zero_size.sol new file mode 100644 index 000000000000..fe4420110561 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/mcopy_zero_size.sol @@ -0,0 +1,21 @@ +{ + calldatacopy(0, 0, 0x60) + + mcopy(0x20, 0x40, 0) // Equivalent to mcopy(0, 0, 0) + + return(0, 0x60) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: expressionSimplifier +// +// { +// { +// let _1 := 0x60 +// let _2 := 0 +// calldatacopy(_2, _2, _1) +// mcopy(0, 0, _2) +// return(_2, _1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/mcopy.sol b/test/libyul/yulOptimizerTests/fullSuite/mcopy.sol new file mode 100644 index 000000000000..b04852429f80 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/mcopy.sol @@ -0,0 +1,23 @@ +{ + calldatacopy(0, 0, 0x40) + + mcopy(0x20, 0, 0x20) // Not redundant. MCOPY reads it. + mcopy(0x40, 0x10, 0x30) + mstore(0x20, 42) + + return(0, 0x40) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: fullSuite +// +// { +// { +// calldatacopy(0, 0, 0x40) +// mcopy(0x20, 0, 0x20) +// mcopy(0x40, 0x10, 0x30) +// mstore(0x20, 42) +// return(0, 0x40) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/mcopy_redundant_overwritten.sol b/test/libyul/yulOptimizerTests/fullSuite/mcopy_redundant_overwritten.sol new file mode 100644 index 000000000000..252363de0ef7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/mcopy_redundant_overwritten.sol @@ -0,0 +1,21 @@ +{ + calldatacopy(0, 0, 0x40) + + mcopy(0, 0x20, 0x20) // Redundant. Overwritten by MSTORE. + mstore(0, 42) + + return(0, 0x40) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: fullSuite +// +// { +// { +// calldatacopy(0, 0, 0x40) +// mcopy(0, 0x20, 0x20) +// mstore(0, 42) +// return(0, 0x40) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/mcopy_redundant_same_area.sol b/test/libyul/yulOptimizerTests/fullSuite/mcopy_redundant_same_area.sol new file mode 100644 index 000000000000..a39f5b689b39 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/mcopy_redundant_same_area.sol @@ -0,0 +1,19 @@ +{ + calldatacopy(0, 0, 0x20) + + mcopy(0, 0, 0x20) // Redundant. Does not change any values. + + return(0, 0x20) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: fullSuite +// +// { +// { +// calldatacopy(0, 0, 0x20) +// mcopy(0, 0, 0x20) +// return(0, 0x20) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/mcopy_redundant_zero_size.sol b/test/libyul/yulOptimizerTests/fullSuite/mcopy_redundant_zero_size.sol new file mode 100644 index 000000000000..3ae7c2c31330 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/mcopy_redundant_zero_size.sol @@ -0,0 +1,19 @@ +{ + calldatacopy(0, 0, 0x40) + + mcopy(0, 0x20, 0) // Redundant. Does not copy anything. + + return(0, 0x40) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: fullSuite +// +// { +// { +// calldatacopy(0, 0, 0x40) +// mcopy(0, 0, 0) +// return(0, 0x40) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_mcopy_no_overlap.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_mcopy_no_overlap.yul new file mode 100644 index 000000000000..51b6da83fa02 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_mcopy_no_overlap.yul @@ -0,0 +1,31 @@ +{ + calldatacopy(0, 0, 0x80) + + let _0 := 0 + let _32 := 32 + let _64 := 64 + let _96 := 96 + + // Sanity check: independent MCOPY is not affected + mcopy(_0, _32, _32) // Not redundant. Not being overwritten. + mcopy(_64, _96, _32) + + return(0, 0x80) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x80) +// let _0 := 0 +// let _32 := 32 +// let _64 := 64 +// let _96 := 96 +// mcopy(_0, _32, _32) +// mcopy(_64, _96, _32) +// return(0, 0x80) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_mstore_no_overlap.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_mstore_no_overlap.yul new file mode 100644 index 000000000000..0ff179d4a4d2 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_mstore_no_overlap.yul @@ -0,0 +1,31 @@ +{ + calldatacopy(0, 0, 0x60) + + let _0 := 0 + let _32 := 32 + let _42 := 42 + let _64 := 64 + + // Sanity check: independent MCOPY and MSTORE are not affected + mcopy(_0, _32, _32) // Not redundant. Not being overwritten. + mstore(_64, _42) + + return(0, 0x60) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x60) +// let _0 := 0 +// let _32 := 32 +// let _42 := 42 +// let _64 := 64 +// mcopy(_0, _32, _32) +// mstore(_64, _42) +// return(0, 0x60) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_non_overlapping_areas.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_non_overlapping_areas.yul new file mode 100644 index 000000000000..48e1a4ec1ecb --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_non_overlapping_areas.yul @@ -0,0 +1,24 @@ +{ + calldatacopy(0, 0, 0x40) + + let _0 := 0 + let _32 := 32 + + mcopy(_0, _32, _32) // Not redundant. Not being overwritten. + + return(0, 0x40) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x40) +// let _0 := 0 +// let _32 := 32 +// mcopy(_0, _32, _32) +// return(0, 0x40) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_overlapping_areas.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_overlapping_areas.yul new file mode 100644 index 000000000000..70d67640a9c9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_overlapping_areas.yul @@ -0,0 +1,25 @@ +{ + calldatacopy(0, 0, 0x60) + + let _0 := 0 + let _32 := 32 + let _64 := 64 + + mcopy(_0, _32, _64) // Not redundant. Not being overwritten. + + return(0, 0x60) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x60) +// let _0 := 0 +// let _32 := 32 +// mcopy(_0, _32, 64) +// return(0, 0x60) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_overwriting_mcopy.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_overwriting_mcopy.yul new file mode 100644 index 000000000000..3334580fcfe2 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_overwriting_mcopy.yul @@ -0,0 +1,26 @@ +{ + calldatacopy(0, 0, 0x80) + + let _0 := 0 + let _64 := 64 + + mcopy(_0, _64, _64) // Redundant. MCOPY overwrites it. + mcopy(_0, _64, _64) + + return(0, 0x80) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x80) +// let _0 := 0 +// let _64 := 64 +// mcopy(_0, _64, _64) +// mcopy(_0, _64, _64) +// return(0, 0x80) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_overwriting_mstore.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_overwriting_mstore.yul new file mode 100644 index 000000000000..0c4490a44f94 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_overwriting_mstore.yul @@ -0,0 +1,27 @@ +{ + calldatacopy(0, 0, 0x40) + + let _0 := 0 + let _32 := 32 + let _42 := 42 + + mstore(_0, _42) // Redundant. MCOPY overwrites it. + mcopy(_0, _32, _32) + + return(0, 0x40) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x40) +// let _0 := 0 +// let _32 := 32 +// let _42 := 42 +// mcopy(_0, _32, _32) +// return(0, 0x40) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_overwriting_mstore_overlap.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_overwriting_mstore_overlap.yul new file mode 100644 index 000000000000..1c99063f5c94 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_overwriting_mstore_overlap.yul @@ -0,0 +1,32 @@ +{ + calldatacopy(0, 0, 0x60) + + let _0 := 0 + let _1 := 1 + let _32 := 32 + let _42 := 42 + let _64 := 64 + + mstore(_0, _42) // Not redundant. MCOPY does not overwrite all of it. + mcopy(_1, _64, _32) + + return(0, 0x60) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x60) +// let _0 := 0 +// let _1 := 1 +// let _32 := 32 +// let _42 := 42 +// let _64 := 64 +// mstore(_0, _42) +// mcopy(_1, _64, _32) +// return(0, 0x60) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mcopy.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mcopy.yul new file mode 100644 index 000000000000..96d69ffbba0f --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mcopy.yul @@ -0,0 +1,31 @@ +{ + calldatacopy(0, 0, 0x60) + + let _0 := 0 + let _32 := 32 + let _42 := 42 + let _64 := 64 + + mcopy(_32, _64, _32) // Not redundant. MCOPY reads it. + mcopy(_0, _32, _32) + mstore(_32, _42) + + return(0, 0x60) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x60) +// let _0 := 0 +// let _32 := 32 +// let _42 := 42 +// mcopy(_32, 64, _32) +// mcopy(_0, _32, _32) +// mstore(_32, _42) +// return(0, 0x60) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore.yul new file mode 100644 index 000000000000..d0c50bfbf1c1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore.yul @@ -0,0 +1,32 @@ +{ + calldatacopy(0, 0, 0x40) + + let _0 := 0 + let _32 := 32 + let _42 := 42 + let _123 := 123 + + mstore(_0, _42) // Not redundant. MCOPY reads it. + mcopy(_32, _0, _32) + mstore(_0, _123) + + return(0, 0x40) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x40) +// let _0 := 0 +// let _32 := 32 +// let _42 := 42 +// let _123 := 123 +// mstore(_0, _42) +// mcopy(_32, _0, _32) +// mstore(_0, _123) +// return(0, 0x40) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore8.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore8.yul new file mode 100644 index 000000000000..c6dcbc188c8a --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore8.yul @@ -0,0 +1,34 @@ +{ + calldatacopy(0, 0, 0x40) + + let _0 := 0 + let _1 := 1 + let _32 := 32 + let _42 := 42 + let _123 := 123 + + mstore8(_0, _42) // Not redundant. MCOPY reads it. + mcopy(_32, _0, _1) + mstore8(_0, _123) + + return(0, 0x40) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x40) +// let _0 := 0 +// let _1 := 1 +// let _32 := 32 +// let _42 := 42 +// let _123 := 123 +// mstore8(_0, _42) +// mcopy(_32, _0, _1) +// mstore8(_0, _123) +// return(0, 0x40) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore_overhang.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore_overhang.yul new file mode 100644 index 000000000000..98a77592ea1c --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore_overhang.yul @@ -0,0 +1,34 @@ +{ + calldatacopy(0, 0, 0x120) + + let _0 := 0 + let _32 := 32 + let _42 := 42 + let _96 := 96 + let _123 := 123 + + mstore(_32, _42) // Not redundant. MCOPY reads it. + mcopy(_96, _0, _96) + mstore(_32, _123) + + return(0, 0x120) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x120) +// let _0 := 0 +// let _32 := 32 +// let _42 := 42 +// let _96 := 96 +// let _123 := 123 +// mstore(_32, _42) +// mcopy(_96, _0, _96) +// mstore(_32, _123) +// return(0, 0x120) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore_overlap.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore_overlap.yul new file mode 100644 index 000000000000..32a1fbf4e1c1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore_overlap.yul @@ -0,0 +1,36 @@ +{ + calldatacopy(0, 0, 0x60) + + let _0 := 0 + let _31 := 31 + let _32 := 32 + let _42 := 42 + let _64 := 64 + let _123 := 123 + + mstore(_0, _42) // Not redundant. MCOPY reads part of it. + mcopy(_64, _31, _32) + mstore(_0, _123) + + return(0, 0x60) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x60) +// let _0 := 0 +// let _31 := 31 +// let _32 := 32 +// let _42 := 42 +// let _64 := 64 +// let _123 := 123 +// mstore(_0, _42) +// mcopy(_64, _31, _32) +// mstore(_0, _123) +// return(0, 0x60) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore_underhang.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore_underhang.yul new file mode 100644 index 000000000000..b339d2843460 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_mstore_underhang.yul @@ -0,0 +1,36 @@ +{ + calldatacopy(0, 0, 0x40) + + let _0 := 0 + let _1 := 1 + let _16 := 16 + let _32 := 32 + let _42 := 42 + let _123 := 123 + + mstore(_0, _42) // Not redundant. MCOPY reads part of it. + mcopy(_32, _16, _1) + mstore(_0, _123) + + return(0, 0x40) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x40) +// let _0 := 0 +// let _1 := 1 +// let _16 := 16 +// let _32 := 32 +// let _42 := 42 +// let _123 := 123 +// mstore(_0, _42) +// mcopy(_32, _16, _1) +// mstore(_0, _123) +// return(0, 0x40) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_overlapping_areas_mstore.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_overlapping_areas_mstore.yul new file mode 100644 index 000000000000..fa73f0171a33 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_overlapping_areas_mstore.yul @@ -0,0 +1,34 @@ +{ + calldatacopy(0, 0, 0x60) + + let _0 := 0 + let _32 := 32 + let _42 := 42 + let _64 := 64 + let _123 := 123 + + mstore(_32, _42) // Redundant. MCOPY overwrites it. + mcopy(_0, _32, _64) + mstore(_32, _123) + + return(0, 0x60) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x60) +// let _0 := 0 +// let _32 := 32 +// let _42 := 42 +// let _64 := 64 +// let _123 := 123 +// mstore(_32, _42) +// mcopy(_0, _32, _64) +// mstore(_32, _123) +// return(0, 0x60) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_same_area_mcopy.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_same_area_mcopy.yul new file mode 100644 index 000000000000..b7227913c15f --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_same_area_mcopy.yul @@ -0,0 +1,30 @@ +{ + calldatacopy(0, 0, 0x40) + + let _0 := 0 + let _32 := 32 + let _42 := 42 + + mcopy(_0, _32, _32) // Redundant. MSTORE overwrites it. + mcopy(_0, _0, _32) // Redundant (no-op) + mstore(_32, _42) + + return(0, 0x40) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x40) +// let _0 := 0 +// let _32 := 32 +// let _42 := 42 +// mcopy(_0, _32, _32) +// mcopy(_0, _0, _32) +// mstore(_32, _42) +// return(0, 0x40) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_same_area_mstore.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_same_area_mstore.yul new file mode 100644 index 000000000000..5edbc22b5f88 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_same_area_mstore.yul @@ -0,0 +1,34 @@ +{ + calldatacopy(0, 0, 0x20) + + let _0 := 0 + let _32 := 32 + let _42 := 42 + let _123 := 123 + + mstore(_0, _42) // Redundant. MSTORE overwrites it. + mcopy(_0, _0, _32) // Redundant (no-op) + mstore(_0, _123) + + return(0, 0x20) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// let _1 := 0x20 +// let _2 := 0 +// let _3 := 0 +// let _0 := 0 +// let _32 := 32 +// let _42 := 42 +// let _123 := 123 +// mstore(_0, _42) +// mcopy(_0, _0, _32) +// mstore(_0, _123) +// return(0, 0x20) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_same_area_mstore8.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_same_area_mstore8.yul new file mode 100644 index 000000000000..aab661f62bb9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_same_area_mstore8.yul @@ -0,0 +1,32 @@ +{ + calldatacopy(0, 0, 0x20) + + let _0 := 0 + let _1 := 1 + let _42 := 42 + let _123 := 123 + + mstore8(_0, _42) // Redundant. MSTORE8 overwrites it. + mcopy(_0, _0, _1) // Redundant (no-op) + mstore8(_0, _123) + + return(0, 0x20) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x20) +// let _0 := 0 +// let _1 := 1 +// let _42 := 42 +// let _123 := 123 +// mstore8(_0, _42) +// mcopy(_0, _0, _1) +// mstore8(_0, _123) +// return(0, 0x20) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_same_area_mstore_overlap.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_same_area_mstore_overlap.yul new file mode 100644 index 000000000000..36b4fc489ff9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_reading_writing_same_area_mstore_overlap.yul @@ -0,0 +1,34 @@ +{ + calldatacopy(0, 0, 0x40) + + let _0 := 0 + let _1 := 1 + let _32 := 32 + let _42 := 42 + let _123 := 123 + + mstore(_0, _42) // Redundant. MSTORE overwrites it. + mcopy(_1, _1, _32) // Redundant (no-op) + mstore(_0, _123) + + return(0, 0x40) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x40) +// let _0 := 0 +// let _1 := 1 +// let _32 := 32 +// let _42 := 42 +// let _123 := 123 +// mstore(_0, _42) +// mcopy(_1, _1, _32) +// mstore(_0, _123) +// return(0, 0x40) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_same_area.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_same_area.yul new file mode 100644 index 000000000000..1d0f86b86aa7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_same_area.yul @@ -0,0 +1,23 @@ +{ + calldatacopy(0, 0, 0x20) + + let _0 := 0 + let _32 := 32 + + mcopy(_0, _0, _32) // Redundant (no-op) + + return(0, 0x20) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x20) +// let _0 := 0 +// mcopy(_0, _0, 32) +// return(0, 0x20) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_same_write_area_overwriting_mcopy.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_same_write_area_overwriting_mcopy.yul new file mode 100644 index 000000000000..0e473a3599a7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_same_write_area_overwriting_mcopy.yul @@ -0,0 +1,28 @@ +{ + calldatacopy(0, 0, 0x60) + + let _0 := 0 + let _32 := 32 + let _64 := 64 + + mcopy(_0, _32, _32) // Redundant. MCOPY overwrites it. + mcopy(_0, _64, _32) + + return(0, 0x60) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x60) +// let _0 := 0 +// let _32 := 32 +// let _64 := 64 +// mcopy(_0, _32, _32) +// mcopy(_0, _64, _32) +// return(0, 0x60) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_sstore.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_sstore.yul new file mode 100644 index 000000000000..87fd89d3a750 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mcopy_sstore.yul @@ -0,0 +1,32 @@ +{ + calldatacopy(0, 0, 0x40) + + let _0 := 0 + let _32 := 32 + let _42 := 42 + let _123 := 123 + + // Sanity check: MCOPY does not affect storage + sstore(_0, _42) // Redundant. SSTORE overwrites it. + mcopy(_32, _0, _32) + sstore(_0, _123) + + return(0, 0x40) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x40) +// let _0 := 0 +// let _32 := 32 +// let _42 := 42 +// let _123 := 123 +// mcopy(_32, _0, _32) +// sstore(_0, _123) +// return(0, 0x40) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mload_reading_mcopy.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mload_reading_mcopy.yul new file mode 100644 index 000000000000..759e631f60bc --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mload_reading_mcopy.yul @@ -0,0 +1,32 @@ +{ + calldatacopy(0, 0, 0x40) + + let _0 := 0 + let _32 := 32 + let _42 := 42 + + mcopy(_0, _32, _32) // Not redundant. MLOAD reads it. + let mem0 := mload(_0) + mstore(_0, _42) + + sstore(_0, mem0) + return(0, 0x40) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x40) +// let _0 := 0 +// let _32 := 32 +// let _42 := 42 +// mcopy(_0, _32, _32) +// let mem0 := mload(_0) +// mstore(_0, _42) +// sstore(_0, mem0) +// return(0, 0x40) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/mstore_overwriting_mcopy.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mstore_overwriting_mcopy.yul new file mode 100644 index 000000000000..f32267a30c55 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/mstore_overwriting_mcopy.yul @@ -0,0 +1,28 @@ +{ + calldatacopy(0, 0, 0x40) + + let _0 := 0 + let _32 := 32 + let _42 := 42 + + mcopy(_0, _32, _32) // Redundant. MSTORE overwrites it. + mstore(_0, _42) + + return(0, 0x40) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// calldatacopy(0, 0, 0x40) +// let _0 := 0 +// let _32 := 32 +// let _42 := 42 +// mcopy(_0, _32, _32) +// mstore(_0, _42) +// return(0, 0x40) +// } +// } diff --git a/test/libyul/yulSyntaxTests/mcopy.yul b/test/libyul/yulSyntaxTests/mcopy.yul new file mode 100644 index 000000000000..1f5d042fed6e --- /dev/null +++ b/test/libyul/yulSyntaxTests/mcopy.yul @@ -0,0 +1,5 @@ +{ + mcopy(0x100, 0x200, 0x300) +} +// ==== +// EVMVersion: >=cancun diff --git a/test/libyul/yulSyntaxTests/mcopy_as_identifier.yul b/test/libyul/yulSyntaxTests/mcopy_as_identifier.yul new file mode 100644 index 000000000000..e48c27d06d81 --- /dev/null +++ b/test/libyul/yulSyntaxTests/mcopy_as_identifier.yul @@ -0,0 +1,14 @@ +{ + { + let mcopy := 1 + } + + { + function mcopy() {} + mcopy() + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// ParserError 5568: (20-25): Cannot use builtin function name "mcopy" as identifier name. diff --git a/test/libyul/yulSyntaxTests/mcopy_as_identifier_pre_cancun.yul b/test/libyul/yulSyntaxTests/mcopy_as_identifier_pre_cancun.yul new file mode 100644 index 000000000000..096fce4ed3bb --- /dev/null +++ b/test/libyul/yulSyntaxTests/mcopy_as_identifier_pre_cancun.yul @@ -0,0 +1,12 @@ +{ + { + let mcopy := 1 + } + + { + function mcopy() {} + mcopy() + } +} +// ==== +// EVMVersion: =cancun +// ---- +// TypeError 7000: (6-11): Function "mcopy" expects 3 arguments but got 4. +// TypeError 7000: (44-49): Function "mcopy" expects 3 arguments but got 2. +// TypeError 7000: (68-73): Function "mcopy" expects 3 arguments but got 1. +// TypeError 7000: (85-90): Function "mcopy" expects 3 arguments but got 0. diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index 0466d6f66984..12a046d70e74 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -87,6 +87,22 @@ void copyZeroExtended( _target[_targetOffset + i] = (_sourceOffset + i < _source.size() ? _source[_sourceOffset + i] : 0); } +void copyZeroExtendedWithOverlap( + std::map& _target, + std::map const& _source, + size_t _targetOffset, + size_t _sourceOffset, + size_t _size +) +{ + if (_targetOffset >= _sourceOffset) + for (size_t i = _size; i > 0; --i) + _target[_targetOffset + i - 1] = (_source.count(_sourceOffset + i - 1) != 0 ? _source.at(_sourceOffset + i - 1) : 0); + else + for (size_t i = 0; i < _size; ++i) + _target[_targetOffset + i] = (_source.count(_sourceOffset + i) != 0 ? _source.at(_sourceOffset + i) : 0); +} + } using u512 = boost::multiprecision::number>; @@ -262,6 +278,17 @@ u256 EVMInstructionInterpreter::eval( ); logTrace(_instruction, arg); return 0; + case Instruction::MCOPY: + if (accessMemory(arg[1], arg[2]) && accessMemory(arg[0], arg[2])) + copyZeroExtendedWithOverlap( + m_state.memory, + m_state.memory, + static_cast(arg[0]), + static_cast(arg[1]), + static_cast(arg[2]) + ); + logTrace(_instruction, arg); + return 0; case Instruction::BLOCKHASH: if (arg[0] >= m_state.blockNumber || arg[0] + 256 < m_state.blockNumber) return 0; diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.h b/test/tools/yulInterpreter/EVMInstructionInterpreter.h index 2f50217ed107..d3d221112991 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.h +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.h @@ -56,6 +56,19 @@ void copyZeroExtended( size_t _size ); +/// Copy @a _size bytes of @a _source at offset @a _sourceOffset to +/// @a _target at offset @a _targetOffset. Behaves as if @a _source would +/// continue with an infinite sequence of zero bytes beyond its end. +/// When target and source areas overlap, behaves as if the data was copied +/// using an intermediate buffer. +void copyZeroExtendedWithOverlap( + std::map& _target, + std::map const& _source, + size_t _targetOffset, + size_t _sourceOffset, + size_t _size +); + struct InterpreterState; /** From ba95a29744e8ad66a3d0afdfe119a7c03b3df68c Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Mon, 22 Jan 2024 15:27:58 +0100 Subject: [PATCH 066/189] Fix macOS dependency cache. --- .circleci/config.yml | 55 ++++++++++++++++++++++++++- .circleci/osx_install_dependencies.sh | 8 +--- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8a771e69596f..9a967febaa48 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -363,12 +363,63 @@ commands: - matrix_notify_failure_unless_pr install_dependencies_osx: + # An extra cache key is used to only save the flag that communicates whether the cache exists. + # if this flag was set (the cache exist) we remove all files located in /usr/local & /opt/homebrew. + # With this simple trick restoring the cache is much faster. Otherwise CircleCI is generating + # warning messages if a file from the cache is overwriting an already existing file on disk. + # Restoring the cache is much faster if we remove all potentially conflicting files beforehand. steps: - # FIXME: We used to cache dependencies on macOS but now it takes longer than just installing - # them each time. See https://github.com/ethereum/solidity/issues/12925. + - restore_cache: + keys: + - osx-dependencies-cached-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} + - run: + name: Installing dependencies / Restoring dependency cache + command: | + if [[ -f ~/osx-dependencies-cached ]]; then + echo "Dependency flag exists. Removing /usr/local/ and /opt/homebrew/. These directories will be restored from cache." + + # CircleCI is providing the circleci cli tools via some kind of symlink magic. + # So we just save the original symlinks and restore them later. + circleci_binary_path=$(command -v circleci) + circleci_agent_binary_path=$(command -v circleci-agent) + cp "${circleci_binary_path}" /tmp/circleci + cp "${circleci_agent_binary_path}" /tmp/circleci-agent + + # Homebrew is installed in /usr/local on intel macs, but in /opt/homebrew on apple silicon. + if [[ -d /opt/homebrew ]]; then + sudo rm -rf /opt/homebrew + sudo mkdir -p /opt/homebrew/bin + sudo chmod 777 /opt/{homebrew,homebrew/bin} + fi + # under macos /usr/local itself is read-only, so we just remove its sub-directories. + sudo rm -rf /usr/local/* + sudo mkdir -p /usr/local/bin + sudo chmod 777 /usr/{local,local/bin} + + mv /tmp/circleci "${circleci_binary_path}" + mv /tmp/circleci-agent "${circleci_agent_binary_path}" + fi + - restore_cache: + keys: + - osx-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: name: Install build dependencies command: .circleci/osx_install_dependencies.sh + - run: + name: Mark dependencies as cached + command: touch ~/osx-dependencies-cached + - save_cache: + key: osx-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 + - save_cache: + key: osx-dependencies-cached-{{ arch }}-{{ checksum ".circleci/osx_install_dependencies.sh" }} + paths: + - ~/osx-dependencies-cached defaults: diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh index 3e3f3b1bbd80..8b8656abd783 100755 --- a/.circleci/osx_install_dependencies.sh +++ b/.circleci/osx_install_dependencies.sh @@ -26,12 +26,8 @@ # ------------------------------------------------------------------------------ # note that the following directories may be cached by circleci: -# - /usr/local/bin -# - /usr/local/sbin -# - /usr/local/lib -# - /usr/local/include -# - /usr/local/Cellar -# - /usr/local/Homebrew +# - /usr/local +# - /opt/homebrew set -eu From 89b73b61fe7fcc1de88f2f094908255f24fd9848 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Fri, 12 Jan 2024 18:25:43 -0300 Subject: [PATCH 067/189] Introduce transient storage support for Yul and inline assembly. --- Changelog.md | 3 + docs/grammar/SolidityLexer.g4 | 2 +- docs/using-the-compiler.rst | 1 + docs/yul.rst | 7 +- libevmasm/GasMeter.cpp | 15 ++-- libevmasm/Instruction.cpp | 4 ++ libevmasm/Instruction.h | 4 ++ libevmasm/SemanticInformation.cpp | 55 ++++++++++++++- libevmasm/SemanticInformation.h | 11 +-- libevmasm/SimplificationRule.h | 2 + liblangutil/EVMVersion.cpp | 3 + liblangutil/EVMVersion.h | 1 + libyul/AsmAnalysis.cpp | 17 +++++ libyul/SideEffects.h | 14 ++-- libyul/backends/evm/EVMDialect.cpp | 36 +++++++++- libyul/optimiser/Semantics.h | 7 +- libyul/optimiser/UnusedStoreEliminator.cpp | 3 + scripts/test_antlr_grammar.sh | 2 + test/EVMHost.cpp | 5 ++ .../evmasm_transient_storage_opcodes/args | 1 + .../evmasm_transient_storage_opcodes/err | 5 ++ .../input.sol | 11 +++ .../evmasm_transient_storage_opcodes/output | 36 ++++++++++ ...load_tstore_not_reserved_before_cancun.sol | 25 +++++++ .../transient_storage_creation.sol | 14 ++++ .../transient_storage_low_level_calls.sol | 70 +++++++++++++++++++ ..._multiple_calls_different_transactions.sol | 24 +++++++ ...ransient_storage_multiple_transactions.sol | 38 ++++++++++ ...storage_reset_between_creation_runtime.sol | 24 +++++++ .../transient_storage_sanity_checks.sol | 24 +++++++ .../transient_storage_selfdestruct.sol | 44 ++++++++++++ ...ansient_storage_simple_reentrancy_lock.sol | 22 ++++++ .../tstore_hidden_staticcall.sol | 20 ++++++ .../inlineAssembly/tload_reserved_cancun.sol | 12 ++++ .../transient_storage_invalid_pre_cancun.sol | 13 ++++ .../transient_storage_opcodes.sol | 12 ++++ .../inlineAssembly/tstore_reserved_cancun.sol | 12 ++++ ...embly_instructions_allowed_view_cancun.sol | 1 + .../viewPureChecker/tload_not_pure.sol | 11 +++ .../viewPureChecker/tload_view.sol | 10 +++ .../viewPureChecker/tstore_not_pure.sol | 12 ++++ .../viewPureChecker/tstore_not_view.sol | 12 ++++ .../access_large_memory_offsets.yul | 1 + .../yulInterpreterTests/ambiguous_vars.yul | 1 + .../libyul/yulInterpreterTests/and_create.yul | 1 + .../yulInterpreterTests/and_create2.yul | 1 + .../yulInterpreterTests/blobbasefee.yul | 1 + test/libyul/yulInterpreterTests/blobhash.yul | 1 + .../yulInterpreterTests/bounded_recursion.yul | 1 + test/libyul/yulInterpreterTests/create2.yul | 1 + test/libyul/yulInterpreterTests/datacopy.yul | 1 + .../libyul/yulInterpreterTests/dataoffset.yul | 1 + test/libyul/yulInterpreterTests/datasize.yul | 1 + .../libyul/yulInterpreterTests/difficulty.yul | 1 + test/libyul/yulInterpreterTests/exp.yul | 1 + .../expr_nesting_depth_exceeded.yul | 1 + .../expr_nesting_depth_not_exceeded.yul | 1 + .../external_call_to_self.yul | 1 + .../external_call_unexecuted.yul | 1 + .../external_callcode_unexecuted.yul | 1 + .../external_delegatecall_unexecuted.yul | 1 + .../external_staticcall_unexecuted.yul | 1 + .../yulInterpreterTests/function_calls.yul | 1 + .../yulInterpreterTests/function_scopes.yul | 1 + .../yulInterpreterTests/hex_literals.yul | 1 + .../infinite_recursion.yul | 1 + .../infinite_recursion_tracelimit.yul | 1 + test/libyul/yulInterpreterTests/leave.yul | 1 + .../yulInterpreterTests/leave_for_init.yul | 1 + .../yulInterpreterTests/long_obect_name.yul | 1 + test/libyul/yulInterpreterTests/loop.yul | 1 + test/libyul/yulInterpreterTests/mcopy.yul | 1 + .../mcopy_memory_access_out_of_range.yul | 1 + .../mcopy_memory_expansion_on_read.yul | 1 + .../mcopy_memory_expansion_on_write.yul | 1 + .../mcopy_memory_expansion_zero_size.yul | 1 + .../yulInterpreterTests/mcopy_overlap.yul | 1 + .../yulInterpreterTests/pop_byte_shr_call.yul | 1 + .../yulInterpreterTests/pop_byte_shr_func.yul | 1 + .../libyul/yulInterpreterTests/prevrandao.yul | 1 + test/libyul/yulInterpreterTests/recursion.yul | 1 + .../recursive_function_for_loop.yul | 1 + .../yulInterpreterTests/self_balance.yul | 1 + .../yulInterpreterTests/shadowed_symbol.yul | 1 + .../yulInterpreterTests/side_effect_free.yul | 1 + .../yulInterpreterTests/simple_mstore.yul | 1 + test/libyul/yulInterpreterTests/smoke.yul | 1 + .../yulInterpreterTests/switch_statement.yul | 1 + .../yulInterpreterTests/transient_storage.yul | 13 ++++ .../yulInterpreterTests/zero_length_reads.yul | 1 + .../zero_length_reads_and_revert.yul | 1 + .../libyul/yulInterpreterTests/zero_range.yul | 1 + .../transient_storage.yul | 25 +++++++ .../fullSuite/transient_storage.yul | 21 ++++++ .../no_move_transient_storage.yul | 20 ++++++ .../unusedPruner/transient_storage.yul | 20 ++++++ .../unusedStoreEliminator/tload.yul | 22 ++++++ .../unusedStoreEliminator/tstore.yul | 23 ++++++ .../tload_as_identifier_post_cancun.yul | 9 +++ .../tstore_as_identifier_post_cancun.yul | 7 ++ test/libyul/yulSyntaxTests/tstore_tload.yul | 11 +++ ...tstore_tload_as_identifiers_pre_cancun.yul | 6 ++ .../EVMInstructionInterpreter.cpp | 8 +++ test/tools/yulInterpreter/Interpreter.cpp | 16 ++++- test/tools/yulInterpreter/Interpreter.h | 3 + 105 files changed, 870 insertions(+), 28 deletions(-) create mode 100644 test/cmdlineTests/evmasm_transient_storage_opcodes/args create mode 100644 test/cmdlineTests/evmasm_transient_storage_opcodes/err create mode 100644 test/cmdlineTests/evmasm_transient_storage_opcodes/input.sol create mode 100644 test/cmdlineTests/evmasm_transient_storage_opcodes/output create mode 100644 test/libsolidity/semanticTests/inlineAssembly/tload_tstore_not_reserved_before_cancun.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/transient_storage_creation.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/transient_storage_low_level_calls.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/transient_storage_multiple_calls_different_transactions.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/transient_storage_multiple_transactions.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/transient_storage_reset_between_creation_runtime.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/transient_storage_sanity_checks.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/transient_storage_selfdestruct.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/transient_storage_simple_reentrancy_lock.sol create mode 100644 test/libsolidity/semanticTests/inlineAssembly/tstore_hidden_staticcall.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/tload_reserved_cancun.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/transient_storage_invalid_pre_cancun.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/transient_storage_opcodes.sol create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/tstore_reserved_cancun.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/tload_not_pure.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/tload_view.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/tstore_not_pure.sol create mode 100644 test/libsolidity/syntaxTests/viewPureChecker/tstore_not_view.sol create mode 100644 test/libyul/yulInterpreterTests/transient_storage.yul create mode 100644 test/libyul/yulOptimizerTests/equalStoreEliminator/transient_storage.yul create mode 100644 test/libyul/yulOptimizerTests/fullSuite/transient_storage.yul create mode 100644 test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_transient_storage.yul create mode 100644 test/libyul/yulOptimizerTests/unusedPruner/transient_storage.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/tload.yul create mode 100644 test/libyul/yulOptimizerTests/unusedStoreEliminator/tstore.yul create mode 100644 test/libyul/yulSyntaxTests/tload_as_identifier_post_cancun.yul create mode 100644 test/libyul/yulSyntaxTests/tstore_as_identifier_post_cancun.yul create mode 100644 test/libyul/yulSyntaxTests/tstore_tload.yul create mode 100644 test/libyul/yulSyntaxTests/tstore_tload_as_identifiers_pre_cancun.yul diff --git a/Changelog.md b/Changelog.md index ea9cd13676c0..c876aa7bc8ff 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,11 +6,14 @@ Language Features: * Yul: Introduce builtin ``blobbasefee()`` for retrieving the blob base fee of the current block. * Yul: Introduce builtin ``blobhash()`` for retrieving versioned hashes of blobs associated with the transaction. * Yul: Introduce builtin ``mcopy()`` for cheaply copying data between memory areas. + * Yul: Introduce builtins ``tload()`` and ``tstore()`` for transient storage access. + Compiler Features: * EVM: Support for the EVM Version "Cancun". * SMTChecker: Support `bytes.concat` except when string literals are passed as arguments. + Bugfixes: * AST import: Fix bug when importing inline assembly with empty ``let`` variable declaration. diff --git a/docs/grammar/SolidityLexer.g4 b/docs/grammar/SolidityLexer.g4 index 91d6b4177434..43648ceb5d97 100644 --- a/docs/grammar/SolidityLexer.g4 +++ b/docs/grammar/SolidityLexer.g4 @@ -298,7 +298,7 @@ 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' - | 'pop' | 'mload' | 'mstore' | 'mstore8' | 'sload' | 'sstore' | 'msize' | 'gas' + | 'pop' | 'mload' | 'mstore' | 'mstore8' | 'sload' | 'sstore' | 'tload' | 'tstore'| 'msize' | 'gas' | 'address' | 'balance' | 'selfbalance' | 'caller' | 'callvalue' | 'calldataload' | 'calldatasize' | 'calldatacopy' | 'extcodesize' | 'extcodecopy' | 'returndatasize' | 'returndatacopy' | 'mcopy' | 'extcodehash' | 'create' | 'create2' | 'call' | 'callcode' diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index bb495c8155c5..bbeb116c9eab 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -180,6 +180,7 @@ at each version. Backward compatibility is not guaranteed between each version. - The block's blob base fee (`EIP-7516 `_ and `EIP-4844 `_) can be accessed via the global ``block.blobbasefee`` or ``blobbasefee()`` in inline assembly. - 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 `_). .. index:: ! standard JSON, ! --standard-json .. _compiler-api: diff --git a/docs/yul.rst b/docs/yul.rst index 482e80ea134c..3cf2785fc444 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -756,7 +756,8 @@ Opcodes marked with ``F``, ``H``, ``B``, ``C``, ``I``, ``L``, ``P`` and ``N`` ar Homestead, Byzantium, Constantinople, Istanbul, London, Paris or Cancun respectively. In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to -but not including position ``b`` and ``storage[p]`` signifies the storage contents at slot ``p``. +but not including position ``b``, ``storage[p]`` signifies the storage contents at slot ``p``, and +similarly, ``transientStorage[p]`` signifies the transient storage contents at slot ``p``. Since Yul manages local variables and control-flow, opcodes that interfere with these features are not available. This includes @@ -833,6 +834,10 @@ the ``dup`` and ``swap`` instructions as well as ``jump`` instructions, labels a +-------------------------+-----+---+-----------------------------------------------------------------+ | sstore(p, v) | `-` | F | storage[p] := v | +-------------------------+-----+---+-----------------------------------------------------------------+ +| tload(p) | | N | transientStorage[p] | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| tstore(p, v) | `-` | N | transientStorage[p] := v | ++-------------------------+-----+---+-----------------------------------------------------------------+ | msize() | | F | size of memory, i.e. largest accessed memory index | +-------------------------+-----+---+-----------------------------------------------------------------+ | gas() | | F | gas still available to execution | diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index 6594d1ff6c2f..5982b8a08092 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -274,13 +274,14 @@ unsigned GasMeter::runGas(Instruction _instruction, langutil::EVMVersion _evmVer switch (instructionInfo(_instruction, _evmVersion).gasPriceTier) { - case Tier::Zero: return GasCosts::tier0Gas; - case Tier::Base: return GasCosts::tier1Gas; - case Tier::VeryLow: return GasCosts::tier2Gas; - case Tier::Low: return GasCosts::tier3Gas; - case Tier::Mid: return GasCosts::tier4Gas; - case Tier::High: return GasCosts::tier5Gas; - case Tier::Ext: return GasCosts::tier6Gas; + case Tier::Zero: return GasCosts::tier0Gas; + case Tier::Base: return GasCosts::tier1Gas; + case Tier::VeryLow: return GasCosts::tier2Gas; + case Tier::Low: return GasCosts::tier3Gas; + case Tier::Mid: return GasCosts::tier4Gas; + case Tier::High: return GasCosts::tier5Gas; + case Tier::Ext: return GasCosts::tier6Gas; + case Tier::WarmAccess: return GasCosts::warmStorageReadCost; default: break; } assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction, _evmVersion).name); diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index c00bc3542151..a21f33e1838f 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -90,6 +90,8 @@ std::map const solidity::evmasm::c_instructions = { "MSTORE8", Instruction::MSTORE8 }, { "SLOAD", Instruction::SLOAD }, { "SSTORE", Instruction::SSTORE }, + { "TLOAD", Instruction::TLOAD }, + { "TSTORE", Instruction::TSTORE }, { "JUMP", Instruction::JUMP }, { "JUMPI", Instruction::JUMPI }, { "PC", Instruction::PC }, @@ -242,6 +244,8 @@ static std::map const c_instructionInfo = { Instruction::MSTORE8, { "MSTORE8", 0, 2, 0, true, Tier::VeryLow } }, { Instruction::SLOAD, { "SLOAD", 0, 1, 1, false, Tier::Special } }, { Instruction::SSTORE, { "SSTORE", 0, 2, 0, true, Tier::Special } }, + { Instruction::TLOAD, { "TLOAD", 0, 1, 1, false, Tier::WarmAccess} }, + { Instruction::TSTORE, { "TSTORE", 0, 2, 0, true, Tier::WarmAccess} }, { Instruction::JUMP, { "JUMP", 0, 1, 0, true, Tier::Mid } }, { Instruction::JUMPI, { "JUMPI", 0, 2, 0, true, Tier::High } }, { Instruction::PC, { "PC", 0, 0, 1, false, Tier::Base } }, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index 4e7ac8b97ff0..1733863aa5a2 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -106,6 +106,9 @@ enum class Instruction: uint8_t JUMPDEST, ///< set a potential jump destination MCOPY = 0x5e, ///< copy between memory areas + TLOAD = 0x5c, ///< load word from transient storage + TSTORE = 0x5d, ///< save word to transient storage + PUSH0 = 0x5f, ///< place the value 0 on stack PUSH1 = 0x60, ///< place 1 byte item on stack PUSH2, ///< place 2 byte item on stack @@ -293,6 +296,7 @@ enum class Tier Mid, // 8, Mid High, // 10, Slow Ext, // 20, Ext + WarmAccess, // 100, Warm Access ExtCode, // 700, Extcode Balance, // 400, Balance Special, // multiparam or otherwise special diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 5c8b986be90d..c5e14d5f4b85 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -37,6 +37,7 @@ std::vector SemanticInformation::readWriteOperat { assertThrow(memory(_instruction) == Effect::None, OptimizerException, ""); assertThrow(storage(_instruction) != Effect::None, OptimizerException, ""); + assertThrow(transientStorage(_instruction) == Effect::None, OptimizerException, ""); Operation op; op.effect = storage(_instruction); op.location = Location::Storage; @@ -51,6 +52,7 @@ std::vector SemanticInformation::readWriteOperat { assertThrow(memory(_instruction) != Effect::None, OptimizerException, ""); assertThrow(storage(_instruction) == Effect::None, OptimizerException, ""); + assertThrow(transientStorage(_instruction) == Effect::None, OptimizerException, ""); Operation op; op.effect = memory(_instruction); op.location = Location::Memory; @@ -62,6 +64,19 @@ std::vector SemanticInformation::readWriteOperat return {op}; } + case Instruction::TSTORE: + case Instruction::TLOAD: + { + assertThrow(memory(_instruction) == Effect::None, OptimizerException, ""); + assertThrow(storage(_instruction) == Effect::None, OptimizerException, ""); + assertThrow(transientStorage(_instruction) != Effect::None, OptimizerException, ""); + Operation op; + op.effect = transientStorage(_instruction); + op.location = Location::TransientStorage; + op.startParameter = 0; + op.lengthConstant = 1; + return {op}; + } case Instruction::REVERT: case Instruction::RETURN: case Instruction::KECCAK256: @@ -72,6 +87,7 @@ std::vector SemanticInformation::readWriteOperat case Instruction::LOG4: { assertThrow(storage(_instruction) == Effect::None, OptimizerException, ""); + assertThrow(transientStorage(_instruction) == Effect::None, OptimizerException, ""); assertThrow(memory(_instruction) == Effect::Read, OptimizerException, ""); Operation op; op.effect = memory(_instruction); @@ -84,6 +100,7 @@ std::vector SemanticInformation::readWriteOperat { assertThrow(memory(_instruction) == Effect::Write, OptimizerException, ""); assertThrow(storage(_instruction) == Effect::None, OptimizerException, ""); + assertThrow(transientStorage(_instruction) == Effect::None, OptimizerException, ""); Operation op; op.effect = memory(_instruction); op.location = Location::Memory; @@ -97,6 +114,7 @@ std::vector SemanticInformation::readWriteOperat { assertThrow(memory(_instruction) == Effect::Write, OptimizerException, ""); assertThrow(storage(_instruction) == Effect::None, OptimizerException, ""); + assertThrow(transientStorage(_instruction) == Effect::None, OptimizerException, ""); Operation op; op.effect = memory(_instruction); op.location = Location::Memory; @@ -131,10 +149,14 @@ std::vector SemanticInformation::readWriteOperat size_t paramCount = static_cast(instructionInfo(_instruction, langutil::EVMVersion()).args); std::vector operations{ Operation{Location::Memory, Effect::Read, paramCount - 4, paramCount - 3, {}}, - Operation{Location::Storage, Effect::Read, {}, {}, {}} + Operation{Location::Storage, Effect::Read, {}, {}, {}}, + Operation{Location::TransientStorage, Effect::Read, {}, {}, {}} }; if (_instruction != Instruction::STATICCALL) + { operations.emplace_back(Operation{Location::Storage, Effect::Write, {}, {}, {}}); + operations.emplace_back(Operation{Location::TransientStorage, Effect::Write, {}, {}, {}}); + } operations.emplace_back(Operation{ Location::Memory, Effect::Write, @@ -157,13 +179,15 @@ std::vector SemanticInformation::readWriteOperat {} }, Operation{Location::Storage, Effect::Read, {}, {}, {}}, - Operation{Location::Storage, Effect::Write, {}, {}, {}} + Operation{Location::Storage, Effect::Write, {}, {}, {}}, + Operation{Location::TransientStorage, Effect::Read, {}, {}, {}}, + Operation{Location::TransientStorage, Effect::Write, {}, {}, {}} }; case Instruction::MSIZE: // This is just to satisfy the assert below. return std::vector{}; default: - assertThrow(storage(_instruction) == None && memory(_instruction) == None, AssemblyException, ""); + assertThrow(storage(_instruction) == None && memory(_instruction) == None && transientStorage(_instruction) == None, AssemblyException, ""); } return {}; } @@ -348,6 +372,7 @@ bool SemanticInformation::movable(Instruction _instruction) case Instruction::EXTCODEHASH: case Instruction::RETURNDATASIZE: case Instruction::SLOAD: + case Instruction::TLOAD: case Instruction::PC: case Instruction::MSIZE: case Instruction::GAS: @@ -420,6 +445,7 @@ bool SemanticInformation::movableApartFromEffects(Instruction _instruction) case Instruction::BALANCE: case Instruction::SELFBALANCE: case Instruction::SLOAD: + case Instruction::TLOAD: case Instruction::KECCAK256: case Instruction::MLOAD: return true; @@ -450,6 +476,27 @@ SemanticInformation::Effect SemanticInformation::storage(Instruction _instructio } } +SemanticInformation::Effect SemanticInformation::transientStorage(Instruction _instruction) +{ + switch (_instruction) + { + case Instruction::CALL: + case Instruction::CALLCODE: + case Instruction::DELEGATECALL: + case Instruction::CREATE: + case Instruction::CREATE2: + case Instruction::TSTORE: + return SemanticInformation::Write; + + case Instruction::TLOAD: + case Instruction::STATICCALL: + return SemanticInformation::Read; + + default: + return SemanticInformation::None; + } +} + SemanticInformation::Effect SemanticInformation::otherState(Instruction _instruction) { switch (_instruction) @@ -508,6 +555,7 @@ bool SemanticInformation::invalidInPureFunctions(Instruction _instruction) case Instruction::GASLIMIT: case Instruction::STATICCALL: case Instruction::SLOAD: + case Instruction::TLOAD: return true; default: break; @@ -520,6 +568,7 @@ bool SemanticInformation::invalidInViewFunctions(Instruction _instruction) switch (_instruction) { case Instruction::SSTORE: + case Instruction::TSTORE: case Instruction::JUMP: case Instruction::JUMPI: case Instruction::LOG0: diff --git a/libevmasm/SemanticInformation.h b/libevmasm/SemanticInformation.h index 36aabd55f9f6..c3491cda8bd8 100644 --- a/libevmasm/SemanticInformation.h +++ b/libevmasm/SemanticInformation.h @@ -39,8 +39,8 @@ class AssemblyItem; */ struct SemanticInformation { - /// Corresponds to the effect that a YUL-builtin has on a generic data location (storage, memory - /// and other blockchain state). + /// Corresponds to the effect that a YUL-builtin has on a generic data location (storage, memory, + /// transient storage and other blockchain state). enum Effect { None, @@ -48,7 +48,7 @@ struct SemanticInformation Write }; - enum class Location { Storage, Memory }; + enum class Location { Storage, Memory, TransientStorage }; /** * Represents a read or write operation from or to one of the data locations. @@ -87,10 +87,10 @@ struct SemanticInformation static bool terminatesControlFlow(Instruction _instruction); static bool reverts(Instruction _instruction); /// @returns false if the value put on the stack by _item depends on anything else than - /// the information in the current block header, memory, storage or stack. + /// the information in the current block header, memory, storage, transient storage or stack. static bool isDeterministic(AssemblyItem const& _item); /// @returns true if the instruction can be moved or copied (together with its arguments) - /// without altering the semantics. This means it cannot depend on storage or memory, + /// without altering the semantics. This means it cannot depend on storage, transient storage or memory, /// cannot have any side-effects, but it can depend on a call-constant state of the blockchain. static bool movable(Instruction _instruction); /// If true, the expressions in this code can be moved or copied (together with their arguments) @@ -109,6 +109,7 @@ struct SemanticInformation static bool canBeRemovedIfNoMSize(Instruction _instruction); static Effect memory(Instruction _instruction); static Effect storage(Instruction _instruction); + static Effect transientStorage(Instruction _instruction); static Effect otherState(Instruction _instruction); static bool invalidInPureFunctions(Instruction _instruction); static bool invalidInViewFunctions(Instruction _instruction); diff --git a/libevmasm/SimplificationRule.h b/libevmasm/SimplificationRule.h index 746dfa4c67d1..6d34a50410fb 100644 --- a/libevmasm/SimplificationRule.h +++ b/libevmasm/SimplificationRule.h @@ -136,6 +136,8 @@ struct EVMBuiltins static auto constexpr MSTORE8 = PatternGenerator{}; static auto constexpr SLOAD = PatternGenerator{}; static auto constexpr SSTORE = PatternGenerator{}; + static auto constexpr TLOAD = PatternGenerator{}; + static auto constexpr TSTORE = PatternGenerator{}; static auto constexpr PC = PatternGenerator{}; static auto constexpr MSIZE = PatternGenerator{}; static auto constexpr GAS = PatternGenerator{}; diff --git a/liblangutil/EVMVersion.cpp b/liblangutil/EVMVersion.cpp index 34996dc6b98c..438932a1cdaa 100644 --- a/liblangutil/EVMVersion.cpp +++ b/liblangutil/EVMVersion.cpp @@ -55,6 +55,9 @@ bool EVMVersion::hasOpcode(Instruction _opcode) const return hasBlobBaseFee(); case Instruction::MCOPY: return hasMcopy(); + case Instruction::TSTORE: + case Instruction::TLOAD: + return supportsTransientStorage(); default: return true; } diff --git a/liblangutil/EVMVersion.h b/liblangutil/EVMVersion.h index 5b4e73b15e7d..992933a898bf 100644 --- a/liblangutil/EVMVersion.h +++ b/liblangutil/EVMVersion.h @@ -106,6 +106,7 @@ class EVMVersion: bool hasPush0() const { return *this >= shanghai(); } bool hasBlobHash() const { return *this >= cancun(); } bool hasMcopy() const { return *this >= cancun(); } + bool supportsTransientStorage() const { return *this >= cancun(); } bool hasOpcode(evmasm::Instruction _opcode) const; diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index c4dade689830..97f3d6eeb5c9 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -319,6 +319,20 @@ std::vector AsmAnalyzer::operator()(FunctionCall const& _funCall) "The underlying opcode will eventually undergo breaking changes, " "and its use is not recommended." ); + else if ( + m_evmVersion.supportsTransientStorage() && + _funCall.functionName.name == "tstore"_yulstring + ) + m_errorReporter.warning( + 2394_error, + nativeLocationOf(_funCall.functionName), + "Transient storage as defined by EIP-1153 can break the composability of smart contracts: " + "Since transient storage is cleared only at the end of the transaction and not at the end of the outermost call frame to the contract within a transaction, " + "your contract may unintentionally misbehave when invoked multiple times in a complex transaction. " + "To avoid this, be sure to clear all transient storage at the end of any call to your contract. " + "The use of transient storage for reentrancy guards that are cleared at the end of the call is safe." + ); + parameterTypes = &f->parameters; returnTypes = &f->returns; if (!f->literalArguments.empty()) @@ -739,6 +753,9 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio else if (_instr == evmasm::Instruction::MCOPY && !m_evmVersion.hasMcopy()) // TODO: Change this assertion to an error, similar to the ones above, when Cancun becomes the default EVM version. yulAssert(false); + else if ((_instr == evmasm::Instruction::TSTORE || _instr == evmasm::Instruction::TLOAD) && !m_evmVersion.supportsTransientStorage()) + // TODO: Change this assertion to an error, similar to the ones above, when Cancun becomes the default EVM version. + yulAssert(false); else if (_instr == evmasm::Instruction::PC) m_errorReporter.error( 2450_error, diff --git a/libyul/SideEffects.h b/libyul/SideEffects.h index b6df8df83f89..eb3fe9b55a95 100644 --- a/libyul/SideEffects.h +++ b/libyul/SideEffects.h @@ -50,7 +50,7 @@ struct SideEffects /// At statement level, it means that functions containing this code can be /// called multiple times, their calls can be rearranged and calls can also be /// deleted without changing the semantics. - /// This means it cannot depend on storage or memory, cannot have any side-effects, + /// This means it cannot depend on storage, memory or transient storage, cannot have any side-effects, /// but it can depend on state that is constant across an EVM-call. bool movable = true; /// If true, the expressions in this code can be moved or copied (together with their arguments) @@ -76,11 +76,15 @@ struct SideEffects /// or `None` respectively. Note that, when the value is `Read`, the expression can have an /// effect on `msize()`. Effect memory = None; + /// Can write, read or have no effect on transient storage, when the value of `transientStorage` is `Write`, `Read` + /// or `None` respectively. When the value is `Write`, the expression can invalidate transient storage, + /// potentially indirectly through external calls. + Effect transientStorage = None; /// @returns the worst-case side effects. static SideEffects worst() { - return SideEffects{false, false, false, false, false, Write, Write, Write}; + return SideEffects{false, false, false, false, false, Write, Write, Write, Write}; } /// @returns the combined side effects of two pieces of code. @@ -94,7 +98,8 @@ struct SideEffects cannotLoop && _other.cannotLoop, otherState + _other.otherState, storage + _other.storage, - memory + _other.memory + memory + _other.memory, + transientStorage + _other.transientStorage }; } @@ -115,7 +120,8 @@ struct SideEffects cannotLoop == _other.cannotLoop && otherState == _other.otherState && storage == _other.storage && - memory == _other.memory; + memory == _other.memory && + transientStorage == _other.transientStorage; } }; diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 36e6f1d0078d..f3c529bb1ab5 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -148,6 +148,14 @@ std::set createReservedIdentifiers(langutil::EVMVersion _evmVersion) { return _instr == evmasm::Instruction::BLOBHASH && _evmVersion < langutil::EVMVersion::cancun(); }; + // TODO remove this in 0.9.0. We allow creating functions or identifiers in Yul with the names + // tstore or tload for VMs before cancun. + auto transientStorageException = [&](evmasm::Instruction _instr) -> bool + { + return + _evmVersion < langutil::EVMVersion::cancun() && + (_instr == evmasm::Instruction::TSTORE || _instr == evmasm::Instruction::TLOAD); + }; std::set reserved; for (auto const& instr: evmasm::c_instructions) @@ -158,7 +166,8 @@ std::set createReservedIdentifiers(langutil::EVMVersion _evmVersion) !prevRandaoException(name) && !blobHashException(instr.second) && !blobBaseFeeException(instr.second) && - !mcopyException(instr.second) + !mcopyException(instr.second) && + !transientStorageException(instr.second) ) reserved.emplace(name); } @@ -277,7 +286,17 @@ std::map createBuiltins(langutil::EVMVersion _ "datacopy", 3, 0, - SideEffects{false, true, false, false, true, SideEffects::None, SideEffects::None, SideEffects::Write}, + SideEffects{ + false, // movable + true, // movableApartFromEffects + false, // canBeRemoved + false, // canBeRemovedIfNotMSize + true, // cannotLoop + SideEffects::None, // otherState + SideEffects::None, // storage + SideEffects::Write, // memory + SideEffects::None // transientStorage + }, {}, []( FunctionCall const&, @@ -291,7 +310,17 @@ std::map createBuiltins(langutil::EVMVersion _ "setimmutable", 3, 0, - SideEffects{false, false, false, false, true, SideEffects::None, SideEffects::None, SideEffects::Write}, + SideEffects{ + false, // movable + false, // movableApartFromEffects + false, // canBeRemoved + false, // canBeRemovedIfNotMSize + true, // cannotLoop + SideEffects::None, // otherState + SideEffects::None, // storage + SideEffects::Write, // memory + SideEffects::None // transientStorage + }, {std::nullopt, LiteralKind::String, std::nullopt}, []( FunctionCall const& _call, @@ -396,6 +425,7 @@ SideEffects EVMDialect::sideEffectsOfInstruction(evmasm::Instruction _instructio translate(evmasm::SemanticInformation::otherState(_instruction)), translate(evmasm::SemanticInformation::storage(_instruction)), translate(evmasm::SemanticInformation::memory(_instruction)), + translate(evmasm::SemanticInformation::transientStorage(_instruction)), }; } diff --git a/libyul/optimiser/Semantics.h b/libyul/optimiser/Semantics.h index 762c80110618..df8ae17a58e1 100644 --- a/libyul/optimiser/Semantics.h +++ b/libyul/optimiser/Semantics.h @@ -78,7 +78,8 @@ class SideEffectsCollector: public ASTWalker !m_sideEffects.movableApartFromEffects || m_sideEffects.storage == SideEffects::Write || m_sideEffects.otherState == SideEffects::Write || - m_sideEffects.memory == SideEffects::Write + m_sideEffects.memory == SideEffects::Write || + m_sideEffects.transientStorage == SideEffects::Write ) return false; @@ -94,6 +95,10 @@ class SideEffectsCollector: public ASTWalker if (_codeContainsMSize || _other.memory == SideEffects::Write) return false; + if (m_sideEffects.transientStorage == SideEffects::Read) + if (_other.transientStorage == SideEffects::Write) + return false; + return true; } diff --git a/libyul/optimiser/UnusedStoreEliminator.cpp b/libyul/optimiser/UnusedStoreEliminator.cpp index 95296369b77d..ed3394847c35 100644 --- a/libyul/optimiser/UnusedStoreEliminator.cpp +++ b/libyul/optimiser/UnusedStoreEliminator.cpp @@ -214,7 +214,10 @@ void UnusedStoreEliminator::visit(Statement const& _statement) if (operations.front().location == Location::Storage) activeStorageStores().insert(&_statement); else + { + yulAssert(operations.front().location == Location::Memory, ""); activeMemoryStores().insert(&_statement); + } m_storeOperations[&_statement] = std::move(operations.front()); } } diff --git a/scripts/test_antlr_grammar.sh b/scripts/test_antlr_grammar.sh index d21228c5be3f..5256b8128600 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 tests with "let tstore/tload := ..." + grep -v -E 'inlineAssembly/tload_tstore_not_reserved_before_cancun.sol' | # Skipping license error, unrelated to the grammar grep -v -E 'license/license_double5.sol' | grep -v -E 'license/license_hidden_unicode.sol' | diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 84327a5b977f..5b4776273c3d 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -186,11 +186,16 @@ void EVMHost::newTransactionFrame() recorded_account_accesses.clear(); for (auto& [address, account]: accounts) + { for (auto& [slot, value]: account.storage) { value.access_status = EVMC_ACCESS_COLD; // Clear EIP-2929 storage access indicator value.original = value.current; // Clear EIP-2200 dirty slot } + + // Clear transient storage according to EIP 1153 + account.transient_storage.clear(); + } // Process selfdestruct list for (auto& [address, _]: recorded_selfdestructs) accounts.erase(address); diff --git a/test/cmdlineTests/evmasm_transient_storage_opcodes/args b/test/cmdlineTests/evmasm_transient_storage_opcodes/args new file mode 100644 index 000000000000..552cea1ca144 --- /dev/null +++ b/test/cmdlineTests/evmasm_transient_storage_opcodes/args @@ -0,0 +1 @@ +--evm-version cancun --no-cbor-metadata --via-ir --optimize --opcodes --asm --debug-info none diff --git a/test/cmdlineTests/evmasm_transient_storage_opcodes/err b/test/cmdlineTests/evmasm_transient_storage_opcodes/err new file mode 100644 index 000000000000..a6cd56261e05 --- /dev/null +++ b/test/cmdlineTests/evmasm_transient_storage_opcodes/err @@ -0,0 +1,5 @@ +Warning: Transient storage as defined by EIP-1153 can break the composability of smart contracts: Since transient storage is cleared only at the end of the transaction and not at the end of the outermost call frame to the contract within a transaction, your contract may unintentionally misbehave when invoked multiple times in a complex transaction. To avoid this, be sure to clear all transient storage at the end of any call to your contract. The use of transient storage for reentrancy guards that are cleared at the end of the call is safe. + --> evmasm_transient_storage_opcodes/input.sol:7:13: + | +7 | tstore(0, 0) + | ^^^^^^ diff --git a/test/cmdlineTests/evmasm_transient_storage_opcodes/input.sol b/test/cmdlineTests/evmasm_transient_storage_opcodes/input.sol new file mode 100644 index 000000000000..94914ac4eef7 --- /dev/null +++ b/test/cmdlineTests/evmasm_transient_storage_opcodes/input.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0.0; + +contract C { + fallback() external { + assembly { + tstore(0, 0) + sstore(0, tload(0)) + } + } +} diff --git a/test/cmdlineTests/evmasm_transient_storage_opcodes/output b/test/cmdlineTests/evmasm_transient_storage_opcodes/output new file mode 100644 index 000000000000..3d1eddc9ad02 --- /dev/null +++ b/test/cmdlineTests/evmasm_transient_storage_opcodes/output @@ -0,0 +1,36 @@ + +======= evmasm_transient_storage_opcodes/input.sol:C ======= +EVM assembly: + 0x80 + dup1 + 0x40 + mstore + jumpi(tag_1, callvalue) + dataSize(sub_0) + swap1 + dup2 + dataOffset(sub_0) + dup3 + codecopy + return +tag_1: + 0x00 + dup1 + revert +stop + +sub_0: assembly { + jumpi(tag_1, callvalue) + 0x00 + dup1 + tstore + sstore(0x00, tload(0x00)) + stop + tag_1: + 0x00 + dup1 + revert +} + +Opcodes: +PUSH1 0x80 DUP1 PUSH1 0x40 MSTORE CALLVALUE PUSH1 0x13 JUMPI PUSH1 0x10 SWAP1 DUP2 PUSH1 0x18 DUP3 CODECOPY RETURN JUMPDEST PUSH0 DUP1 REVERT INVALID CALLVALUE PUSH1 0xC JUMPI PUSH0 DUP1 TSTORE PUSH0 TLOAD PUSH0 SSTORE STOP JUMPDEST PUSH0 DUP1 REVERT diff --git a/test/libsolidity/semanticTests/inlineAssembly/tload_tstore_not_reserved_before_cancun.sol b/test/libsolidity/semanticTests/inlineAssembly/tload_tstore_not_reserved_before_cancun.sol new file mode 100644 index 000000000000..930c531eaacf --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/tload_tstore_not_reserved_before_cancun.sol @@ -0,0 +1,25 @@ +contract C { + function f() public view returns (uint ret) { + assembly { + let tload := sload(0) + let tstore := add(tload, 1) + ret := tstore + } + } + function g() public view returns (uint ret) { + assembly { + function tstore() -> a { + a := 2 + } + function tload() -> b { + b := 3 + } + ret := add(tstore(), tload()) + } + } +} +// ==== +// EVMVersion: 1 +// g() -> 5 diff --git a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_creation.sol b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_creation.sol new file mode 100644 index 000000000000..5a97dc84ac0d --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_creation.sol @@ -0,0 +1,14 @@ +contract C { + constructor() { + uint x; + assembly { + tstore(0, 42) + x := tload(0) + } + assert(x == 42); + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// constructor() -> diff --git a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_low_level_calls.sol b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_low_level_calls.sol new file mode 100644 index 000000000000..f3fe0384472d --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_low_level_calls.sol @@ -0,0 +1,70 @@ +contract D { + function addOne() external { + assembly { + let x := tload(0) + tstore(0, add(x, 1)) + } + } + function get() external returns (uint x) { + assembly { + x := tload(0) + } + } +} + +contract C { + function set(uint x) external { + assembly { + tstore(0, x) + } + } + + function get() external view returns (uint x) { + assembly { + x := tload(0) + } + } + + function testDelegateCall() external returns (bool) { + this.set(5); + D d = new D(); + // Caller contract is the owner of the transient storage + (bool success, ) = address(d).delegatecall(abi.encodeCall(d.addOne, ())); + require(success); + require(this.get() == 6); + return true; + } + + function testCall() external returns (bool) { + this.set(5); + D d = new D(); + // Callee/Target contract is the owner of the transient storage + (bool success, ) = address(d).call(abi.encodeCall(d.addOne, ())); + require(success); + require(d.get() == 1); + return true; + } + + function tloadAllowedStaticCall() external returns (bool) { + this.set(5); + D d = new D(); + (bool success, bytes memory result) = address(d).staticcall(abi.encodeCall(d.get, ())); + require(success); + require(abi.decode(result, (uint)) == 0); + return true; + } + + function tstoreNotAllowedStaticCall() external returns (bool) { + D d = new D(); + (bool success, ) = address(d).staticcall(abi.encodeCall(d.addOne, ())); + require(!success); + return true; + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// testDelegateCall() -> true +// testCall() -> true +// tloadAllowedStaticCall() -> true +// tstoreNotAllowedStaticCall() -> true diff --git a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_multiple_calls_different_transactions.sol b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_multiple_calls_different_transactions.sol new file mode 100644 index 000000000000..c82286550eb0 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_multiple_calls_different_transactions.sol @@ -0,0 +1,24 @@ +contract C { + function get(uint256 addr) external view returns (uint256 x) { + assembly { + x := tload(addr) + } + } + function set(uint256 addr, uint256 x) external { + assembly { + tstore(addr, x) + } + } + function test() public { + assert(this.get(0) == 0 && this.get(42) == 0); + this.set(0, 21); + assert(this.get(0) == 21 && this.get(42) == 0); + this.set(42, 131); + assert(this.get(0) == 21 && this.get(42) == 131); + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// test() -> +// test() -> diff --git a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_multiple_transactions.sol b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_multiple_transactions.sol new file mode 100644 index 000000000000..931f34ded5dc --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_multiple_transactions.sol @@ -0,0 +1,38 @@ +contract C { + function set(uint value) private { + assembly { + tstore(0, value) + } + } + + function get() private view returns (uint value) { + assembly { + value := tload(0) + } + } + + function f() external { + assembly { + tstore(0, 42) + } + } + + function g() external view returns(uint r) { + assembly { + r := tload(0) + } + } + + function h() external returns (uint x) { + set(99); + x = get(); + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// g() -> 0 +// f() -> +// g() -> 0 +// h() -> 0x63 +// g() -> 0 diff --git a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_reset_between_creation_runtime.sol b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_reset_between_creation_runtime.sol new file mode 100644 index 000000000000..e41fcb600339 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_reset_between_creation_runtime.sol @@ -0,0 +1,24 @@ +contract C { + constructor() { + uint x; + assembly { + tstore(0, 42) + x := tload(0) + } + assert(x == 42); + } + + function f() public returns (uint x) { + assembly { + x := tload(0) + if eq(x, 42) { + revert(0, 0) + } + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// constructor() -> +// f() -> 0 diff --git a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_sanity_checks.sol b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_sanity_checks.sol new file mode 100644 index 000000000000..101c3c103d1b --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_sanity_checks.sol @@ -0,0 +1,24 @@ +contract C { + function f() external { + assembly { + tstore(0, 21) + mstore(0, 42) + sstore(0, 42) + if iszero(eq(tload(0), 21)) { + revert(0, 0) + } + } + } + function g() external view returns (uint s, uint m, uint t) { + assembly { + s := sload(0) + m := mload(0) + t := tload(0) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// f() -> +// g() -> 0x2a, 0, 0 diff --git a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_selfdestruct.sol b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_selfdestruct.sol new file mode 100644 index 000000000000..935435420e44 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_selfdestruct.sol @@ -0,0 +1,44 @@ +contract C { + function set(uint value) external { + assembly { + tstore(0, value) + } + } + + function get() external view returns (uint value) { + assembly { + value := tload(0) + } + } + + function terminate(address payable a) external { + selfdestruct(a); + } +} + +contract D { + C public c; + + constructor() { + c = new C(); + } + + function destroy() external { + c.set(42); + c.terminate(payable(address(this))); + assert(c.get() == 42); + } + + function createAndDestroy() external { + c = new C(); + c.set(42); + c.terminate(payable(address(this))); + assert(c.get() == 42); + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// constructor() -> +// destroy() -> +// createAndDestroy() -> diff --git a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_simple_reentrancy_lock.sol b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_simple_reentrancy_lock.sol new file mode 100644 index 000000000000..0d05c926ceef --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_simple_reentrancy_lock.sol @@ -0,0 +1,22 @@ +contract C { + modifier nonreentrant { + assembly { + if tload(0) { revert(0, 0) } + tstore(0, 1) + } + _; + assembly { + tstore(0, 0) + } + } + function f(bool simulateReentrancy) nonreentrant public { + if (simulateReentrancy) { + f(false); + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// f(bool): false -> +// f(bool): true -> FAILURE diff --git a/test/libsolidity/semanticTests/inlineAssembly/tstore_hidden_staticcall.sol b/test/libsolidity/semanticTests/inlineAssembly/tstore_hidden_staticcall.sol new file mode 100644 index 000000000000..bdd9401ee5a6 --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/tstore_hidden_staticcall.sol @@ -0,0 +1,20 @@ +contract C { + function f() internal { + assembly { + tstore(0, 0) + } + } + function g() public view { + function() internal ptr = f; + function() internal view ptr2; + assembly { ptr2 := ptr } + ptr2(); // we force calling the non-view function, which should result in a revert during the staticcall + } + function test() public { + this.g(); // an external call to a view function should use static call + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// test() -> FAILURE diff --git a/test/libsolidity/syntaxTests/inlineAssembly/tload_reserved_cancun.sol b/test/libsolidity/syntaxTests/inlineAssembly/tload_reserved_cancun.sol new file mode 100644 index 000000000000..77c988577620 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/tload_reserved_cancun.sol @@ -0,0 +1,12 @@ +contract C { + function f() public view returns (uint ret) { + assembly { + let tload := sload(0) + ret := tload + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// ParserError 5568: (98-103): Cannot use builtin function name "tload" as identifier name. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/transient_storage_invalid_pre_cancun.sol b/test/libsolidity/syntaxTests/inlineAssembly/transient_storage_invalid_pre_cancun.sol new file mode 100644 index 000000000000..25ae9e9d172b --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/transient_storage_invalid_pre_cancun.sol @@ -0,0 +1,13 @@ +contract C { + function f() public { + assembly { + tstore(0, 13) + tload(0) + } + } +} +// ==== +// EVMVersion: =cancun +// ---- +// Warning 2394: (88-94): Transient storage as defined by EIP-1153 can break the composability of smart contracts: Since transient storage is cleared only at the end of the transaction and not at the end of the outermost call frame to the contract within a transaction, your contract may unintentionally misbehave when invoked multiple times in a complex transaction. To avoid this, be sure to clear all transient storage at the end of any call to your contract. The use of transient storage for reentrancy guards that are cleared at the end of the call is safe. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/tstore_reserved_cancun.sol b/test/libsolidity/syntaxTests/inlineAssembly/tstore_reserved_cancun.sol new file mode 100644 index 000000000000..9f08d985aa38 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/tstore_reserved_cancun.sol @@ -0,0 +1,12 @@ +contract C { + function f() public view returns (uint ret) { + assembly { + let tstore := sload(0) + ret := tstore + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// ParserError 5568: (98-104): Cannot use builtin function name "tstore" as identifier name. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol index 8aacecdec7ed..dcf6d6c6fc6e 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed_view_cancun.sol @@ -4,6 +4,7 @@ contract C { pop(blobhash(0)) pop(blobbasefee()) mcopy(1, 2, 3) + pop(tload(0)) } } } diff --git a/test/libsolidity/syntaxTests/viewPureChecker/tload_not_pure.sol b/test/libsolidity/syntaxTests/viewPureChecker/tload_not_pure.sol new file mode 100644 index 000000000000..13b44f197f09 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/tload_not_pure.sol @@ -0,0 +1,11 @@ +contract C { + function f() external pure { + assembly { + pop(tload(0)) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// TypeError 2527: (81-89): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libsolidity/syntaxTests/viewPureChecker/tload_view.sol b/test/libsolidity/syntaxTests/viewPureChecker/tload_view.sol new file mode 100644 index 000000000000..c94bd867027b --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/tload_view.sol @@ -0,0 +1,10 @@ +contract C { + function f() external view returns (uint a) { + assembly { + a := tload(0) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- diff --git a/test/libsolidity/syntaxTests/viewPureChecker/tstore_not_pure.sol b/test/libsolidity/syntaxTests/viewPureChecker/tstore_not_pure.sol new file mode 100644 index 000000000000..3cda8d08e9d8 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/tstore_not_pure.sol @@ -0,0 +1,12 @@ +contract C { + function f() external pure { + assembly { + tstore(0, 0) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// Warning 2394: (77-83): Transient storage as defined by EIP-1153 can break the composability of smart contracts: Since transient storage is cleared only at the end of the transaction and not at the end of the outermost call frame to the contract within a transaction, your contract may unintentionally misbehave when invoked multiple times in a complex transaction. To avoid this, be sure to clear all transient storage at the end of any call to your contract. The use of transient storage for reentrancy guards that are cleared at the end of the call is safe. +// TypeError 8961: (77-89): Function cannot be declared as pure because this expression (potentially) modifies the state. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/tstore_not_view.sol b/test/libsolidity/syntaxTests/viewPureChecker/tstore_not_view.sol new file mode 100644 index 000000000000..5e02ef910c3c --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/tstore_not_view.sol @@ -0,0 +1,12 @@ +contract C { + function f() external view { + assembly { + tstore(0, 0) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// Warning 2394: (77-83): Transient storage as defined by EIP-1153 can break the composability of smart contracts: Since transient storage is cleared only at the end of the transaction and not at the end of the outermost call frame to the contract within a transaction, your contract may unintentionally misbehave when invoked multiple times in a complex transaction. To avoid this, be sure to clear all transient storage at the end of any call to your contract. The use of transient storage for reentrancy guards that are cleared at the end of the call is safe. +// TypeError 8961: (77-89): Function cannot be declared as view because this expression (potentially) modifies the state. diff --git a/test/libyul/yulInterpreterTests/access_large_memory_offsets.yul b/test/libyul/yulInterpreterTests/access_large_memory_offsets.yul index 483220bc1317..ce108ddf7dd4 100644 --- a/test/libyul/yulInterpreterTests/access_large_memory_offsets.yul +++ b/test/libyul/yulInterpreterTests/access_large_memory_offsets.yul @@ -12,3 +12,4 @@ // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000007 // 0000000000000000000000000000000000000000000000000000000000000001: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/ambiguous_vars.yul b/test/libyul/yulInterpreterTests/ambiguous_vars.yul index 25e0928ae653..2fca766ea177 100644 --- a/test/libyul/yulInterpreterTests/ambiguous_vars.yul +++ b/test/libyul/yulInterpreterTests/ambiguous_vars.yul @@ -12,3 +12,4 @@ // 0: 0000000000000000000000000000000000000000000000000000000000000003 // 20: 0000000000000000000000000000000000000000000000000000000000000002 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/and_create.yul b/test/libyul/yulInterpreterTests/and_create.yul index cf32c4658edb..da962c3c0b04 100644 --- a/test/libyul/yulInterpreterTests/and_create.yul +++ b/test/libyul/yulInterpreterTests/and_create.yul @@ -11,3 +11,4 @@ // Memory dump: // 0: 0000000000000000000000000000000000000000000000000000000000000001 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/and_create2.yul b/test/libyul/yulInterpreterTests/and_create2.yul index 5201b0188a76..0b83d818f087 100644 --- a/test/libyul/yulInterpreterTests/and_create2.yul +++ b/test/libyul/yulInterpreterTests/and_create2.yul @@ -13,3 +13,4 @@ // Memory dump: // 0: 0000000000000000000000000000000000000000000000000000000000000001 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/blobbasefee.yul b/test/libyul/yulInterpreterTests/blobbasefee.yul index d863b1d8265f..5dd9095e7616 100644 --- a/test/libyul/yulInterpreterTests/blobbasefee.yul +++ b/test/libyul/yulInterpreterTests/blobbasefee.yul @@ -8,3 +8,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000001 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/blobhash.yul b/test/libyul/yulInterpreterTests/blobhash.yul index 2d5ff80f03c8..48ed47c68590 100644 --- a/test/libyul/yulInterpreterTests/blobhash.yul +++ b/test/libyul/yulInterpreterTests/blobhash.yul @@ -11,3 +11,4 @@ // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 014916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5 // 0000000000000000000000000000000000000000000000000000000000000001: 0167d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/bounded_recursion.yul b/test/libyul/yulInterpreterTests/bounded_recursion.yul index ce22638ca188..48d133409ebe 100644 --- a/test/libyul/yulInterpreterTests/bounded_recursion.yul +++ b/test/libyul/yulInterpreterTests/bounded_recursion.yul @@ -14,3 +14,4 @@ // Memory dump: // 0: 0000000000000000000000000000000000000000000000000000000000000096 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/create2.yul b/test/libyul/yulInterpreterTests/create2.yul index 928a16304667..b7484f5db8de 100644 --- a/test/libyul/yulInterpreterTests/create2.yul +++ b/test/libyul/yulInterpreterTests/create2.yul @@ -11,3 +11,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000020 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/datacopy.yul b/test/libyul/yulInterpreterTests/datacopy.yul index fb8998c8bc79..ce5b6ce5c53a 100644 --- a/test/libyul/yulInterpreterTests/datacopy.yul +++ b/test/libyul/yulInterpreterTests/datacopy.yul @@ -16,3 +16,4 @@ object "main" // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 6465636f00000000000000000000000000000000000000000000000000000000 // 0000000000000000000000000000000000000000000000000000000000000001: 636f6465636f6465000000000000000000000000000000000000000000000000 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/dataoffset.yul b/test/libyul/yulInterpreterTests/dataoffset.yul index 399a8c95ceb9..f5a9aeb12f34 100644 --- a/test/libyul/yulInterpreterTests/dataoffset.yul +++ b/test/libyul/yulInterpreterTests/dataoffset.yul @@ -12,3 +12,4 @@ object "main" // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 000000000000000000000000000000000000000000000000000000000000006e // 0000000000000000000000000000000000000000000000000000000000000001: 000000000000000000000000000000000000000000000000000000000000070c +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/datasize.yul b/test/libyul/yulInterpreterTests/datasize.yul index 6e38e9321115..ff80edee7c94 100644 --- a/test/libyul/yulInterpreterTests/datasize.yul +++ b/test/libyul/yulInterpreterTests/datasize.yul @@ -12,3 +12,4 @@ object "main" // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000b64 // 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000000000000000000000000000000109 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/difficulty.yul b/test/libyul/yulInterpreterTests/difficulty.yul index 7a59bade2a47..bea70cb44dbb 100644 --- a/test/libyul/yulInterpreterTests/difficulty.yul +++ b/test/libyul/yulInterpreterTests/difficulty.yul @@ -8,3 +8,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000009999999 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/exp.yul b/test/libyul/yulInterpreterTests/exp.yul index 07e7d7f7234c..15825b8a7592 100644 --- a/test/libyul/yulInterpreterTests/exp.yul +++ b/test/libyul/yulInterpreterTests/exp.yul @@ -6,3 +6,4 @@ // Memory dump: // 0: 8e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e38e39 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/expr_nesting_depth_exceeded.yul b/test/libyul/yulInterpreterTests/expr_nesting_depth_exceeded.yul index c1456b00f355..5447dcb91c71 100644 --- a/test/libyul/yulInterpreterTests/expr_nesting_depth_exceeded.yul +++ b/test/libyul/yulInterpreterTests/expr_nesting_depth_exceeded.yul @@ -12,3 +12,4 @@ // Maximum expression nesting level reached. // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/expr_nesting_depth_not_exceeded.yul b/test/libyul/yulInterpreterTests/expr_nesting_depth_not_exceeded.yul index 0a347057129b..19aa3677f0fa 100644 --- a/test/libyul/yulInterpreterTests/expr_nesting_depth_not_exceeded.yul +++ b/test/libyul/yulInterpreterTests/expr_nesting_depth_not_exceeded.yul @@ -12,3 +12,4 @@ // Memory dump: // 0: 000000000000000000000000000000000000000000000000000000000000001f // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/external_call_to_self.yul b/test/libyul/yulInterpreterTests/external_call_to_self.yul index ad2d2bb77f7d..3f5c887b4aeb 100644 --- a/test/libyul/yulInterpreterTests/external_call_to_self.yul +++ b/test/libyul/yulInterpreterTests/external_call_to_self.yul @@ -20,3 +20,4 @@ // 100: 0000000000000000000000000000000000000000000000000000000000000042 // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000100: 0000000000000000000000000000000000000000000000000000000000000001 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/external_call_unexecuted.yul b/test/libyul/yulInterpreterTests/external_call_unexecuted.yul index 5edfe600d25a..0a5458b47e2f 100644 --- a/test/libyul/yulInterpreterTests/external_call_unexecuted.yul +++ b/test/libyul/yulInterpreterTests/external_call_unexecuted.yul @@ -8,3 +8,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000064: 0000000000000000000000000000000000000000000000000000000000000001 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/external_callcode_unexecuted.yul b/test/libyul/yulInterpreterTests/external_callcode_unexecuted.yul index 427d5604b36c..5245d7443a93 100644 --- a/test/libyul/yulInterpreterTests/external_callcode_unexecuted.yul +++ b/test/libyul/yulInterpreterTests/external_callcode_unexecuted.yul @@ -8,3 +8,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000064: 0000000000000000000000000000000000000000000000000000000000000001 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/external_delegatecall_unexecuted.yul b/test/libyul/yulInterpreterTests/external_delegatecall_unexecuted.yul index 661782589434..a827181fca2c 100644 --- a/test/libyul/yulInterpreterTests/external_delegatecall_unexecuted.yul +++ b/test/libyul/yulInterpreterTests/external_delegatecall_unexecuted.yul @@ -8,3 +8,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000064: 0000000000000000000000000000000000000000000000000000000000000001 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/external_staticcall_unexecuted.yul b/test/libyul/yulInterpreterTests/external_staticcall_unexecuted.yul index 32b50270dad6..66ea146a06ab 100644 --- a/test/libyul/yulInterpreterTests/external_staticcall_unexecuted.yul +++ b/test/libyul/yulInterpreterTests/external_staticcall_unexecuted.yul @@ -10,3 +10,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000064: 0000000000000000000000000000000000000000000000000000000000000001 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/function_calls.yul b/test/libyul/yulInterpreterTests/function_calls.yul index 2abc26380aa6..69a351ae3fed 100644 --- a/test/libyul/yulInterpreterTests/function_calls.yul +++ b/test/libyul/yulInterpreterTests/function_calls.yul @@ -11,3 +11,4 @@ // Memory dump: // Storage dump: // 000000000000000000000000000000000000000000000000000000000000000d: 000000000000000000000000000000000000000000000000000000000000002a +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/function_scopes.yul b/test/libyul/yulInterpreterTests/function_scopes.yul index 0150fa990b08..48e29a40e4ac 100644 --- a/test/libyul/yulInterpreterTests/function_scopes.yul +++ b/test/libyul/yulInterpreterTests/function_scopes.yul @@ -18,3 +18,4 @@ // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000007 // 0000000000000000000000000000000000000000000000000000000000000001: 0000000000000000000000000000000000000000000000000000000000000008 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/hex_literals.yul b/test/libyul/yulInterpreterTests/hex_literals.yul index 60e59b21932d..a0710de4e3f8 100644 --- a/test/libyul/yulInterpreterTests/hex_literals.yul +++ b/test/libyul/yulInterpreterTests/hex_literals.yul @@ -10,3 +10,4 @@ // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 112233445566778899aabbccddeeff6677889900000000000000000000000000 // 0000000000000000000000000000000000000000000000000000000000000001: 1234abcd00000000000000000000000000000000000000000000000000000000 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/infinite_recursion.yul b/test/libyul/yulInterpreterTests/infinite_recursion.yul index 0e3109ab5e07..91d1773f1788 100644 --- a/test/libyul/yulInterpreterTests/infinite_recursion.yul +++ b/test/libyul/yulInterpreterTests/infinite_recursion.yul @@ -9,3 +9,4 @@ // Interpreter execution step limit reached. // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/infinite_recursion_tracelimit.yul b/test/libyul/yulInterpreterTests/infinite_recursion_tracelimit.yul index 45d3cc781c71..207d004e3499 100644 --- a/test/libyul/yulInterpreterTests/infinite_recursion_tracelimit.yul +++ b/test/libyul/yulInterpreterTests/infinite_recursion_tracelimit.yul @@ -42,3 +42,4 @@ // Trace size limit reached. // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/leave.yul b/test/libyul/yulInterpreterTests/leave.yul index 6f082ee33e83..b127f31298ea 100644 --- a/test/libyul/yulInterpreterTests/leave.yul +++ b/test/libyul/yulInterpreterTests/leave.yul @@ -18,3 +18,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000005: 0000000000000000000000000000000000000000000000000000000000000001 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/leave_for_init.yul b/test/libyul/yulInterpreterTests/leave_for_init.yul index cd0557e71ba1..ad4965e4feac 100644 --- a/test/libyul/yulInterpreterTests/leave_for_init.yul +++ b/test/libyul/yulInterpreterTests/leave_for_init.yul @@ -16,3 +16,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000007 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/long_obect_name.yul b/test/libyul/yulInterpreterTests/long_obect_name.yul index 4105c6e42df4..ede304029997 100644 --- a/test/libyul/yulInterpreterTests/long_obect_name.yul +++ b/test/libyul/yulInterpreterTests/long_obect_name.yul @@ -17,3 +17,4 @@ object "t" { // Trace: // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/loop.yul b/test/libyul/yulInterpreterTests/loop.yul index 08ed090c33c3..cccbdc0c94c6 100644 --- a/test/libyul/yulInterpreterTests/loop.yul +++ b/test/libyul/yulInterpreterTests/loop.yul @@ -8,3 +8,4 @@ // Memory dump: // 40: 0000000000000000000000900000000000000000000000000000000000000000 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/mcopy.yul b/test/libyul/yulInterpreterTests/mcopy.yul index 7331544b8b1b..6483239cb151 100644 --- a/test/libyul/yulInterpreterTests/mcopy.yul +++ b/test/libyul/yulInterpreterTests/mcopy.yul @@ -32,3 +32,4 @@ // 100: 1111111111111111111111111111111122222222222222222222222222222222 // 120: 0000000000000000000000000000000044000000000000000000000000000000 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/mcopy_memory_access_out_of_range.yul b/test/libyul/yulInterpreterTests/mcopy_memory_access_out_of_range.yul index 45599109b5a3..d45163b8ff2a 100644 --- a/test/libyul/yulInterpreterTests/mcopy_memory_access_out_of_range.yul +++ b/test/libyul/yulInterpreterTests/mcopy_memory_access_out_of_range.yul @@ -19,3 +19,4 @@ // Memory dump: // FFFFFFFFFFFFFFE0: 0000000000000000000000000000000000000000000000000000000000000001 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_read.yul b/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_read.yul index b0b2b561e747..a20dc7b07267 100644 --- a/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_read.yul +++ b/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_read.yul @@ -10,3 +10,4 @@ // MCOPY(48, 48, 0) // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_write.yul b/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_write.yul index 0448b20c0669..69c4fedf8309 100644 --- a/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_write.yul +++ b/test/libyul/yulInterpreterTests/mcopy_memory_expansion_on_write.yul @@ -11,3 +11,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000040 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/mcopy_memory_expansion_zero_size.yul b/test/libyul/yulInterpreterTests/mcopy_memory_expansion_zero_size.yul index 0448b20c0669..69c4fedf8309 100644 --- a/test/libyul/yulInterpreterTests/mcopy_memory_expansion_zero_size.yul +++ b/test/libyul/yulInterpreterTests/mcopy_memory_expansion_zero_size.yul @@ -11,3 +11,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000040 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/mcopy_overlap.yul b/test/libyul/yulInterpreterTests/mcopy_overlap.yul index 2ac2460d7fe3..aefb84beada2 100644 --- a/test/libyul/yulInterpreterTests/mcopy_overlap.yul +++ b/test/libyul/yulInterpreterTests/mcopy_overlap.yul @@ -19,3 +19,4 @@ // 40: 1111111122222222333333334444444455555555666666661111111122222222 // 60: 3333333344444444555555556666666677777777888888880000000000000000 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul b/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul index 4e431a6b182e..ec9ca2fe6437 100644 --- a/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul +++ b/test/libyul/yulInterpreterTests/pop_byte_shr_call.yul @@ -8,3 +8,4 @@ // CALL(0, 0, 0, 0, 0, 0, 0) // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/pop_byte_shr_func.yul b/test/libyul/yulInterpreterTests/pop_byte_shr_func.yul index b520b9181cbd..e9a63dcb5a50 100644 --- a/test/libyul/yulInterpreterTests/pop_byte_shr_func.yul +++ b/test/libyul/yulInterpreterTests/pop_byte_shr_func.yul @@ -9,3 +9,4 @@ // Memory dump: // 0: 0000000000000000000000000000000000000000000000000000000000001337 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/prevrandao.yul b/test/libyul/yulInterpreterTests/prevrandao.yul index 809506c9fe56..29a5c114eaee 100644 --- a/test/libyul/yulInterpreterTests/prevrandao.yul +++ b/test/libyul/yulInterpreterTests/prevrandao.yul @@ -8,3 +8,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000010000000000000001 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/recursion.yul b/test/libyul/yulInterpreterTests/recursion.yul index 0d80faf30d62..777e01857d92 100644 --- a/test/libyul/yulInterpreterTests/recursion.yul +++ b/test/libyul/yulInterpreterTests/recursion.yul @@ -12,3 +12,4 @@ // Memory dump: // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000015 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/recursive_function_for_loop.yul b/test/libyul/yulInterpreterTests/recursive_function_for_loop.yul index b3cd4b48c0e1..9d2c08ffeb46 100644 --- a/test/libyul/yulInterpreterTests/recursive_function_for_loop.yul +++ b/test/libyul/yulInterpreterTests/recursive_function_for_loop.yul @@ -20,3 +20,4 @@ // 0: 0001000000000000000000000000000000000000000000000000000000000000 // Storage dump: // 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000002 +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/self_balance.yul b/test/libyul/yulInterpreterTests/self_balance.yul index 99b1f960c271..b61f470c8691 100644 --- a/test/libyul/yulInterpreterTests/self_balance.yul +++ b/test/libyul/yulInterpreterTests/self_balance.yul @@ -14,3 +14,4 @@ // 40: 0000000000000000000000000000000000000000000000000000000022223333 // 60: 0000000000000000000000000000000000000000000000000000000022222222 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/shadowed_symbol.yul b/test/libyul/yulInterpreterTests/shadowed_symbol.yul index 335f98a02c90..b6443f51f694 100644 --- a/test/libyul/yulInterpreterTests/shadowed_symbol.yul +++ b/test/libyul/yulInterpreterTests/shadowed_symbol.yul @@ -17,3 +17,4 @@ // Trace: // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/side_effect_free.yul b/test/libyul/yulInterpreterTests/side_effect_free.yul index 1b8107646ffc..adedd299f579 100644 --- a/test/libyul/yulInterpreterTests/side_effect_free.yul +++ b/test/libyul/yulInterpreterTests/side_effect_free.yul @@ -18,3 +18,4 @@ // Trace: // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/simple_mstore.yul b/test/libyul/yulInterpreterTests/simple_mstore.yul index 3beef347dbee..82b088e91f83 100644 --- a/test/libyul/yulInterpreterTests/simple_mstore.yul +++ b/test/libyul/yulInterpreterTests/simple_mstore.yul @@ -6,3 +6,4 @@ // Memory dump: // 20: 0000000000000000000b00000000000000000000000000000000000000000000 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/smoke.yul b/test/libyul/yulInterpreterTests/smoke.yul index f7f1a1aefb8a..f1d3564c06ae 100644 --- a/test/libyul/yulInterpreterTests/smoke.yul +++ b/test/libyul/yulInterpreterTests/smoke.yul @@ -3,3 +3,4 @@ // Trace: // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/switch_statement.yul b/test/libyul/yulInterpreterTests/switch_statement.yul index 1a067ee1bd0d..52b4319df7a7 100644 --- a/test/libyul/yulInterpreterTests/switch_statement.yul +++ b/test/libyul/yulInterpreterTests/switch_statement.yul @@ -9,3 +9,4 @@ // Memory dump: // 20: 0200000000000000000000000000000000000000000000000000000000000000 // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/transient_storage.yul b/test/libyul/yulInterpreterTests/transient_storage.yul new file mode 100644 index 000000000000..3e9466f8e405 --- /dev/null +++ b/test/libyul/yulInterpreterTests/transient_storage.yul @@ -0,0 +1,13 @@ +{ + tstore(0, 8) + mstore(0, tload(0)) +} +// ==== +// EVMVersion: >=cancun +// ---- +// Trace: +// Memory dump: +// 0: 0000000000000000000000000000000000000000000000000000000000000008 +// Storage dump: +// Transient storage dump: +// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000008 diff --git a/test/libyul/yulInterpreterTests/zero_length_reads.yul b/test/libyul/yulInterpreterTests/zero_length_reads.yul index 6c1f38d1e9ff..661db49ffbb6 100644 --- a/test/libyul/yulInterpreterTests/zero_length_reads.yul +++ b/test/libyul/yulInterpreterTests/zero_length_reads.yul @@ -38,3 +38,4 @@ // RETURN(0, 0) // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/zero_length_reads_and_revert.yul b/test/libyul/yulInterpreterTests/zero_length_reads_and_revert.yul index 4be49eeb0977..0d0f92420e30 100644 --- a/test/libyul/yulInterpreterTests/zero_length_reads_and_revert.yul +++ b/test/libyul/yulInterpreterTests/zero_length_reads_and_revert.yul @@ -38,3 +38,4 @@ // REVERT(0, 0) // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulInterpreterTests/zero_range.yul b/test/libyul/yulInterpreterTests/zero_range.yul index 1ad25ed37bca..50b604358846 100644 --- a/test/libyul/yulInterpreterTests/zero_range.yul +++ b/test/libyul/yulInterpreterTests/zero_range.yul @@ -8,3 +8,4 @@ // CALLDATACOPY(0, 0, 0) // Memory dump: // Storage dump: +// Transient storage dump: diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/transient_storage.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/transient_storage.yul new file mode 100644 index 000000000000..f50e1ba06ab5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/transient_storage.yul @@ -0,0 +1,25 @@ +{ + tstore(0, 42) + tstore(0, 42) + let x := tload(0) + tstore(0, 42) + x := tload(0) + tstore(0, 24) + x := tload(0) + tstore(0, 24) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: equalStoreEliminator +// +// { +// tstore(0, 42) +// tstore(0, 42) +// let x := tload(0) +// tstore(0, 42) +// x := tload(0) +// tstore(0, 24) +// x := tload(0) +// tstore(0, 24) +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/transient_storage.yul b/test/libyul/yulOptimizerTests/fullSuite/transient_storage.yul new file mode 100644 index 000000000000..44cd0a7f553e --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/transient_storage.yul @@ -0,0 +1,21 @@ +{ + tstore(0, 13) + tstore(0, 42) + tstore(0x20, tload(0)) + let x:= tload(0x20) + tstore(0, x) + pop(tload(0x20)) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: fullSuite +// +// { +// { +// tstore(0, 13) +// tstore(0, 42) +// tstore(0x20, tload(0)) +// tstore(0, tload(0x20)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_transient_storage.yul b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_transient_storage.yul new file mode 100644 index 000000000000..4c766c0bc8eb --- /dev/null +++ b/test/libyul/yulOptimizerTests/loopInvariantCodeMotion/no_move_transient_storage.yul @@ -0,0 +1,20 @@ +{ + for { let i := 1 } iszero(eq(i, 10)) { i := add(i, 1) } + { + tstore(0, i) + sstore(i, tload(0)) + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: loopInvariantCodeMotion +// +// { +// let i := 1 +// for { } iszero(eq(i, 10)) { i := add(i, 1) } +// { +// tstore(0, i) +// sstore(i, tload(0)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/transient_storage.yul b/test/libyul/yulOptimizerTests/unusedPruner/transient_storage.yul new file mode 100644 index 000000000000..de0832053b9f --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/transient_storage.yul @@ -0,0 +1,20 @@ +{ + tstore(0, 5) + let x:= tload(0) + tstore(0, 8) + let y := tload(0) + tstore(0, y) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedPruner +// +// { +// { +// tstore(0, 5) +// tstore(0, 8) +// let y := tload(0) +// tstore(0, y) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/tload.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/tload.yul new file mode 100644 index 000000000000..43b96f0e92a3 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/tload.yul @@ -0,0 +1,22 @@ +{ + let zero := 0 + tstore(zero, 5) + let x := tload(zero) + tstore(zero, 8) + let y := tload(zero) + tstore(zero, y) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// let zero := 0 +// tstore(zero, 5) +// let x := tload(zero) +// tstore(zero, 8) +// tstore(zero, tload(zero)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/unusedStoreEliminator/tstore.yul b/test/libyul/yulOptimizerTests/unusedStoreEliminator/tstore.yul new file mode 100644 index 000000000000..f3befb79d0ad --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedStoreEliminator/tstore.yul @@ -0,0 +1,23 @@ +{ + let x := 5 + tstore(x, 10) + sstore(x, 10) + pop(mload(0)) + tstore(x, 10) + sstore(x, 20) +} +// ==== +// EVMVersion: >=cancun +// ---- +// step: unusedStoreEliminator +// +// { +// { +// let x := 5 +// tstore(x, 10) +// let _2 := 10 +// pop(mload(0)) +// tstore(x, 10) +// sstore(x, 20) +// } +// } diff --git a/test/libyul/yulSyntaxTests/tload_as_identifier_post_cancun.yul b/test/libyul/yulSyntaxTests/tload_as_identifier_post_cancun.yul new file mode 100644 index 000000000000..2c1dcb0d92cf --- /dev/null +++ b/test/libyul/yulSyntaxTests/tload_as_identifier_post_cancun.yul @@ -0,0 +1,9 @@ +{ + function f() { + let tload := 0 + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// ParserError 5568: (33-38): Cannot use builtin function name "tload" as identifier name. diff --git a/test/libyul/yulSyntaxTests/tstore_as_identifier_post_cancun.yul b/test/libyul/yulSyntaxTests/tstore_as_identifier_post_cancun.yul new file mode 100644 index 000000000000..d810ed2b38aa --- /dev/null +++ b/test/libyul/yulSyntaxTests/tstore_as_identifier_post_cancun.yul @@ -0,0 +1,7 @@ +{ + function tstore() {} +} +// ==== +// EVMVersion: >=cancun +// ---- +// ParserError 5568: (15-21): Cannot use builtin function name "tstore" as identifier name. diff --git a/test/libyul/yulSyntaxTests/tstore_tload.yul b/test/libyul/yulSyntaxTests/tstore_tload.yul new file mode 100644 index 000000000000..e73429351d43 --- /dev/null +++ b/test/libyul/yulSyntaxTests/tstore_tload.yul @@ -0,0 +1,11 @@ +{ + function f() { + let x := tload(0) + let y := add(x, 1) + tstore(0, y) + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// Warning 2394: (82-88): Transient storage as defined by EIP-1153 can break the composability of smart contracts: Since transient storage is cleared only at the end of the transaction and not at the end of the outermost call frame to the contract within a transaction, your contract may unintentionally misbehave when invoked multiple times in a complex transaction. To avoid this, be sure to clear all transient storage at the end of any call to your contract. The use of transient storage for reentrancy guards that are cleared at the end of the call is safe. diff --git a/test/libyul/yulSyntaxTests/tstore_tload_as_identifiers_pre_cancun.yul b/test/libyul/yulSyntaxTests/tstore_tload_as_identifiers_pre_cancun.yul new file mode 100644 index 000000000000..19e9d2f5280b --- /dev/null +++ b/test/libyul/yulSyntaxTests/tstore_tload_as_identifiers_pre_cancun.yul @@ -0,0 +1,6 @@ +{ + function tstore() {} + function tload() {} +} +// ==== +// EVMVersion: storage; + std::map transientStorage; util::h160 address = util::h160("0x0000000000000000000000000000000011111111"); u256 balance = 0x22222222; u256 selfbalance = 0x22223333; @@ -125,6 +126,8 @@ struct InterpreterState void dumpTraceAndState(std::ostream& _out, bool _disableMemoryTrace) const; /// Prints non-zero storage to @param _out. void dumpStorage(std::ostream& _out) const; + /// Prints non-zero transient storage to @param _out. + void dumpTransientStorage(std::ostream& _out) const; bytes readMemory(u256 const& _offset, u256 const& _size) { From 261e8b9f7d63c2b0cfe0c7d45d1857d264ff3610 Mon Sep 17 00:00:00 2001 From: r0qs Date: Tue, 16 Jan 2024 17:17:28 -0300 Subject: [PATCH 068/189] Test new semantic of selfdestruct added by EIP-6780 --- libsolidity/analysis/TypeChecker.cpp | 7 +- libyul/AsmAnalysis.cpp | 7 +- test/EVMHost.cpp | 12 ++- test/EVMHost.h | 5 + .../semanticTests/various/selfdestruct.sol | 46 -------- .../various/selfdestruct_post_cancun.sol | 88 +++++++++++++++ ...uct_post_cancun_multiple_beneficiaries.sol | 56 ++++++++++ .../selfdestruct_post_cancun_redeploy.sol | 101 ++++++++++++++++++ .../various/selfdestruct_pre_cancun.sol | 86 +++++++++++++++ ...ruct_pre_cancun_multiple_beneficiaries.sol | 67 ++++++++++++ .../selfdestruct_pre_cancun_redeploy.sol | 100 +++++++++++++++++ .../globalFunctions/selfdestruct_no_call.sol | 6 ++ .../430_bare_selfdestruct.sol | 6 -- .../address_nonpayable_selfdestruct.sol | 2 +- .../address/address_payable_selfdestruct.sol | 2 +- .../viewPureChecker/builtin_functions.sol | 2 +- .../builtin_functions_view_fail.sol | 2 +- .../inline_assembly_instructions_allowed.sol | 2 +- ..._assembly_instructions_disallowed_pure.sol | 2 +- ..._assembly_instructions_disallowed_view.sol | 2 +- test/libyul/yulSyntaxTests/selfdestruct.yul | 2 +- 21 files changed, 538 insertions(+), 65 deletions(-) delete mode 100644 test/libsolidity/semanticTests/various/selfdestruct.sol create mode 100644 test/libsolidity/semanticTests/various/selfdestruct_post_cancun.sol create mode 100644 test/libsolidity/semanticTests/various/selfdestruct_post_cancun_multiple_beneficiaries.sol create mode 100644 test/libsolidity/semanticTests/various/selfdestruct_post_cancun_redeploy.sol create mode 100644 test/libsolidity/semanticTests/various/selfdestruct_pre_cancun.sol create mode 100644 test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_multiple_beneficiaries.sol create mode 100644 test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_redeploy.sol create mode 100644 test/libsolidity/syntaxTests/globalFunctions/selfdestruct_no_call.sol delete mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/430_bare_selfdestruct.sol diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 11812878a5da..91224610f55a 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -3767,8 +3767,11 @@ bool TypeChecker::visit(Identifier const& _identifier) 5159_error, _identifier.location(), "\"selfdestruct\" has been deprecated. " - "The underlying opcode will eventually undergo breaking changes, " - "and its use is not recommended." + "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/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index c4dade689830..c73cab74fedd 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -316,8 +316,11 @@ std::vector AsmAnalyzer::operator()(FunctionCall const& _funCall) 1699_error, nativeLocationOf(_funCall.functionName), "\"selfdestruct\" has been deprecated. " - "The underlying opcode will eventually undergo breaking changes, " - "and its use is not recommended." + "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." ); parameterTypes = &f->parameters; returnTypes = &f->returns; diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 84327a5b977f..66c3fdf19d20 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -193,7 +193,12 @@ void EVMHost::newTransactionFrame() } // Process selfdestruct list for (auto& [address, _]: recorded_selfdestructs) - accounts.erase(address); + if (m_evmVersion < langutil::EVMVersion::cancun() || newlyCreatedAccounts.count(address)) + // EIP-6780: If SELFDESTRUCT is executed in a transaction different from the one + // in which it was created, we do NOT record it or clear any data. + // Otherwise, the previous behavior (pre-Cancun) is maintained. + accounts.erase(address); + newlyCreatedAccounts.clear(); recorded_selfdestructs.clear(); } @@ -208,6 +213,8 @@ bool EVMHost::selfdestruct(const evmc::address& _addr, const evmc::address& _ben { // TODO actual selfdestruct is even more complicated. + // NOTE: EIP-6780: The transfer of the entire account balance to the beneficiary should still happen + // after cancun. transfer(accounts[_addr], accounts[_beneficiary], convertFromEVMC(accounts[_addr].balance)); // Record self destructs. Clearing will be done in newTransactionFrame(). @@ -343,6 +350,9 @@ evmc::Result EVMHost::call(evmc_message const& _message) noexcept code = accounts[message.code_address].code; auto& destination = accounts[message.recipient]; + if (message.kind == EVMC_CREATE || message.kind == EVMC_CREATE2) + // Mark account as created if it is a CREATE or CREATE2 call + newlyCreatedAccounts.emplace(message.recipient); if (value != 0 && message.kind != EVMC_DELEGATECALL && message.kind != EVMC_CALLCODE) { diff --git a/test/EVMHost.h b/test/EVMHost.h index 967adc2486aa..144596e05e38 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -32,6 +32,8 @@ #include +#include + namespace solidity::test { using Address = util::h160; @@ -127,6 +129,9 @@ class EVMHost: public evmc::MockedHost static evmc::Result resultWithGas(int64_t gas_limit, int64_t gas_required, bytes const& _data) noexcept; static evmc::Result resultWithFailure() noexcept; + /// Store the accounts that have been created in the current transaction. + std::unordered_set newlyCreatedAccounts; + evmc::VM& m_vm; /// EVM version requested by the testing tool langutil::EVMVersion m_evmVersion; diff --git a/test/libsolidity/semanticTests/various/selfdestruct.sol b/test/libsolidity/semanticTests/various/selfdestruct.sol deleted file mode 100644 index 6aa42cb14413..000000000000 --- a/test/libsolidity/semanticTests/various/selfdestruct.sol +++ /dev/null @@ -1,46 +0,0 @@ -contract C { - event Terminated(); - - constructor() payable { - } - - function terminate() external { - emit Terminated(); - selfdestruct(payable(msg.sender)); - // Execution stops here, so the second one is not executed. - selfdestruct(payable(msg.sender)); - emit Terminated(); - } -} - -contract D { - C public c; - - constructor() payable { - c = new C{value: 1 ether}(); - } - - function f() external { - c.terminate(); - } - - function exists() external returns (bool) { - return address(c).code.length != 0; - } -} -// ---- -// constructor(), 1 ether -> -// gas irOptimized: 186958 -// gas legacy: 255973 -// gas legacyOptimized: 178919 -// c() -> 0x137aa4dfc0911524504fcd4d98501f179bc13b4a -// balance: 0x137aa4dfc0911524504fcd4d98501f179bc13b4a -> 1000000000000000000 -// balance -> 0 -// exists() -> true -// f() -> -// ~ emit Terminated() from 0x137aa4dfc0911524504fcd4d98501f179bc13b4a -// balance: 0x137aa4dfc0911524504fcd4d98501f179bc13b4a -> 0 -// ~ emit Terminated() from 0x137aa4dfc0911524504fcd4d98501f179bc13b4a -// balance -> 1000000000000000000 -// ~ emit Terminated() from 0x137aa4dfc0911524504fcd4d98501f179bc13b4a -// exists() -> false diff --git a/test/libsolidity/semanticTests/various/selfdestruct_post_cancun.sol b/test/libsolidity/semanticTests/various/selfdestruct_post_cancun.sol new file mode 100644 index 000000000000..85c95be44795 --- /dev/null +++ b/test/libsolidity/semanticTests/various/selfdestruct_post_cancun.sol @@ -0,0 +1,88 @@ +contract C { + constructor() payable {} + + function terminate() external { + // NOTE: The contract `c` should still exist in the test below + // when the call to selfdestruct is executed in a transaction + // different from the one in which the contract was created. + // However, it should still send all Ether in `c` to the beneficiary. + selfdestruct(payable(msg.sender)); + assert(false); + } +} + +contract D { + C public c; + + constructor() payable {} + + function deploy_create() public payable { + c = new C{value: 1 ether}(); + } + + function deploy_create2() public payable { + c = new C{value: 1 ether, salt: hex"1234"}(); + } + + function terminate() public { + // NOTE: A second call to `c.terminate()` or any other function of the contract `c` will succeed if the + // previous selfdestruct was performed in a different transaction, since the contract will still exists. + c.terminate(); + } + + function test_create_and_terminate() public { + deploy_create(); + assert(exists()); + test_balance_after_create(); + terminate(); + test_balance_after_selfdestruct(); + } + + function test_create2_and_terminate() public { + deploy_create2(); + assert(exists()); + test_balance_after_create(); + terminate(); + test_balance_after_selfdestruct(); + } + + function test_balance_after_create() public view { + assert(address(this).balance == 0); + assert(address(c).balance == 1 ether); + } + + function test_balance_after_selfdestruct() public view { + assert(address(this).balance == 1 ether); + assert(address(c).balance == 0); + } + + function exists() public view returns (bool) { + return address(c).code.length != 0; + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// constructor(), 1 ether -> +// gas irOptimized: 455954 +// gas legacy: 375495 +// gas legacyOptimized: 507630 +// exists() -> false +// test_create_and_terminate() -> +// exists() -> false +// terminate() -> FAILURE +// deploy_create() -> +// test_balance_after_create() -> +// exists() -> true +// terminate() -> +// test_balance_after_selfdestruct() -> +// exists() -> true +// test_create2_and_terminate() -> +// exists() -> false +// deploy_create2() -> +// test_balance_after_create() -> +// exists() -> true +// terminate() -> +// test_balance_after_selfdestruct() -> +// exists() -> true +// terminate() -> diff --git a/test/libsolidity/semanticTests/various/selfdestruct_post_cancun_multiple_beneficiaries.sol b/test/libsolidity/semanticTests/various/selfdestruct_post_cancun_multiple_beneficiaries.sol new file mode 100644 index 000000000000..4bc65b760811 --- /dev/null +++ b/test/libsolidity/semanticTests/various/selfdestruct_post_cancun_multiple_beneficiaries.sol @@ -0,0 +1,56 @@ +contract C { + constructor() payable {} + function terminate(address _beneficiary) public { + selfdestruct(payable(_beneficiary)); + assert(false); + } +} + +contract D { + address account1 = payable(0x1111111111111111111111111111111111111111); + address account2 = payable(0x2222222222222222222222222222222222222222); + C public c; + + constructor() payable {} + + function deploy() public payable { + c = new C{value: 1 ether}(); + } + + function terminate(address _beneficiary) public { + c.terminate(_beneficiary); + } + + function test_deploy_and_terminate_twice() public { + deploy(); + terminate(account1); + terminate(account2); + } + + function exists() public view returns (bool) { + return address(c).code.length != 0; + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// constructor(), 2 ether -> +// balance: 0x1111111111111111111111111111111111111111 -> 0 +// balance: 0x2222222222222222222222222222222222222222 -> 0 +// balance -> 2000000000000000000 +// exists() -> false +// test_deploy_and_terminate_twice() -> +// exists() -> false +// balance: 0x1111111111111111111111111111111111111111 -> 1000000000000000000 +// balance: 0x2222222222222222222222222222222222222222 -> 0 +// balance -> 1000000000000000000 +// deploy() -> +// exists() -> true +// balance: 0x1111111111111111111111111111111111111111 -> 1000000000000000000 +// balance: 0x2222222222222222222222222222222222222222 -> 0 +// balance -> 0 +// terminate(address): 0x1111111111111111111111111111111111111111 -> +// balance: 0x1111111111111111111111111111111111111111 -> 2000000000000000000 +// balance: 0x2222222222222222222222222222222222222222 -> 0 +// balance -> 0 +// exists() -> true diff --git a/test/libsolidity/semanticTests/various/selfdestruct_post_cancun_redeploy.sol b/test/libsolidity/semanticTests/various/selfdestruct_post_cancun_redeploy.sol new file mode 100644 index 000000000000..473571bf1391 --- /dev/null +++ b/test/libsolidity/semanticTests/various/selfdestruct_post_cancun_redeploy.sol @@ -0,0 +1,101 @@ +contract Factory { + event Deployed(address, bytes32); + + function deploy(bytes32 _salt) external payable returns (address implAddr) { + // NOTE: The bytecode of contract C is used here instead of `type(C).creationCode` since the address calculation depends on the precise init code + // and that will change in our test framework between legacy and via-IR codegen and via optimized vs non-optimized. + //contract C { + // constructor() payable {} + // function terminate() external { + // selfdestruct(payable(msg.sender)); + // } + //} + bytes memory initCode = + hex"6080806040526068908160108239f3fe6004361015600b575f80fd5b5f3560e0" + hex"1c630c08bf8814601d575f80fd5b34602e575f366003190112602e5733ff5b5f" + hex"80fdfea2646970667358221220fe3c4fe66c1838016e2efdc5b65538e5ff3dbf" + hex"ced7eff135da3556db4bd841aa64736f6c63430008180033"; + + address target = address(uint160(uint256(keccak256(abi.encodePacked( + bytes1(0xff), + address(this), + _salt, + keccak256(abi.encodePacked(initCode)) + ))))); + + assembly { + implAddr := create2(callvalue(), add(initCode, 0x20), mload(initCode), _salt) + if iszero(extcodesize(implAddr)) { + revert(0, 0) + } + } + assert(address(implAddr) == target); + emit Deployed(implAddr, _salt); + } +} + +interface IC { + function terminate() external; +} + +contract D { + Factory public factory = new Factory(); + IC public c; + + constructor() payable {} + + function deploy_create2() public payable { + // NOTE: `create2` cannot be used anymore to redeploy a contract in the same place to make it upgradable + // if not performed in the same transaction. + // Therefore, an attempt to redeploy using `create2` in a different transaction must revert for EVM >= Cancun. + c = IC(factory.deploy{value: 1 ether}(hex"1234")); + } + + function terminate() public { + c.terminate(); + } + + function test_deploy_and_terminate() public { + deploy_create2(); + assert(exists()); + test_balance_after_create(); + terminate(); + test_balance_after_selfdestruct(); + } + + function test_balance_after_create() public view { + assert(address(this).balance == 0); + assert(address(c).balance == 1 ether); + } + + function test_balance_after_selfdestruct() public view { + assert(address(this).balance == 1 ether); + assert(address(c).balance == 0); + } + + function exists() public view returns (bool) { + return address(c).code.length != 0; + } +} + +// ==== +// EVMVersion: >=cancun +// ---- +// constructor(), 1 ether -> +// gas irOptimized: 562058 +// gas legacy: 633310 +// gas legacyOptimized: 590051 +// exists() -> false +// test_deploy_and_terminate() -> +// ~ emit Deployed(address,bytes32) from 0x137aa4dfc0911524504fcd4d98501f179bc13b4a: 0x7e6580007e709ac52945fae182c61131d42634e8, 0x1234000000000000000000000000000000000000000000000000000000000000 +// gas irOptimized: 118541 +// gas legacyOptimized: 118305 +// exists() -> false +// deploy_create2() -> +// ~ emit Deployed(address,bytes32) from 0x137aa4dfc0911524504fcd4d98501f179bc13b4a: 0x7e6580007e709ac52945fae182c61131d42634e8, 0x1234000000000000000000000000000000000000000000000000000000000000 +// test_balance_after_create() -> +// exists() -> true +// terminate() -> +// test_balance_after_selfdestruct() -> +// exists() -> true +// deploy_create2() -> FAILURE diff --git a/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun.sol b/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun.sol new file mode 100644 index 000000000000..4142eb25cf2d --- /dev/null +++ b/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun.sol @@ -0,0 +1,86 @@ +contract C { + constructor() payable {} + + function terminate() external { + // NOTE: The contract `c` should still exists in the test below, + // since the call to the selfdestruct method was done in a tx that is + // not the same tx that the contract was created. + // However, it should send all Ether in `c` to the beneficiary. + selfdestruct(payable(msg.sender)); + assert(false); + } +} + +contract D { + C public c; + + constructor() payable {} + + function deploy_create() public payable { + c = new C{value: 1 ether}(); + } + + function deploy_create2() public payable { + c = new C{value: 1 ether, salt: hex"1234"}(); + } + + function terminate() public { + c.terminate(); + } + + function test_create_and_terminate() public { + deploy_create(); + assert(exists()); + test_balance_after_create(); + terminate(); + test_balance_after_selfdestruct(); + } + + function test_create2_and_terminate() public { + deploy_create2(); + assert(exists()); + test_balance_after_create(); + terminate(); + test_balance_after_selfdestruct(); + } + + function test_balance_after_create() public view { + assert(address(this).balance == 0); + assert(address(c).balance == 1 ether); + } + + function test_balance_after_selfdestruct() public view { + assert(address(this).balance == 1 ether); + assert(address(c).balance == 0); + } + + function exists() public view returns (bool) { + return address(c).code.length != 0; + } +} +// ==== +// EVMVersion: =shanghai +// ---- +// constructor(), 1 ether -> +// gas irOptimized: 242428 +// gas legacy: 373563 +// gas legacyOptimized: 234516 +// exists() -> false +// test_create_and_terminate() -> +// exists() -> false +// terminate() -> FAILURE +// deploy_create() -> +// test_balance_after_create() -> +// exists() -> true +// terminate() -> +// test_balance_after_selfdestruct() -> +// exists() -> false +// test_create2_and_terminate() -> +// exists() -> false +// deploy_create2() -> +// test_balance_after_create() -> +// exists() -> true +// terminate() -> +// test_balance_after_selfdestruct() -> +// exists() -> false +// terminate() -> FAILURE diff --git a/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_multiple_beneficiaries.sol b/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_multiple_beneficiaries.sol new file mode 100644 index 000000000000..57b2d5c66ac4 --- /dev/null +++ b/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_multiple_beneficiaries.sol @@ -0,0 +1,67 @@ +contract C { + constructor() payable {} + function terminate(address _beneficiary) public { + selfdestruct(payable(_beneficiary)); + assert(false); + } +} + +contract D { + address account1 = payable(0x1111111111111111111111111111111111111111); + address account2 = payable(0x2222222222222222222222222222222222222222); + C public c; + + constructor() payable {} + + function deploy() public payable { + c = new C{value: 1 ether}(); + } + + function terminate(address _beneficiary) public { + c.terminate(_beneficiary); + } + + function test_deploy_and_terminate_twice() public { + deploy(); + terminate(account1); + terminate(account2); + } + + function exists() public view returns (bool) { + return address(c).code.length != 0; + } +} +// ==== +// EVMVersion: <=shanghai +// ---- +// constructor(), 2 ether -> +// gas irOptimized: 223918 +// gas legacy: 374228 +// gas legacyOptimized: 239815 +// balance: 0x1111111111111111111111111111111111111111 -> 0 +// balance: 0x2222222222222222222222222222222222222222 -> 0 +// balance -> 2000000000000000000 +// exists() -> false +// test_deploy_and_terminate_twice() -> +// gas irOptimized: 135350 +// gas legacy: 165584 +// gas legacyOptimized: 144396 +// exists() -> false +// balance: 0x1111111111111111111111111111111111111111 -> 1000000000000000000 +// balance: 0x2222222222222222222222222222222222222222 -> 0 +// balance -> 1000000000000000000 +// deploy() -> +// gas legacy: 101691 +// exists() -> true +// balance: 0x1111111111111111111111111111111111111111 -> 1000000000000000000 +// balance: 0x2222222222222222222222222222222222222222 -> 0 +// balance -> 0 +// terminate(address): 0x1111111111111111111111111111111111111111 -> +// balance: 0x1111111111111111111111111111111111111111 -> 2000000000000000000 +// balance: 0x2222222222222222222222222222222222222222 -> 0 +// balance -> 0 +// terminate(address): 0x2222222222222222222222222222222222222222 -> FAILURE +// balance: 0x1111111111111111111111111111111111111111 -> 2000000000000000000 +// balance: 0x2222222222222222222222222222222222222222 -> 0 +// balance -> 0 +// exists() -> false diff --git a/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_redeploy.sol b/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_redeploy.sol new file mode 100644 index 000000000000..9c9949854a0a --- /dev/null +++ b/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_redeploy.sol @@ -0,0 +1,100 @@ +contract Factory { + event Deployed(address, bytes32); + + function deploy(bytes32 _salt) external payable returns (address implAddr) { + // NOTE: The bytecode of contract C is used here instead of `type(C).creationCode` since the address calculation depends on the precise init code + // and that will change in our test framework between legacy and via-IR codegen and via optimized vs non-optimized. + //contract C { + // constructor() payable {} + // function terminate() external { + // selfdestruct(payable(msg.sender)); + // } + //} + bytes memory initCode = + hex"6080806040526068908160108239f3fe6004361015600b575f80fd5b5f3560e0" + hex"1c630c08bf8814601d575f80fd5b34602e575f366003190112602e5733ff5b5f" + hex"80fdfea2646970667358221220fe3c4fe66c1838016e2efdc5b65538e5ff3dbf" + hex"ced7eff135da3556db4bd841aa64736f6c63430008180033"; + + address target = address(uint160(uint256(keccak256(abi.encodePacked( + bytes1(0xff), + address(this), + _salt, + keccak256(abi.encodePacked(initCode)) + ))))); + + assembly { + implAddr := create2(callvalue(), add(initCode, 0x20), mload(initCode), _salt) + if iszero(extcodesize(implAddr)) { + revert(0, 0) + } + } + assert(address(implAddr) == target); + emit Deployed(implAddr, _salt); + } +} + +interface IC { + function terminate() external; +} + +contract D { + Factory public factory = new Factory(); + IC public c; + + constructor() payable {} + + function deploy_create2() public payable { + c = IC(factory.deploy{value: 1 ether}(hex"1234")); + } + + function terminate() public { + c.terminate(); + } + + function test_deploy_and_terminate() public { + deploy_create2(); + assert(exists()); + test_balance_after_create(); + terminate(); + test_balance_after_selfdestruct(); + } + + function test_balance_after_create() public view { + assert(address(this).balance == 0); + assert(address(c).balance == 1 ether); + } + + function test_balance_after_selfdestruct() public view { + assert(address(this).balance == 1 ether); + assert(address(c).balance == 0); + } + + function exists() public view returns (bool) { + return address(c).code.length != 0; + } +} + +// ==== +// EVMVersion: =shanghai +// ---- +// constructor(), 1 ether -> +// gas irOptimized: 430265 +// gas legacy: 690264 +// gas legacyOptimized: 412819 +// exists() -> false +// test_deploy_and_terminate() -> +// ~ emit Deployed(address,bytes32) from 0x137aa4dfc0911524504fcd4d98501f179bc13b4a: 0x7e6580007e709ac52945fae182c61131d42634e8, 0x1234000000000000000000000000000000000000000000000000000000000000 +// gas irOptimized: 117489 +// gas legacy: 118895 +// gas legacyOptimized: 117137 +// exists() -> false +// deploy_create2() -> +// ~ emit Deployed(address,bytes32) from 0x137aa4dfc0911524504fcd4d98501f179bc13b4a: 0x7e6580007e709ac52945fae182c61131d42634e8, 0x1234000000000000000000000000000000000000000000000000000000000000 +// test_balance_after_create() -> +// exists() -> true +// terminate() -> +// test_balance_after_selfdestruct() -> +// exists() -> false +// deploy_create2() -> +// ~ emit Deployed(address,bytes32) from 0x137aa4dfc0911524504fcd4d98501f179bc13b4a: 0x7e6580007e709ac52945fae182c61131d42634e8, 0x1234000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/syntaxTests/globalFunctions/selfdestruct_no_call.sol b/test/libsolidity/syntaxTests/globalFunctions/selfdestruct_no_call.sol new file mode 100644 index 000000000000..e8a657bdf348 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/selfdestruct_no_call.sol @@ -0,0 +1,6 @@ +contract C { + function f() pure public { selfdestruct; } +} +// ---- +// Warning 5159: (44-56): "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. +// Warning 6133: (44-56): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/430_bare_selfdestruct.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/430_bare_selfdestruct.sol deleted file mode 100644 index 6662d7b29964..000000000000 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/430_bare_selfdestruct.sol +++ /dev/null @@ -1,6 +0,0 @@ -contract C { - function f() pure public { selfdestruct; } -} -// ---- -// Warning 5159: (44-56): "selfdestruct" has been deprecated. The underlying opcode will eventually undergo breaking changes, and its use is not recommended. -// Warning 6133: (44-56): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/types/address/address_nonpayable_selfdestruct.sol b/test/libsolidity/syntaxTests/types/address/address_nonpayable_selfdestruct.sol index 9122d2a89429..0a467757027a 100644 --- a/test/libsolidity/syntaxTests/types/address/address_nonpayable_selfdestruct.sol +++ b/test/libsolidity/syntaxTests/types/address/address_nonpayable_selfdestruct.sol @@ -4,5 +4,5 @@ contract C { } } // ---- -// Warning 5159: (56-68): "selfdestruct" has been deprecated. The underlying opcode will eventually undergo breaking changes, and its use is not recommended. +// Warning 5159: (56-68): "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 9553: (69-70): Invalid type for argument in function call. Invalid implicit conversion from address to address payable requested. diff --git a/test/libsolidity/syntaxTests/types/address/address_payable_selfdestruct.sol b/test/libsolidity/syntaxTests/types/address/address_payable_selfdestruct.sol index bd1aff795277..754ff2c67488 100644 --- a/test/libsolidity/syntaxTests/types/address/address_payable_selfdestruct.sol +++ b/test/libsolidity/syntaxTests/types/address/address_payable_selfdestruct.sol @@ -4,4 +4,4 @@ contract C { } } // ---- -// Warning 5159: (64-76): "selfdestruct" has been deprecated. The underlying opcode will eventually undergo breaking changes, and its use is not recommended. +// Warning 5159: (64-76): "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.sol b/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions.sol index 3ee940679873..4d1710a3a160 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions.sol @@ -19,4 +19,4 @@ contract C { receive() payable external {} } // ---- -// Warning 5159: (122-134): "selfdestruct" has been deprecated. The underlying opcode will eventually undergo breaking changes, and its use is not recommended. +// 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 f458aaad5dfa..4f9efcdb5dd4 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions_view_fail.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions_view_fail.sol @@ -20,7 +20,7 @@ contract C { } } // ---- -// Warning 5159: (201-213): "selfdestruct" has been deprecated. The underlying opcode will eventually undergo breaking changes, and its use is not recommended. +// 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. // TypeError 8961: (201-228): Function cannot be declared as view because this expression (potentially) modifies the state. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed.sol index b3753706cfb0..886d6de0ce2f 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_allowed.sol @@ -85,7 +85,7 @@ contract C { // ==== // EVMVersion: >=paris // ---- -// Warning 1699: (1754-1766): "selfdestruct" has been deprecated. The underlying opcode will eventually undergo breaking changes, and its use is not recommended. +// Warning 1699: (1754-1766): "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. // Warning 5740: (89-1716): Unreachable code. // Warning 5740: (1729-1741): Unreachable code. // Warning 5740: (1754-1769): Unreachable code. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure.sol index be84b801f2ab..665a17eebf63 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_pure.sol @@ -35,7 +35,7 @@ contract C { } } // ---- -// Warning 1699: (498-510): "selfdestruct" has been deprecated. The underlying opcode will eventually undergo breaking changes, and its use is not recommended. +// Warning 1699: (498-510): "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. // Warning 5740: (526-853): Unreachable code. // TypeError 2527: (79-87): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". // TypeError 8961: (101-113): Function cannot be declared as pure because this expression (potentially) modifies the state. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_view.sol b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_view.sol index cd69bed793c8..b2f241ff08c5 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_view.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/inline_assembly_instructions_disallowed_view.sol @@ -23,7 +23,7 @@ contract C { // ==== // EVMVersion: >=london // ---- -// Warning 1699: (308-320): "selfdestruct" has been deprecated. The underlying opcode will eventually undergo breaking changes, and its use is not recommended. +// Warning 1699: (308-320): "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. // Warning 5740: (336-468): Unreachable code. // TypeError 8961: (75-87): Function cannot be declared as view because this expression (potentially) modifies the state. // TypeError 8961: (104-119): Function cannot be declared as view because this expression (potentially) modifies the state. diff --git a/test/libyul/yulSyntaxTests/selfdestruct.yul b/test/libyul/yulSyntaxTests/selfdestruct.yul index 745fdc5c5afd..faecf92c3805 100644 --- a/test/libyul/yulSyntaxTests/selfdestruct.yul +++ b/test/libyul/yulSyntaxTests/selfdestruct.yul @@ -2,4 +2,4 @@ selfdestruct(0x02) } // ---- -// Warning 1699: (3-15): "selfdestruct" has been deprecated. The underlying opcode will eventually undergo breaking changes, and its use is not recommended. +// Warning 1699: (3-15): "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. From ea0b75089bc999604b541eb042e39a8ac3648484 Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Mon, 11 Dec 2023 15:52:47 +0100 Subject: [PATCH 069/189] Add support for apple silicon. --- .circleci/compare_bytecode_reports.sh | 1 + .circleci/config.yml | 29 +++++++- .circleci/osx_install_dependencies.sh | 68 ++++++++++++++----- .circleci/parallel_bytecode_report.sh | 5 +- cmake/jsoncpp.cmake | 2 + scripts/bytecodecompare/prepare_report.py | 30 +++++++- scripts/install_obsolete_jsoncpp_1_7_4.sh | 2 +- .../test_bytecodecompare_prepare_report.py | 9 ++- 8 files changed, 120 insertions(+), 26 deletions(-) diff --git a/.circleci/compare_bytecode_reports.sh b/.circleci/compare_bytecode_reports.sh index 7b62656bf24e..7574b0656c81 100755 --- a/.circleci/compare_bytecode_reports.sh +++ b/.circleci/compare_bytecode_reports.sh @@ -30,6 +30,7 @@ native_platforms=( ubuntu2004-static ubuntu osx + osx_intel windows ) interfaces=( diff --git a/.circleci/config.yml b/.circleci/config.yml index 9a967febaa48..02c15e691b4f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -81,6 +81,7 @@ commands: enum: - solcjs - native + - osx_intel binary_path: type: string preset: @@ -491,12 +492,13 @@ defaults: - base_osx: &base_osx macos: - xcode: "14.2.0" - resource_class: macos.x86.medium.gen2 + xcode: "15.0.0" + resource_class: macos.m1.medium.gen1 environment: &base_osx_env TERM: xterm MAKEFLAGS: -j5 CPUs: 5 + ETH_EVMONE: /usr/local/lib/libevmone.dylib - base_python_small: &base_python_small docker: @@ -1146,6 +1148,7 @@ jobs: environment: <<: *base_osx_env CMAKE_BUILD_TYPE: Release + CMAKE_OPTIONS: -DCMAKE_OSX_ARCHITECTURES:STRING=x86_64;arm64 steps: - checkout - install_dependencies_osx @@ -1657,6 +1660,22 @@ jobs: binary_path: "build/solc/solc" preset: "<< parameters.preset >>" + b_bytecode_osx_intel: + parameters: + preset: + type: string + <<: *base_osx + parallelism: 2 # For prepare_bytecode_report + steps: + - checkout + - attach_workspace: + at: . + - prepare_bytecode_report: + label: "osx_intel" + binary_type: osx_intel + binary_path: "build/solc/solc" + preset: "<< parameters.preset >>" + b_bytecode_win: parameters: preset: @@ -1880,6 +1899,11 @@ workflows: matrix: *bytecode_compare_preset_matrix requires: - b_osx + - b_bytecode_osx_intel: + <<: *on_all_tags_and_branches + matrix: *bytecode_compare_preset_matrix + requires: + - b_osx - b_bytecode_ems: <<: *on_all_tags_and_branches matrix: *bytecode_compare_preset_matrix @@ -1892,6 +1916,7 @@ workflows: - b_bytecode_ubu - b_bytecode_win - b_bytecode_osx + - b_bytecode_osx_intel - b_bytecode_ems # Final artifacts diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh index 8b8656abd783..4ca4a31d503b 100755 --- a/.circleci/osx_install_dependencies.sh +++ b/.circleci/osx_install_dependencies.sh @@ -50,33 +50,67 @@ if [ ! -f /usr/local/lib/libz3.a ] # if this file does not exists (cache was not then brew update brew upgrade - brew install boost brew install cmake brew install wget brew install coreutils brew install diffutils brew install grep - ./scripts/install_obsolete_jsoncpp_1_7_4.sh + + # writing to /usr/local/lib need administrative privileges. + sudo ./scripts/install_obsolete_jsoncpp_1_7_4.sh + + # boost + boost_version="1.84.0" + boost_package="boost_${boost_version//./_}.tar.bz2" + boost_dir="boost_${boost_version//./_}" + wget "https://boostorg.jfrog.io/artifactory/main/release/$boost_version/source/$boost_package" + tar xf "$boost_package" + rm "$boost_package" + cd "$boost_dir" + ./bootstrap.sh --with-toolset=clang --with-libraries=thread,system,filesystem,program_options,serialization,test + # the default number of jobs that b2 is taking, is the number of detected available CPU threads. + sudo ./b2 -a address-model=64 architecture=arm+x86 install + cd .. + sudo rm -rf "$boost_dir" # z3 z3_version="4.12.1" - z3_dir="z3-${z3_version}-x64-osx-10.16" - z3_package="${z3_dir}.zip" - wget "https://github.com/Z3Prover/z3/releases/download/z3-${z3_version}/${z3_package}" - validate_checksum "$z3_package" 7601f844de6d906235140d0f76cca58be7ac716f3e2c29c35845aa24b24f73b9 - unzip "$z3_package" + z3_dir="z3-z3-$z3_version" + z3_package="z3-$z3_version.tar.gz" + wget "https://github.com/Z3Prover/z3/archive/refs/tags/$z3_package" + validate_checksum "$z3_package" a3735fabf00e1341adcc70394993c05fd3e2ae167a3e9bb46045e33084eb64a3 + tar xf "$z3_package" rm "$z3_package" - cp "${z3_dir}/bin/libz3.a" /usr/local/lib - cp "${z3_dir}/bin/z3" /usr/local/bin - cp "${z3_dir}/include/"* /usr/local/include - rm -r "$z3_dir" + cd "$z3_dir" + mkdir build + cd build + cmake -DCMAKE_OSX_ARCHITECTURES:STRING="x86_64;arm64" -DZ3_BUILD_LIBZ3_SHARED=false .. + make -j + sudo make install + cd ../.. + rm -rf "$z3_dir" # evmone evmone_version="0.11.0" - evmone_package="evmone-${evmone_version}-darwin-x86_64.tar.gz" - wget "https://github.com/ethereum/evmone/releases/download/v${evmone_version}/${evmone_package}" - validate_checksum "$evmone_package" 83ed20676681d9a31bd30cac399ab7c615ccab8adb8087cc2c7e9cd22b4d2efc - tar xzpf "$evmone_package" -C /usr/local - rm "$evmone_package" - + if [[ $(uname -m) == 'arm64' ]] + then + # evmone does not provide any builds for apple silicon yet. so lets just build it locally. + # be aware that we are only building the arm version here, we don't build a universal binary. + git clone https://github.com/ethereum/evmone.git + cd evmone + git checkout "v${evmone_version}" + git submodule update --init + cmake -S . -B build + cmake --build build + cd build + sudo make install + cd ../.. + rm -rf evmone + else + evmone_package="evmone-${evmone_version}-darwin-x86_64.tar.gz" + wget "https://github.com/ethereum/evmone/releases/download/v${evmone_version}/${evmone_package}" + validate_checksum "$evmone_package" 83ed20676681d9a31bd30cac399ab7c615ccab8adb8087cc2c7e9cd22b4d2efc + tar xzpf "$evmone_package" -C /usr/local + rm "$evmone_package" + fi fi diff --git a/.circleci/parallel_bytecode_report.sh b/.circleci/parallel_bytecode_report.sh index 9b316266a21e..7fe22c343283 100755 --- a/.circleci/parallel_bytecode_report.sh +++ b/.circleci/parallel_bytecode_report.sh @@ -33,7 +33,7 @@ binary_type="$2" binary_path="$3" # This path must be absolute preset="$4" -[[ $binary_type == native || $binary_type == solcjs ]] || { >&2 echo "Invalid binary type: ${binary_type}"; exit 1; } +[[ $binary_type == native || $binary_type == "osx_intel" || $binary_type == solcjs ]] || { >&2 echo "Invalid binary type: ${binary_type}"; exit 1; } # NOTE: Locale affects the order of the globbed files. export LC_ALL=C @@ -47,7 +47,7 @@ python3 ../scripts/isolate_tests.py ../test/ # FIXME: These cases crash because of https://github.com/ethereum/solidity/issues/13583 rm ./*_bytecode_too_large_*.sol ./*_combined_too_large_*.sol -if [[ $binary_type == native ]]; then +if [[ $binary_type == native || $binary_type == "osx_intel" ]]; then interface=$(echo -e "standard-json\ncli" | circleci tests split) echo "Selected interface: ${interface}" @@ -56,6 +56,7 @@ if [[ $binary_type == native ]]; then "$binary_path" \ --interface "$interface" \ --preset "$preset" \ + --execution-arch "$binary_type" \ --report-file "../bytecode-report-${label}-${interface}-${preset}.txt" else echo "Installing solc-js" diff --git a/cmake/jsoncpp.cmake b/cmake/jsoncpp.cmake index cf1cc656b3b0..c47c2697a3dc 100644 --- a/cmake/jsoncpp.cmake +++ b/cmake/jsoncpp.cmake @@ -40,6 +40,7 @@ if (WIN32 AND POLICY CMP0091 AND CMAKE_MSVC_RUNTIME_LIBRARY) list(APPEND JSONCPP_CMAKE_ARGS "-DCMAKE_MSVC_RUNTIME_LIBRARY=${CMAKE_MSVC_RUNTIME_LIBRARY}") endif() +string(REPLACE ";" "$" CMAKE_OSX_ARCHITECTURES_ "${CMAKE_OSX_ARCHITECTURES}") ExternalProject_Add(jsoncpp-project PREFIX "${prefix}" DOWNLOAD_DIR "${PROJECT_SOURCE_DIR}/deps/downloads" @@ -57,6 +58,7 @@ ExternalProject_Add(jsoncpp-project -DJSONCPP_WITH_PKGCONFIG_SUPPORT=OFF -DCMAKE_CXX_FLAGS=${JSONCPP_CXX_FLAGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES_} ${JSONCPP_CMAKE_ARGS} ${byproducts} ) diff --git a/scripts/bytecodecompare/prepare_report.py b/scripts/bytecodecompare/prepare_report.py index 8be9ae045cca..938f5171f1a6 100755 --- a/scripts/bytecodecompare/prepare_report.py +++ b/scripts/bytecodecompare/prepare_report.py @@ -26,6 +26,11 @@ class CompilerInterface(Enum): STANDARD_JSON = 'standard-json' +class ExecutionArchitecture(Enum): + NATIVE = 'native' + OSX_INTEL = 'osx_intel' + + class SettingsPreset(Enum): LEGACY_OPTIMIZE = 'legacy-optimize' LEGACY_NO_OPTIMIZE = 'legacy-no-optimize' @@ -214,6 +219,7 @@ def parse_cli_output(source_file_name: Path, cli_output: str, exit_code: int) -> def prepare_compiler_input( compiler_path: Path, + execution_arch: ExecutionArchitecture, source_file_name: Path, force_no_optimize_yul: bool, interface: CompilerInterface, @@ -224,6 +230,12 @@ def prepare_compiler_input( settings = CompilerSettings.from_preset(preset) + command_line = [] + if execution_arch == ExecutionArchitecture.OSX_INTEL: + command_line = ["/usr/bin/arch", "-64", "-x86_64"] + else: + assert execution_arch == ExecutionArchitecture.NATIVE + if interface == CompilerInterface.STANDARD_JSON: json_input: dict = { 'language': 'Solidity', @@ -241,7 +253,7 @@ def prepare_compiler_input( if smt_use == SMTUse.DISABLE: json_input['settings']['modelChecker'] = {'engine': 'none'} - command_line = [str(compiler_path), '--standard-json'] + command_line += [str(compiler_path), '--standard-json'] compiler_input = json.dumps(json_input) else: assert interface == CompilerInterface.CLI @@ -258,7 +270,7 @@ def prepare_compiler_input( if smt_use == SMTUse.DISABLE: compiler_options += ['--model-checker-engine', 'none'] - command_line = [str(compiler_path)] + compiler_options + command_line += [str(compiler_path)] + compiler_options compiler_input = load_source(source_file_name, smt_use) return (command_line, compiler_input) @@ -289,6 +301,7 @@ def detect_metadata_cli_option_support(compiler_path: Path): def run_compiler( compiler_path: Path, + execution_arch: ExecutionArchitecture, source_file_name: Path, force_no_optimize_yul: bool, interface: CompilerInterface, @@ -298,10 +311,10 @@ def run_compiler( tmp_dir: Path, exit_on_error: bool, ) -> FileReport: - if interface == CompilerInterface.STANDARD_JSON: (command_line, compiler_input) = prepare_compiler_input( compiler_path, + execution_arch, Path(source_file_name.name), force_no_optimize_yul, interface, @@ -325,6 +338,7 @@ def run_compiler( (command_line, compiler_input) = prepare_compiler_input( compiler_path.absolute(), + execution_arch, Path(source_file_name.name), force_no_optimize_yul, interface, @@ -354,6 +368,7 @@ def run_compiler( def generate_report( source_file_names: List[str], compiler_path: Path, + execution_arch: ExecutionArchitecture, interface: CompilerInterface, presets: List[SettingsPreset], smt_use: SMTUse, @@ -373,6 +388,7 @@ def generate_report( try: report = run_compiler( compiler_path, + execution_arch, Path(source_file_name), force_no_optimize_yul, interface, @@ -422,6 +438,13 @@ def commandline_parser() -> ArgumentParser: choices=[c.value for c in CompilerInterface], help="Compiler interface to use.", ) + parser.add_argument( + '--execution-arch', + dest='execution_arch', + default=ExecutionArchitecture.NATIVE.value, + choices=[c.value for c in ExecutionArchitecture], + help="Select the architecture of the universal binary that should be executed. (Only relevant for macOS)", + ) parser.add_argument( '--preset', dest='presets', @@ -470,6 +493,7 @@ def commandline_parser() -> ArgumentParser: generate_report( glob("*.sol"), Path(options.compiler_path), + ExecutionArchitecture(options.execution_arch), CompilerInterface(options.interface), [SettingsPreset(p) for preset_group in presets for p in preset_group], SMTUse(options.smt_use), diff --git a/scripts/install_obsolete_jsoncpp_1_7_4.sh b/scripts/install_obsolete_jsoncpp_1_7_4.sh index 825d1a58a225..f9de0245d70b 100755 --- a/scripts/install_obsolete_jsoncpp_1_7_4.sh +++ b/scripts/install_obsolete_jsoncpp_1_7_4.sh @@ -17,7 +17,7 @@ TEMPDIR=$(mktemp -d) cd "jsoncpp-${jsoncpp_version}" mkdir -p build cd build - cmake -DARCHIVE_INSTALL_DIR=. -G "Unix Makefiles" .. + cmake -DCMAKE_OSX_ARCHITECTURES:STRING="x86_64;arm64" -DARCHIVE_INSTALL_DIR=. -G "Unix Makefiles" .. make make install ) diff --git a/test/scripts/test_bytecodecompare_prepare_report.py b/test/scripts/test_bytecodecompare_prepare_report.py index d73b1f7141bf..28a89ec315cb 100644 --- a/test/scripts/test_bytecodecompare_prepare_report.py +++ b/test/scripts/test_bytecodecompare_prepare_report.py @@ -9,7 +9,8 @@ # NOTE: This test file file only works with scripts/ added to PYTHONPATH so pylint can't find the imports # pragma pylint: disable=import-error -from bytecodecompare.prepare_report import CompilerInterface, FileReport, ContractReport, SettingsPreset, SMTUse, Statistics +from bytecodecompare.prepare_report import ExecutionArchitecture, CompilerInterface, FileReport, ContractReport +from bytecodecompare.prepare_report import SettingsPreset, SMTUse, Statistics from bytecodecompare.prepare_report import load_source, parse_cli_output, parse_standard_json_output, prepare_compiler_input # pragma pylint: enable=import-error @@ -223,6 +224,7 @@ def test_prepare_compiler_input_should_work_with_standard_json_interface(self): (command_line, compiler_input) = prepare_compiler_input( Path('solc'), + ExecutionArchitecture.NATIVE, SMT_SMOKE_TEST_SOL_PATH, preset=SettingsPreset.LEGACY_OPTIMIZE, force_no_optimize_yul=False, @@ -237,6 +239,7 @@ def test_prepare_compiler_input_should_work_with_standard_json_interface(self): def test_prepare_compiler_input_should_work_with_cli_interface(self): (command_line, compiler_input) = prepare_compiler_input( Path('solc'), + ExecutionArchitecture.NATIVE, SMT_SMOKE_TEST_SOL_PATH, preset=SettingsPreset.LEGACY_OPTIMIZE, force_no_optimize_yul=False, @@ -273,6 +276,7 @@ def test_prepare_compiler_input_for_json_preserves_newlines(self): (command_line, compiler_input) = prepare_compiler_input( Path('solc'), + ExecutionArchitecture.NATIVE, SMT_CONTRACT_WITH_MIXED_NEWLINES_SOL_PATH, preset=SettingsPreset.VIA_IR_OPTIMIZE, force_no_optimize_yul=False, @@ -287,6 +291,7 @@ def test_prepare_compiler_input_for_json_preserves_newlines(self): def test_prepare_compiler_input_for_cli_preserves_newlines(self): (_command_line, compiler_input) = prepare_compiler_input( Path('solc'), + ExecutionArchitecture.NATIVE, SMT_CONTRACT_WITH_MIXED_NEWLINES_SOL_PATH, preset=SettingsPreset.LEGACY_OPTIMIZE, force_no_optimize_yul=True, @@ -300,6 +305,7 @@ def test_prepare_compiler_input_for_cli_preserves_newlines(self): def test_prepare_compiler_input_for_cli_should_handle_force_no_optimize_yul_flag(self): (command_line, compiler_input) = prepare_compiler_input( Path('solc'), + ExecutionArchitecture.NATIVE, SMT_SMOKE_TEST_SOL_PATH, preset=SettingsPreset.LEGACY_NO_OPTIMIZE, force_no_optimize_yul=True, @@ -317,6 +323,7 @@ def test_prepare_compiler_input_for_cli_should_handle_force_no_optimize_yul_flag def test_prepare_compiler_input_for_cli_should_not_use_metadata_option_if_not_supported(self): (command_line, compiler_input) = prepare_compiler_input( Path('solc'), + ExecutionArchitecture.NATIVE, SMT_SMOKE_TEST_SOL_PATH, preset=SettingsPreset.VIA_IR_OPTIMIZE, force_no_optimize_yul=False, From 631fd9daaad4e142e15c0fd87dddf3b44219e07a Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 17 Jan 2024 15:20:50 +0100 Subject: [PATCH 070/189] Issue a warning when comparing internal function pointers --- libsolidity/analysis/TypeChecker.cpp | 21 +++++++++++++++++++ .../operators/delete_function_type_1.sol | 8 +++---- .../operators/delete_function_type_2.sol | 8 +++---- .../operators/delete_function_type_3.sol | 16 +++++++------- ...unction_type_to_function_type_internal.sol | 5 +++-- .../comparison_of_function_types_eq.sol | 11 ---------- ...mparison_of_function_types_external_eq.sol | 9 ++++++++ ...arison_of_function_types_internal_eq_1.sol | 11 ++++++++++ ...arison_of_function_types_internal_eq_2.sol | 11 ++++++++++ ...nternal_and_external_function_pointers.sol | 1 + 10 files changed, 72 insertions(+), 29 deletions(-) delete mode 100644 test/libsolidity/syntaxTests/functionTypes/comparison_of_function_types_eq.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/comparison_of_function_types_external_eq.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/comparison_of_function_types_internal_eq_1.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/comparison_of_function_types_internal_eq_2.sol diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 91224610f55a..5bef2803acb6 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1890,6 +1890,27 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) _operation.annotation().isLValue = false; _operation.annotation().isConstant = false; + if (_operation.getOperator() == Token::Equal || _operation.getOperator() == Token::NotEqual) + { + auto const* leftFunction = dynamic_cast(leftType); + auto const* rightFunction = dynamic_cast(rightType); + if ( + leftFunction && + rightFunction && + leftFunction->kind() == FunctionType::Kind::Internal && + rightFunction->kind() == FunctionType::Kind::Internal + ) + { + m_errorReporter.warning( + 3075_error, + _operation.location(), + "Comparison of internal function pointers can yield unexpected results " + "in the legacy pipeline with the optimizer enabled, and will be disallowed entirely " + "in the next breaking release." + ); + } + } + if (_operation.getOperator() == Token::Exp || _operation.getOperator() == Token::SHL) { std::string operation = _operation.getOperator() == Token::Exp ? "exponentiation" : "shift"; diff --git a/test/libsolidity/smtCheckerTests/operators/delete_function_type_1.sol b/test/libsolidity/smtCheckerTests/operators/delete_function_type_1.sol index b6bc1f6a0472..7df2b127ccc8 100644 --- a/test/libsolidity/smtCheckerTests/operators/delete_function_type_1.sol +++ b/test/libsolidity/smtCheckerTests/operators/delete_function_type_1.sol @@ -1,10 +1,10 @@ struct S { - function() e; + function() external e; } contract C { S s; - function() f; + function() external f; constructor() { delete s.e; @@ -12,5 +12,5 @@ contract C { } } // ---- -// Warning 7229: (128-136): Assertion checker does not yet implement the type function () for comparisons -// Warning 6328: (121-137): CHC: Assertion violation happens here. +// Warning 7229: (146-154): Assertion checker does not yet implement the type function () external for comparisons +// Warning 6328: (139-155): CHC: Assertion violation happens here.\nCounterexample:\ns = {e: 0}, f = 0\n\nTransaction trace:\nC.constructor() diff --git a/test/libsolidity/smtCheckerTests/operators/delete_function_type_2.sol b/test/libsolidity/smtCheckerTests/operators/delete_function_type_2.sol index ee7497aca03d..a2168a7c7b0d 100644 --- a/test/libsolidity/smtCheckerTests/operators/delete_function_type_2.sol +++ b/test/libsolidity/smtCheckerTests/operators/delete_function_type_2.sol @@ -1,6 +1,6 @@ contract C { - mapping (uint => function()) m; - function() f; + mapping (uint => function() external) m; + function() external f; constructor() { m[2] = f; @@ -9,5 +9,5 @@ contract C { } } // ---- -// Warning 7229: (142-151): Assertion checker does not yet implement the type function () for comparisons -// Warning 6328: (135-152): CHC: Assertion violation happens here. +// Warning 7229: (160-169): Assertion checker does not yet implement the type function () external for comparisons +// Warning 6328: (153-170): CHC: Assertion violation happens here.\nCounterexample:\nf = 0\n\nTransaction trace:\nC.constructor() diff --git a/test/libsolidity/smtCheckerTests/operators/delete_function_type_3.sol b/test/libsolidity/smtCheckerTests/operators/delete_function_type_3.sol index 33a31d6c4840..06fd29ccaf75 100644 --- a/test/libsolidity/smtCheckerTests/operators/delete_function_type_3.sol +++ b/test/libsolidity/smtCheckerTests/operators/delete_function_type_3.sol @@ -1,6 +1,6 @@ contract C { - function()[] arr; - function() f; + function() external[] arr; + function() external f; constructor() { arr.push(); @@ -11,10 +11,10 @@ contract C { } } // ---- -// Warning 8364: (82-90): Assertion checker does not yet implement type function (function ()[] storage pointer) returns (function ()) -// Warning 8364: (102-110): Assertion checker does not yet implement type function (function ()[] storage pointer) returns (function ()) -// Warning 7229: (172-183): Assertion checker does not yet implement the type function () for comparisons -// Warning 6368: (149-155): CHC: Out of bounds access happens here. -// Warning 6368: (172-178): CHC: Out of bounds access happens here. -// Warning 6328: (165-184): CHC: Assertion violation happens here. +// Warning 8364: (100-108): Assertion checker does not yet implement type function (function () external[] storage pointer) returns (function () external) +// Warning 8364: (120-128): Assertion checker does not yet implement type function (function () external[] storage pointer) returns (function () external) +// Warning 7229: (190-201): Assertion checker does not yet implement the type function () external for comparisons +// Warning 6368: (167-173): CHC: Out of bounds access happens here.\nCounterexample:\nf = 0\n\nTransaction trace:\nC.constructor() +// Warning 6368: (190-196): CHC: Out of bounds access happens here.\nCounterexample:\nf = 0\n\nTransaction trace:\nC.constructor() +// Warning 6328: (183-202): CHC: Assertion violation happens here.\nCounterexample:\narr = [14, 0], f = 0\n\nTransaction trace:\nC.constructor() // 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/typecast/function_type_to_function_type_internal.sol b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol index 368e9c922f6c..2aba4b283eb6 100644 --- a/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol +++ b/test/libsolidity/smtCheckerTests/typecast/function_type_to_function_type_internal.sol @@ -13,10 +13,11 @@ contract C { // SMTEngine: all // ---- // Warning 2519: (93-124): This declaration shadows an existing declaration. +// Warning 3075: (203-209): Comparison of internal function pointers can yield unexpected results in the legacy pipeline with the optimizer enabled, and will be disallowed entirely in the next breaking release. // Warning 6031: (179-183): Internal error: Expression undefined for SMT solver. // Warning 6031: (187-191): Internal error: Expression undefined for SMT solver. // Warning 7229: (203-209): Assertion checker does not yet implement the type function (uint256) returns (uint256) for comparisons // Warning 5729: (179-183): BMC does not yet implement this type of function call. // Warning 5729: (187-191): BMC does not yet implement this type of function call. -// Warning 6328: (172-192): CHC: Assertion violation happens here. -// Warning 6328: (196-210): CHC: Assertion violation happens here. +// Warning 6328: (172-192): CHC: Assertion violation happens here.\nCounterexample:\na = 0, b = 0\n\nTransaction trace:\nC.constructor()\nState: a = 0, b = 0\nC.g()\n C.f(0, 0) -- internal call +// Warning 6328: (196-210): CHC: Assertion violation happens here.\nCounterexample:\na = 0, b = 0\n\nTransaction trace:\nC.constructor()\nState: a = 0, b = 0\nC.g()\n C.f(0, 0) -- internal call diff --git a/test/libsolidity/syntaxTests/functionTypes/comparison_of_function_types_eq.sol b/test/libsolidity/syntaxTests/functionTypes/comparison_of_function_types_eq.sol deleted file mode 100644 index 026f0343becb..000000000000 --- a/test/libsolidity/syntaxTests/functionTypes/comparison_of_function_types_eq.sol +++ /dev/null @@ -1,11 +0,0 @@ -contract C { - function f() public returns (bool ret) { - return f == f; - } - function g() public returns (bool ret) { - return f != f; - } -} -// ---- -// Warning 2018: (17-86): Function state mutability can be restricted to pure -// Warning 2018: (91-160): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/functionTypes/comparison_of_function_types_external_eq.sol b/test/libsolidity/syntaxTests/functionTypes/comparison_of_function_types_external_eq.sol new file mode 100644 index 000000000000..2181fa99cfb7 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/comparison_of_function_types_external_eq.sol @@ -0,0 +1,9 @@ +contract C { + function f() public view returns (bool ret) { + return this.f == this.f; + } + function g() public view returns (bool ret) { + return this.f != this.f; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/functionTypes/comparison_of_function_types_internal_eq_1.sol b/test/libsolidity/syntaxTests/functionTypes/comparison_of_function_types_internal_eq_1.sol new file mode 100644 index 000000000000..44da7f0d18a8 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/comparison_of_function_types_internal_eq_1.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure returns (bool ret) { + return f == f; + } + function g() public pure returns (bool ret) { + return f != f; + } +} +// ---- +// Warning 3075: (78-84): Comparison of internal function pointers can yield unexpected results in the legacy pipeline with the optimizer enabled, and will be disallowed entirely in the next breaking release. +// Warning 3075: (157-163): Comparison of internal function pointers can yield unexpected results in the legacy pipeline with the optimizer enabled, and will be disallowed entirely in the next breaking release. diff --git a/test/libsolidity/syntaxTests/functionTypes/comparison_of_function_types_internal_eq_2.sol b/test/libsolidity/syntaxTests/functionTypes/comparison_of_function_types_internal_eq_2.sol new file mode 100644 index 000000000000..4be35a2396d5 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/comparison_of_function_types_internal_eq_2.sol @@ -0,0 +1,11 @@ +contract C { + function f() internal {} + function g() internal {} + + function test() public pure returns (bool) { + function () internal ptr = C.f; + return ptr == C.g; + } +} +// ---- +// Warning 3075: (176-186): Comparison of internal function pointers can yield unexpected results in the legacy pipeline with the optimizer enabled, and will be disallowed entirely in the next breaking release. diff --git a/test/libsolidity/syntaxTests/functionTypes/comparison_operators_between_internal_and_external_function_pointers.sol b/test/libsolidity/syntaxTests/functionTypes/comparison_operators_between_internal_and_external_function_pointers.sol index 7c0bf096063f..358b9c621f29 100644 --- a/test/libsolidity/syntaxTests/functionTypes/comparison_operators_between_internal_and_external_function_pointers.sol +++ b/test/libsolidity/syntaxTests/functionTypes/comparison_operators_between_internal_and_external_function_pointers.sol @@ -19,5 +19,6 @@ contract C { } } // ---- +// Warning 3075: (509-566): Comparison of internal function pointers can yield unexpected results in the legacy pipeline with the optimizer enabled, and will be disallowed entirely in the next breaking release. // TypeError 2271: (606-672): Built-in binary operator != cannot be applied to types function () and function () external. // TypeError 2271: (688-741): Built-in binary operator != cannot be applied to types function () and function () external. From ef663c25a6c9d65fd101434fcfe3e3dcad3b9dcd Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 18 Jan 2024 12:57:15 +0100 Subject: [PATCH 071/189] Changelog and docs --- Changelog.md | 1 + docs/types/value-types.rst | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/Changelog.md b/Changelog.md index c876aa7bc8ff..7a5fe390c938 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,7 @@ Language Features: Compiler Features: * EVM: Support for the EVM Version "Cancun". * SMTChecker: Support `bytes.concat` except when string literals are passed as arguments. +* TypeChecker: Comparison of internal function pointers now yields a warning, as it can produce unexpected results with the legacy pipeline enabled. Bugfixes: diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index 9fcb49b2131a..d44d2fd70ad8 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -813,6 +813,12 @@ functions. Marking the parameters as ``calldata`` only affects the implementation of the external function and is meaningless in a function pointer on the caller's side. +.. warning:: + Comparison of internal function pointers can have unexpected results in the legacy pipeline with the optimizer enabled, + as it can collapse identical functions into one, which will then lead to said function pointers comparing as equal instead of not. + Such comparisons are not advised, and will lead to the compiler issuing a warning, until the next breaking release (0.9.0), + when the warning will be upgraded to an error, thereby making such comparisons disallowed. + Libraries are excluded because they require a ``delegatecall`` and use :ref:`a different ABI convention for their selectors `. Functions declared in interfaces do not have definitions so pointing at them does not make sense either. From ac3703a1abbb7310c9addddf0e69c93943752fab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 25 Jan 2024 04:39:41 +0100 Subject: [PATCH 072/189] using-the-compiler.rst: Adjust SolidityAST docs to match the pre-existing JSON structure --- docs/using-the-compiler.rst | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index bbeb116c9eab..7643e31a74ec 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -235,14 +235,6 @@ Input Description // If files are used, their directories should be added to the command-line via // `--allow-paths `. ] - // If language is set to "SolidityAST", an AST needs to be supplied under the "ast" key. - // Note that importing ASTs is experimental and in particular that: - // - importing invalid ASTs can produce undefined results and - // - no proper error reporting is available on invalid ASTs. - // Furthermore, note that the AST import only consumes the fields of the AST as - // produced by the compiler in "stopAfter": "parsing" mode and then re-performs - // analysis, so any analysis-based annotations of the AST are ignored upon import. - "ast": { ... } // formatted as the json ast requested with the ``ast`` output selection. }, "destructible": { @@ -250,6 +242,20 @@ Input Description "keccak256": "0x234...", // Required (unless "urls" is used): literal contents of the source file "content": "contract destructible is owned { function shutdown() { if (msg.sender == owner) selfdestruct(owner); } }" + }, + "myFile.sol_json.ast": + { + // If language is set to "SolidityAST", an AST needs to be supplied under the "ast" key + // and there can be only one source file present. + // The format is the same as used by the `ast` output. + // Note that importing ASTs is experimental and in particular that: + // - importing invalid ASTs can produce undefined results and + // - no proper error reporting is available on invalid ASTs. + // Furthermore, note that the AST import only consumes the fields of the AST as + // produced by the compiler in "stopAfter": "parsing" mode and then re-performs + // analysis, so any analysis-based annotations of the AST are ignored upon import. + "ast": { ... } + } } }, // Optional From 4ed238035721f62ecb95355c15aa855498a1664d Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Wed, 31 May 2023 23:22:23 +0200 Subject: [PATCH 073/189] Add support to import EVM assembly via Standard JSON. --- Changelog.md | 1 + docs/using-the-compiler.rst | 15 +- libevmasm/EVMAssemblyStack.cpp | 11 +- libevmasm/EVMAssemblyStack.h | 5 + libsolidity/interface/StandardCompiler.cpp | 134 +++++++++++++++++- libsolidity/interface/StandardCompiler.h | 2 + .../standard_import_evmasm/input.json | 28 ++++ .../standard_import_evmasm/output.json | 51 +++++++ .../input.json | 46 ++++++ .../output.json | 40 ++++++ .../input.json | 15 ++ .../output.json | 12 ++ .../input.json | 28 ++++ .../output.json | 12 ++ .../input.json | 29 ++++ .../output.json | 12 ++ .../input.json | 12 ++ .../output.json | 12 ++ .../input.json | 28 ++++ .../output.json | 12 ++ .../input.json | 20 +++ .../output.json | 12 ++ .../input.json | 38 +++++ .../output.json | 43 ++++++ .../input.json | 32 +++++ .../output.json | 12 ++ .../input.json | 29 ++++ .../output.json | 1 + test/libsolidity/StandardCompiler.cpp | 2 +- 29 files changed, 688 insertions(+), 6 deletions(-) create mode 100644 test/cmdlineTests/standard_import_evmasm/input.json create mode 100644 test/cmdlineTests/standard_import_evmasm/output.json create mode 100644 test/cmdlineTests/standard_import_evmasm_immutable_references/input.json create mode 100644 test/cmdlineTests/standard_import_evmasm_immutable_references/output.json create mode 100644 test/cmdlineTests/standard_import_evmasm_invalid_input_array/input.json create mode 100644 test/cmdlineTests/standard_import_evmasm_invalid_input_array/output.json create mode 100644 test/cmdlineTests/standard_import_evmasm_invalid_key_inside_source/input.json create mode 100644 test/cmdlineTests/standard_import_evmasm_invalid_key_inside_source/output.json create mode 100644 test/cmdlineTests/standard_import_evmasm_invalid_key_other_source/input.json create mode 100644 test/cmdlineTests/standard_import_evmasm_invalid_key_other_source/output.json create mode 100644 test/cmdlineTests/standard_import_evmasm_invalid_no_source/input.json create mode 100644 test/cmdlineTests/standard_import_evmasm_invalid_no_source/output.json create mode 100644 test/cmdlineTests/standard_import_evmasm_invalid_opcode/input.json create mode 100644 test/cmdlineTests/standard_import_evmasm_invalid_opcode/output.json create mode 100644 test/cmdlineTests/standard_import_evmasm_invalid_two_sources/input.json create mode 100644 test/cmdlineTests/standard_import_evmasm_invalid_two_sources/output.json create mode 100644 test/cmdlineTests/standard_import_evmasm_link_references/input.json create mode 100644 test/cmdlineTests/standard_import_evmasm_link_references/output.json create mode 100644 test/cmdlineTests/standard_import_evmasm_multiple_keys_inside_source/input.json create mode 100644 test/cmdlineTests/standard_import_evmasm_multiple_keys_inside_source/output.json create mode 100644 test/cmdlineTests/standard_import_evmasm_no_output_selection/input.json create mode 100644 test/cmdlineTests/standard_import_evmasm_no_output_selection/output.json diff --git a/Changelog.md b/Changelog.md index 7a5fe390c938..db488b183a1f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -13,6 +13,7 @@ Compiler Features: * EVM: Support for the EVM Version "Cancun". * SMTChecker: Support `bytes.concat` except when string literals are passed as arguments. * TypeChecker: Comparison of internal function pointers now yields a warning, as it can produce unexpected results with the legacy pipeline enabled. +* Standard JSON Interface: Add experimental support to import EVM assembly in the format used by ``--asm-json``. Bugfixes: diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 7643e31a74ec..97abbdba9df6 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -208,7 +208,7 @@ Input Description .. code-block:: javascript { - // Required: Source code language. Currently supported are "Solidity", "Yul" and "SolidityAST" (experimental). + // Required: Source code language. Currently supported are "Solidity", "Yul", "SolidityAST" (experimental), "EVMAssembly" (experimental). "language": "Solidity", // Required "sources": @@ -255,6 +255,19 @@ Input Description // produced by the compiler in "stopAfter": "parsing" mode and then re-performs // analysis, so any analysis-based annotations of the AST are ignored upon import. "ast": { ... } + }, + "myFile_evm.json": + { + // If language is set to "EVMAssembly", an EVM Assembly JSON object needs to be supplied + // under the "assemblyJson" key and there can be only one source file present. + // The format is the same as used by the `evm.legacyAssembly` output or `--asm-json` + // output on the command line. + // Note that importing EVM assembly is experimental. + "assemblyJson": + { + ".code": [ ... ], + ".data": { ... }, // optional + "sourceList": [ ... ] // optional (if no `source` node was defined in any `.code` object) } } }, diff --git a/libevmasm/EVMAssemblyStack.cpp b/libevmasm/EVMAssemblyStack.cpp index 68f60808c9d5..a06044256faf 100644 --- a/libevmasm/EVMAssemblyStack.cpp +++ b/libevmasm/EVMAssemblyStack.cpp @@ -36,11 +36,16 @@ namespace solidity::evmasm void EVMAssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string const& _source) { - solAssert(!m_evmAssembly); - m_name = _sourceName; Json::Value assemblyJson; solRequire(jsonParseStrict(_source, assemblyJson), AssemblyImportException, "Could not parse JSON file."); - std::tie(m_evmAssembly, m_sourceList) = evmasm::Assembly::fromJSON(assemblyJson); + analyze(_sourceName, assemblyJson); +} + +void EVMAssemblyStack::analyze(std::string const& _sourceName, Json::Value const& _assemblyJson) +{ + solAssert(!m_evmAssembly); + m_name = _sourceName; + std::tie(m_evmAssembly, m_sourceList) = evmasm::Assembly::fromJSON(_assemblyJson); solRequire(m_evmAssembly != nullptr, AssemblyImportException, "Could not create evm assembly object."); } diff --git a/libevmasm/EVMAssemblyStack.h b/libevmasm/EVMAssemblyStack.h index cf02cec52f2c..7fe77e7d208b 100644 --- a/libevmasm/EVMAssemblyStack.h +++ b/libevmasm/EVMAssemblyStack.h @@ -40,6 +40,11 @@ class EVMAssemblyStack: public AbstractAssemblyStack /// @throws AssemblyImportException, if JSON could not be validated. void parseAndAnalyze(std::string const& _sourceName, std::string const& _source); + /// Runs analysis steps. + /// Multiple calls overwrite the previous state. + /// @throws AssemblyImportException, if JSON could not be validated. + void analyze(std::string const& _sourceName, Json::Value const& _assemblyJson); + void assemble(); std::string const& name() const { return m_name; } diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index b74f764c5592..85a5a5d232e5 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -30,6 +30,7 @@ #include #include +#include #include @@ -730,7 +731,29 @@ std::variant StandardCompiler: for (auto const& sourceName: sources.getMemberNames()) ret.sources[sourceName] = util::jsonCompactPrint(sources[sourceName]); } + else if (ret.language == "EVMAssembly") + { + for (std::string const& sourceName: sources.getMemberNames()) + { + solAssert(sources.isMember(sourceName)); + if ( + !sources[sourceName].isMember("assemblyJson") || + !sources[sourceName]["assemblyJson"].isObject() || + sources[sourceName].size() != 1 + ) + return formatFatalError( + Error::Type::JSONError, + "Invalid input source specified. Expected exactly one object, named 'assemblyJson', inside $.sources." + sourceName + ); + ret.jsonSources[sourceName] = sources[sourceName]["assemblyJson"]; + } + if (ret.jsonSources.size() != 1) + return formatFatalError( + Error::Type::JSONError, + "EVMAssembly import only supports exactly one input file." + ); + } Json::Value const& auxInputs = _input["auxiliaryInput"]; if (auto result = checkAuxiliaryInputKeys(auxInputs)) @@ -1164,8 +1187,113 @@ std::map StandardCompiler::parseAstFromInput(StringMap return sourceJsons; } +Json::Value StandardCompiler::importEVMAssembly(StandardCompiler::InputsAndSettings _inputsAndSettings) +{ + solAssert(_inputsAndSettings.language == "EVMAssembly"); + solAssert(_inputsAndSettings.sources.empty()); + solAssert(_inputsAndSettings.jsonSources.size() == 1); + + if (!isBinaryRequested(_inputsAndSettings.outputSelection)) + return Json::objectValue; + + evmasm::EVMAssemblyStack stack(_inputsAndSettings.evmVersion); + std::string const& sourceName = _inputsAndSettings.jsonSources.begin()->first; // result of structured binding can only be used within lambda from C++20 on. + Json::Value const& sourceJson = _inputsAndSettings.jsonSources.begin()->second; + try + { + stack.analyze(sourceName, sourceJson); + stack.assemble(); + } + catch (evmasm::AssemblyImportException const& e) + { + return formatFatalError(Error::Type::Exception, "Assembly import error: " + std::string(e.what())); + } + catch (evmasm::InvalidOpcode const& e) + { + return formatFatalError(Error::Type::Exception, "Assembly import error: " + std::string(e.what())); + } + catch (...) + { + return formatError( + Error::Type::Exception, + "general", + "Unknown exception during assembly import: " + boost::current_exception_diagnostic_information() + ); + } + if (!stack.compilationSuccessful()) + return Json::objectValue; + + // EVM + bool const wildcardMatchesExperimental = false; + Json::Value evmData = Json::objectValue; + if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "evm.assembly", wildcardMatchesExperimental)) + evmData["assembly"] = stack.assemblyString(sourceName, {}); + if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, "", "evm.legacyAssembly", wildcardMatchesExperimental)) + evmData["legacyAssembly"] = stack.assemblyJSON(sourceName); + + if (isArtifactRequested( + _inputsAndSettings.outputSelection, + sourceName, + "", + evmObjectComponents("bytecode"), + wildcardMatchesExperimental + )) + evmData["bytecode"] = collectEVMObject( + _inputsAndSettings.evmVersion, + stack.object(sourceName), + stack.sourceMapping(sourceName), + {}, + false, // _runtimeObject + [&](std::string const& _element) { + return isArtifactRequested( + _inputsAndSettings.outputSelection, + sourceName, + "", + "evm.bytecode." + _element, + wildcardMatchesExperimental + ); + } + ); + + if (isArtifactRequested( + _inputsAndSettings.outputSelection, + sourceName, + "", + evmObjectComponents("deployedBytecode"), + wildcardMatchesExperimental + )) + evmData["deployedBytecode"] = collectEVMObject( + _inputsAndSettings.evmVersion, + stack.runtimeObject(sourceName), + stack.runtimeSourceMapping(sourceName), + {}, + true, // _runtimeObject + [&](std::string const& _element) { + return isArtifactRequested( + _inputsAndSettings.outputSelection, + sourceName, + "", + "evm.deployedBytecode." + _element, + wildcardMatchesExperimental + ); + } + ); + + Json::Value contractData = Json::objectValue; + if (!evmData.empty()) + contractData["evm"] = evmData; + + Json::Value contractsOutput = Json::objectValue; + contractsOutput[sourceName][""] = contractData; + Json::Value output = Json::objectValue; + output["contracts"] = contractsOutput; + return util::removeNullMembers(output); +} + Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inputsAndSettings) { + solAssert(_inputsAndSettings.jsonSources.empty()); + CompilerStack compilerStack(m_readFile); StringMap sourceList = std::move(_inputsAndSettings.sources); @@ -1470,6 +1598,8 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) { + solAssert(_inputsAndSettings.jsonSources.empty()); + Json::Value output = Json::objectValue; output["errors"] = std::move(_inputsAndSettings.errors); @@ -1627,8 +1757,10 @@ Json::Value StandardCompiler::compile(Json::Value const& _input) noexcept return compileYul(std::move(settings)); else if (settings.language == "SolidityAST") return compileSolidity(std::move(settings)); + else if (settings.language == "EVMAssembly") + return importEVMAssembly(std::move(settings)); else - return formatFatalError(Error::Type::JSONError, "Only \"Solidity\", \"Yul\" or \"SolidityAST\" is supported as a language."); + return formatFatalError(Error::Type::JSONError, "Only \"Solidity\", \"Yul\", \"SolidityAST\" or \"EVMAssembly\" is supported as a language."); } catch (Json::LogicError const& _exception) { diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h index 523c09d94e49..93cc73f3a7e2 100644 --- a/libsolidity/interface/StandardCompiler.h +++ b/libsolidity/interface/StandardCompiler.h @@ -74,6 +74,7 @@ class StandardCompiler Json::Value errors; CompilerStack::State stopAfter = CompilerStack::State::CompilationSuccessful; std::map sources; + std::map jsonSources; std::map smtLib2Responses; langutil::EVMVersion evmVersion; std::optional eofVersion; @@ -95,6 +96,7 @@ class StandardCompiler std::variant parseInput(Json::Value const& _input); std::map parseAstFromInput(StringMap const& _sources); + Json::Value importEVMAssembly(InputsAndSettings _inputsAndSettings); Json::Value compileSolidity(InputsAndSettings _inputsAndSettings); Json::Value compileYul(InputsAndSettings _inputsAndSettings); diff --git a/test/cmdlineTests/standard_import_evmasm/input.json b/test/cmdlineTests/standard_import_evmasm/input.json new file mode 100644 index 000000000000..112f480459af --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm/input.json @@ -0,0 +1,28 @@ +{ + "language": "EVMAssembly", + "sources": { + "A": { + "assemblyJson": { + ".code": [ + { + "begin": 36, + "end": 51, + "name": "PUSH", + "source": 0, + "value": "0" + } + ], + "sourceList": [ + "" + ] + } + } + }, + "settings": { + "outputSelection": { + "*": { + "": ["*"] + } + } + } +} diff --git a/test/cmdlineTests/standard_import_evmasm/output.json b/test/cmdlineTests/standard_import_evmasm/output.json new file mode 100644 index 000000000000..5b7cdf3bb9d4 --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm/output.json @@ -0,0 +1,51 @@ +{ + "contracts": + { + "A": + { + "": + { + "evm": + { + "assembly": " /* \"\":36:51 */ + 0x00 +", + "bytecode": + { + "functionDebugData": {}, + "linkReferences": {}, + "object": "", + "opcodes":"", + "sourceMap":"" + }, + "deployedBytecode": + { + "functionDebugData": {}, + "immutableReferences": {}, + "linkReferences": {}, + "object": "", + "opcodes": "", + "sourceMap": "" + }, + "legacyAssembly": + { + ".code": + [ + { + "begin": 36, + "end": 51, + "name": "PUSH", + "source": 0, + "value": "0" + } + ], + "sourceList": + [ + "" + ] + } + } + } + } + } +} diff --git a/test/cmdlineTests/standard_import_evmasm_immutable_references/input.json b/test/cmdlineTests/standard_import_evmasm_immutable_references/input.json new file mode 100644 index 000000000000..0619f8d03d19 --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_immutable_references/input.json @@ -0,0 +1,46 @@ +{ + "language": "EVMAssembly", + "sources": { + "A": { + "assemblyJson": { + ".code": [ + { + "begin": 59, + "end": 159, + "name": "ASSIGNIMMUTABLE", + "source": 0, + "value": "6" + } + ], + ".data": { + "0": { + ".auxdata": "a2646970667358221220762f49087b0990aeaac25050c4a08d9057732db3cac345579ad648d92458e09e64736f6c63430008170033", + ".code": [ + { + "begin": 153, + "end": 154, + "name": "PUSHIMMUTABLE", + "source": 0, + "value": "6" + } + ] + } + }, + "sourceList": [ + "test.sol", + "#utility.yul" + ] + } + } + }, + "settings": { + "outputSelection": { + "*": { + "": [ + "evm.bytecode", + "evm.deployedBytecode" + ] + } + } + } +} diff --git a/test/cmdlineTests/standard_import_evmasm_immutable_references/output.json b/test/cmdlineTests/standard_import_evmasm_immutable_references/output.json new file mode 100644 index 000000000000..05a6ff7d5eca --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_immutable_references/output.json @@ -0,0 +1,40 @@ +{ + "contracts": + { + "A": + { + "": + { + "evm": + { + "bytecode": + { + "functionDebugData": {}, + "linkReferences": {}, + "object": "", + "opcodes":"", + "sourceMap":"" + }, + "deployedBytecode": + { + "functionDebugData": {}, + "immutableReferences": + { + "6": + [ + { + "length": 32, + "start": 1 + } + ] + }, + "linkReferences": {}, + "object": "", + "opcodes":"", + "sourceMap":"" + } + } + } + } + } +} diff --git a/test/cmdlineTests/standard_import_evmasm_invalid_input_array/input.json b/test/cmdlineTests/standard_import_evmasm_invalid_input_array/input.json new file mode 100644 index 000000000000..7da583b81f12 --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_invalid_input_array/input.json @@ -0,0 +1,15 @@ +{ + "language": "EVMAssembly", + "sources": { + "A": { + "assemblyJson": [] + } + }, + "settings": { + "outputSelection": { + "*": { + "": ["evm.bytecode"] + } + } + } +} diff --git a/test/cmdlineTests/standard_import_evmasm_invalid_input_array/output.json b/test/cmdlineTests/standard_import_evmasm_invalid_input_array/output.json new file mode 100644 index 000000000000..2a075efd02ae --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_invalid_input_array/output.json @@ -0,0 +1,12 @@ +{ + "errors": + [ + { + "component": "general", + "formattedMessage": "Invalid input source specified. Expected exactly one object, named 'assemblyJson', inside $.sources.A", + "message": "Invalid input source specified. Expected exactly one object, named 'assemblyJson', inside $.sources.A", + "severity": "error", + "type": "JSONError" + } + ] +} diff --git a/test/cmdlineTests/standard_import_evmasm_invalid_key_inside_source/input.json b/test/cmdlineTests/standard_import_evmasm_invalid_key_inside_source/input.json new file mode 100644 index 000000000000..e697565d10ff --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_invalid_key_inside_source/input.json @@ -0,0 +1,28 @@ +{ + "language": "EVMAssembly", + "sources": { + "A": { + "invalidAssemblyJsonObject": { + ".code": [ + { + "begin": 36, + "end": 51, + "name": "PUSH", + "source": 0, + "value": "0" + } + ], + "sourceList": [ + "" + ] + } + } + }, + "settings": { + "outputSelection": { + "*": { + "": ["evm.bytecode"] + } + } + } +} diff --git a/test/cmdlineTests/standard_import_evmasm_invalid_key_inside_source/output.json b/test/cmdlineTests/standard_import_evmasm_invalid_key_inside_source/output.json new file mode 100644 index 000000000000..2a075efd02ae --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_invalid_key_inside_source/output.json @@ -0,0 +1,12 @@ +{ + "errors": + [ + { + "component": "general", + "formattedMessage": "Invalid input source specified. Expected exactly one object, named 'assemblyJson', inside $.sources.A", + "message": "Invalid input source specified. Expected exactly one object, named 'assemblyJson', inside $.sources.A", + "severity": "error", + "type": "JSONError" + } + ] +} diff --git a/test/cmdlineTests/standard_import_evmasm_invalid_key_other_source/input.json b/test/cmdlineTests/standard_import_evmasm_invalid_key_other_source/input.json new file mode 100644 index 000000000000..c627b34c375b --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_invalid_key_other_source/input.json @@ -0,0 +1,29 @@ +{ + "language": "EVMAssembly", + "sources": { + "A": { + "keccak256": "0x1234", + "assemblyJson": { + ".code": [ + { + "begin": 36, + "end": 51, + "name": "PUSH", + "source": 0, + "value": "0" + } + ], + "sourceList": [ + "" + ] + } + } + }, + "settings": { + "outputSelection": { + "*": { + "": ["evm.bytecode"] + } + } + } +} diff --git a/test/cmdlineTests/standard_import_evmasm_invalid_key_other_source/output.json b/test/cmdlineTests/standard_import_evmasm_invalid_key_other_source/output.json new file mode 100644 index 000000000000..2a075efd02ae --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_invalid_key_other_source/output.json @@ -0,0 +1,12 @@ +{ + "errors": + [ + { + "component": "general", + "formattedMessage": "Invalid input source specified. Expected exactly one object, named 'assemblyJson', inside $.sources.A", + "message": "Invalid input source specified. Expected exactly one object, named 'assemblyJson', inside $.sources.A", + "severity": "error", + "type": "JSONError" + } + ] +} diff --git a/test/cmdlineTests/standard_import_evmasm_invalid_no_source/input.json b/test/cmdlineTests/standard_import_evmasm_invalid_no_source/input.json new file mode 100644 index 000000000000..01cfc88a2755 --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_invalid_no_source/input.json @@ -0,0 +1,12 @@ +{ + "language": "EVMAssembly", + "sources": { + }, + "settings": { + "outputSelection": { + "*": { + "": ["evm.bytecode"] + } + } + } +} diff --git a/test/cmdlineTests/standard_import_evmasm_invalid_no_source/output.json b/test/cmdlineTests/standard_import_evmasm_invalid_no_source/output.json new file mode 100644 index 000000000000..3495a7411d9e --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_invalid_no_source/output.json @@ -0,0 +1,12 @@ +{ + "errors": + [ + { + "component": "general", + "formattedMessage": "No input sources specified.", + "message": "No input sources specified.", + "severity": "error", + "type": "JSONError" + } + ] +} diff --git a/test/cmdlineTests/standard_import_evmasm_invalid_opcode/input.json b/test/cmdlineTests/standard_import_evmasm_invalid_opcode/input.json new file mode 100644 index 000000000000..2e3e22cc410c --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_invalid_opcode/input.json @@ -0,0 +1,28 @@ +{ + "language": "EVMAssembly", + "sources": { + "A": { + "assemblyJson": { + ".code": [ + { + "begin": 36, + "end": 51, + "name": "INVALID_OPCODE", + "source": 0, + "value": "0" + } + ], + "sourceList": [ + "" + ] + } + } + }, + "settings": { + "outputSelection": { + "*": { + "": ["evm.bytecode"] + } + } + } +} diff --git a/test/cmdlineTests/standard_import_evmasm_invalid_opcode/output.json b/test/cmdlineTests/standard_import_evmasm_invalid_opcode/output.json new file mode 100644 index 000000000000..6314f8b05693 --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_invalid_opcode/output.json @@ -0,0 +1,12 @@ +{ + "errors": + [ + { + "component": "general", + "formattedMessage": "Assembly import error: InvalidOpcode", + "message": "Assembly import error: InvalidOpcode", + "severity": "error", + "type": "Exception" + } + ] +} diff --git a/test/cmdlineTests/standard_import_evmasm_invalid_two_sources/input.json b/test/cmdlineTests/standard_import_evmasm_invalid_two_sources/input.json new file mode 100644 index 000000000000..b5a668c0c1d9 --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_invalid_two_sources/input.json @@ -0,0 +1,20 @@ +{ + "language": "EVMAssembly", + "sources": { + "A": { + "assemblyJson": {} + }, + "B": { + "assemblyJson": {} + } + }, + "settings": { + "outputSelection": { + "*": { + "": [ + "evm.bytecode" + ] + } + } + } +} diff --git a/test/cmdlineTests/standard_import_evmasm_invalid_two_sources/output.json b/test/cmdlineTests/standard_import_evmasm_invalid_two_sources/output.json new file mode 100644 index 000000000000..affe4c34e651 --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_invalid_two_sources/output.json @@ -0,0 +1,12 @@ +{ + "errors": + [ + { + "component": "general", + "formattedMessage": "EVMAssembly import only supports exactly one input file.", + "message": "EVMAssembly import only supports exactly one input file.", + "severity": "error", + "type": "JSONError" + } + ] +} diff --git a/test/cmdlineTests/standard_import_evmasm_link_references/input.json b/test/cmdlineTests/standard_import_evmasm_link_references/input.json new file mode 100644 index 000000000000..47693fa14838 --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_link_references/input.json @@ -0,0 +1,38 @@ +{ + "language": "EVMAssembly", + "sources": { + "A": { + "assemblyJson": { + ".code": [], + ".data": { + "0": { + ".auxdata": "a264697066735822122049fc8a5c547ec9ab3b6a5804732f6ef55e87fdb28a3a8826c23cdeb89c034e3d64736f6c63430008170033", + ".code": [ + { + "begin": 133, + "end": 134, + "name": "PUSHLIB", + "source": 0, + "value": "test.sol:L" + } + ] + } + }, + "sourceList": [ + "test.sol", + "#utility.yul" + ] + } + } + }, + "settings": { + "outputSelection": { + "*": { + "": [ + "evm.bytecode", + "evm.deployedBytecode" + ] + } + } + } +} diff --git a/test/cmdlineTests/standard_import_evmasm_link_references/output.json b/test/cmdlineTests/standard_import_evmasm_link_references/output.json new file mode 100644 index 000000000000..864f65a53f70 --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_link_references/output.json @@ -0,0 +1,43 @@ +{ + "contracts": + { + "A": + { + "": + { + "evm": + { + "bytecode": + { + "functionDebugData": {}, + "linkReferences": {}, + "object": "", + "opcodes":"", + "sourceMap": "" + }, + "deployedBytecode": + { + "functionDebugData": {}, + "immutableReferences": {}, + "linkReferences": + { + "test.sol": + { + "L": + [ + { + "length": 20, + "start": 1 + } + ] + } + }, + "object": "__$d553ddd21b3d8da0d69a6510c170f4a77b$__", + "opcodes":"", + "sourceMap":"" + } + } + } + } + } +} diff --git a/test/cmdlineTests/standard_import_evmasm_multiple_keys_inside_source/input.json b/test/cmdlineTests/standard_import_evmasm_multiple_keys_inside_source/input.json new file mode 100644 index 000000000000..0a8a5988ce19 --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_multiple_keys_inside_source/input.json @@ -0,0 +1,32 @@ +{ + "language": "EVMAssembly", + "sources": { + "A": { + "assemblyJson": { + ".code": [ + { + "begin": 36, + "end": 51, + "name": "PUSH", + "source": 0, + "value": "0" + } + ], + "sourceList": [ + "" + ] + }, + "urls": + [ + "bzzr://56ab..." + ] + } + }, + "settings": { + "outputSelection": { + "*": { + "": ["evm.bytecode"] + } + } + } +} diff --git a/test/cmdlineTests/standard_import_evmasm_multiple_keys_inside_source/output.json b/test/cmdlineTests/standard_import_evmasm_multiple_keys_inside_source/output.json new file mode 100644 index 000000000000..2a075efd02ae --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_multiple_keys_inside_source/output.json @@ -0,0 +1,12 @@ +{ + "errors": + [ + { + "component": "general", + "formattedMessage": "Invalid input source specified. Expected exactly one object, named 'assemblyJson', inside $.sources.A", + "message": "Invalid input source specified. Expected exactly one object, named 'assemblyJson', inside $.sources.A", + "severity": "error", + "type": "JSONError" + } + ] +} diff --git a/test/cmdlineTests/standard_import_evmasm_no_output_selection/input.json b/test/cmdlineTests/standard_import_evmasm_no_output_selection/input.json new file mode 100644 index 000000000000..1cd9c3c7d575 --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_no_output_selection/input.json @@ -0,0 +1,29 @@ +{ + "language": "EVMAssembly", + "sources": { + "A": { + "assemblyJson": { + ".code": [ + { + "begin": 36, + "end": 51, + "name": "PUSH", + "source": 0, + "value": "0" + } + ], + "sourceList": [ + "" + ] + } + } + }, + "settings": { + "outputSelection": { + "*": { + "": [ + ] + } + } + } +} diff --git a/test/cmdlineTests/standard_import_evmasm_no_output_selection/output.json b/test/cmdlineTests/standard_import_evmasm_no_output_selection/output.json new file mode 100644 index 000000000000..0967ef424bce --- /dev/null +++ b/test/cmdlineTests/standard_import_evmasm_no_output_selection/output.json @@ -0,0 +1 @@ +{} diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 4971ce3cd1cf..ab013692d5ea 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -187,7 +187,7 @@ BOOST_AUTO_TEST_CASE(invalid_language) } )"; Json::Value result = compile(input); - BOOST_CHECK(containsError(result, "JSONError", "Only \"Solidity\", \"Yul\" or \"SolidityAST\" is supported as a language.")); + BOOST_CHECK(containsError(result, "JSONError", "Only \"Solidity\", \"Yul\", \"SolidityAST\" or \"EVMAssembly\" is supported as a language.")); } BOOST_AUTO_TEST_CASE(valid_language) From 6d7fc69f6de12d2671ec45df3a0458637ca10a60 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 25 Jan 2024 08:05:27 +0100 Subject: [PATCH 074/189] Fix broken link in docs --- docs/resources.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/resources.rst b/docs/resources.rst index 84df74fb73cb..85b3cf9a3912 100644 --- a/docs/resources.rst +++ b/docs/resources.rst @@ -25,9 +25,6 @@ Integrated (Ethereum) Development Environments * `Dapp `_ Tool for building, testing and deploying smart contracts from the command-line. - * `Embark `_ - Developer platform for building and deploying decentralized applications. - * `Foundry `_ Fast, portable and modular toolkit for Ethereum application development written in Rust. From fe3b94de66f950e41856b9053ecd186d5804aba9 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 25 Jan 2024 08:29:03 +0100 Subject: [PATCH 075/189] Order changelog --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index db488b183a1f..8195a2c61d21 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,8 +12,8 @@ Language Features: Compiler Features: * EVM: Support for the EVM Version "Cancun". * SMTChecker: Support `bytes.concat` except when string literals are passed as arguments. -* TypeChecker: Comparison of internal function pointers now yields a warning, as it can produce unexpected results with the legacy pipeline enabled. * Standard JSON Interface: Add experimental support to import EVM assembly in the format used by ``--asm-json``. +* TypeChecker: Comparison of internal function pointers now yields a warning, as it can produce unexpected results with the legacy pipeline enabled. Bugfixes: From 0da45273674ff2e4c33f60bd83d1622110cbfc19 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 25 Jan 2024 09:19:23 +0100 Subject: [PATCH 076/189] Update changelog for 0.8.24 release --- Changelog.md | 2 +- docs/bugs_by_version.json | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 8195a2c61d21..d36470b01636 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,4 @@ -### 0.8.24 (unreleased) +### 0.8.24 (2024-01-25) Language Features: * Introduce global ``block.blobbasefee`` for retrieving the blob base fee of the current block. diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 152e8d97a4d5..156b846d012d 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1886,6 +1886,10 @@ "bugs": [], "released": "2023-11-08" }, + "0.8.24": { + "bugs": [], + "released": "2024-01-25" + }, "0.8.3": { "bugs": [ "FullInlinerNonExpressionSplitArgumentEvaluationOrder", From 0e03deca512098460d8fcba52bc5e0cb003e90e5 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 25 Jan 2024 09:36:37 +0100 Subject: [PATCH 077/189] Set version to 0.8.25 --- CMakeLists.txt | 2 +- Changelog.md | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f6a905e39aa8..18af34d26bf0 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.24") +set(PROJECT_VERSION "0.8.25") # 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 d36470b01636..171dce2ef756 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,14 @@ +### 0.8.25 (unreleased) + +Language Features: + + +Compiler Features: + + +Bugfixes: + + ### 0.8.24 (2024-01-25) Language Features: From a70cae9c9b6a7b6e2fe6556694adebe320d698f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 26 Jan 2024 17:01:56 +0100 Subject: [PATCH 078/189] Fix 0.8.23 changelog formatting --- Changelog.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 171dce2ef756..7ab13de8fe4f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -21,10 +21,10 @@ Language Features: Compiler Features: -* EVM: Support for the EVM Version "Cancun". -* SMTChecker: Support `bytes.concat` except when string literals are passed as arguments. -* Standard JSON Interface: Add experimental support to import EVM assembly in the format used by ``--asm-json``. -* TypeChecker: Comparison of internal function pointers now yields a warning, as it can produce unexpected results with the legacy pipeline enabled. + * EVM: Support for the EVM Version "Cancun". + * SMTChecker: Support `bytes.concat` except when string literals are passed as arguments. + * Standard JSON Interface: Add experimental support to import EVM assembly in the format used by ``--asm-json``. + * TypeChecker: Comparison of internal function pointers now yields a warning, as it can produce unexpected results with the legacy pipeline enabled. Bugfixes: From 6d5aa393b9261d98f2ef2b4526425a073be36423 Mon Sep 17 00:00:00 2001 From: Dimitris Apostolou Date: Fri, 26 Jan 2024 18:25:37 +0200 Subject: [PATCH 079/189] Fix typos --- libevmasm/ConstantOptimiser.h | 2 +- libsolidity/codegen/YulUtilFunctions.cpp | 2 +- libsolidity/interface/StandardCompiler.h | 2 +- libsolidity/lsp/Transport.h | 2 +- test/evmc/evmc.h | 2 +- test/tools/ossfuzz/README.md | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libevmasm/ConstantOptimiser.h b/libevmasm/ConstantOptimiser.h index b491fb00422f..139e263ba369 100644 --- a/libevmasm/ConstantOptimiser.h +++ b/libevmasm/ConstantOptimiser.h @@ -70,7 +70,7 @@ class ConstantOptimisationMethod virtual ~ConstantOptimisationMethod() = default; virtual bigint gasNeeded() const = 0; /// Executes the method, potentially appending to the assembly and returns a vector of - /// assembly items the constant should be relpaced with in one sweep. + /// assembly items the constant should be replaced with in one sweep. /// If the vector is empty, the constants will not be deleted. virtual AssemblyItems execute(Assembly& _assembly) const = 0; diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 5ff5ac4f3b98..35fea8ac4b76 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -3866,7 +3866,7 @@ std::string YulUtilFunctions::cleanupFunction(Type const& _type) } case Type::Category::Enum: { - // Out of range enums cannot be truncated unambigiously and therefore it should be an error. + // Out of range enums cannot be truncated unambiguously and therefore it should be an error. templ("body", "cleaned := value " + validatorFunction(_type, false) + "(value)"); break; } diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h index 93cc73f3a7e2..03bdc7f12bd2 100644 --- a/libsolidity/interface/StandardCompiler.h +++ b/libsolidity/interface/StandardCompiler.h @@ -59,7 +59,7 @@ class StandardCompiler /// Sets all input parameters according to @a _input which conforms to the standardized input /// format, performs compilation and returns a standardized output. Json::Value compile(Json::Value const& _input) noexcept; - /// Parses input as JSON and peforms the above processing steps, returning a serialized JSON + /// Parses input as JSON and performs the above processing steps, returning a serialized JSON /// output. Parsing errors are returned as regular errors. std::string compile(std::string const& _input) noexcept; diff --git a/libsolidity/lsp/Transport.h b/libsolidity/lsp/Transport.h index 3e94e0398119..e3b03bcc7f2f 100644 --- a/libsolidity/lsp/Transport.h +++ b/libsolidity/lsp/Transport.h @@ -122,7 +122,7 @@ class Transport /// the message body from the transport line. virtual std::string readBytes(size_t _byteCount) = 0; - // Mimmicks std::getline() on this Transport API. + // Mimics std::getline() on this Transport API. virtual std::string getline() = 0; /// Writes the given payload @p _data to transport. diff --git a/test/evmc/evmc.h b/test/evmc/evmc.h index 48b03e554556..571c97f474e0 100644 --- a/test/evmc/evmc.h +++ b/test/evmc/evmc.h @@ -1181,7 +1181,7 @@ struct evmc_vm * * @par Binaries naming convention * For VMs distributed as shared libraries, the name of the library SHOULD match the VM name. - * The convetional library filename prefixes and extensions SHOULD be ignored by the Client. + * The conventional library filename prefixes and extensions SHOULD be ignored by the Client. * For example, the shared library with the "beta-interpreter" implementation may be named * `libbeta-interpreter.so`. * diff --git a/test/tools/ossfuzz/README.md b/test/tools/ossfuzz/README.md index 9343602d44d2..dae338648980 100644 --- a/test/tools/ossfuzz/README.md +++ b/test/tools/ossfuzz/README.md @@ -69,7 +69,7 @@ To help oss-fuzz do this, we (as project maintainers) need to provide the follow To be consistent and aid better evaluation of the utility of the fuzzing dictionary, we stick to the following rules-of-thumb: - Full tokens such as `block.number` are preceded and followed by a whitespace - - Incomplete tokens including function calls such as `msg.sender.send()` are abbreviated `.send(` to provide some leeway to the fuzzer to sythesize variants such as `address(this).send()` + - Incomplete tokens including function calls such as `msg.sender.send()` are abbreviated `.send(` to provide some leeway to the fuzzer to synthesize variants such as `address(this).send()` - Language keywords are suffixed by a whitespace with the exception of those that end a line of code such as `break;` and `continue;` [1]: https://github.com/google/oss-fuzz From fb99132474c177fc861305feeafd43b9cbb93b66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 13 Dec 2023 19:21:58 +0100 Subject: [PATCH 080/189] TypeInference: Remove unreachable errors on missing instantiation annotations guaranteed by previous passes --- .../experimental/analysis/TypeInference.cpp | 50 +++++++------------ scripts/error_codes.py | 5 -- ...instantiation_invalid_type_constructor.sol | 13 +++++ .../inference/instantiation_not_for_class.sol | 12 +++++ 4 files changed, 44 insertions(+), 36 deletions(-) create mode 100644 test/libsolidity/syntaxTests/experimental/inference/instantiation_invalid_type_constructor.sol create mode 100644 test/libsolidity/syntaxTests/experimental/inference/instantiation_not_for_class.sol diff --git a/libsolidity/experimental/analysis/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp index a13d6ebc9403..9ec7c9786724 100644 --- a/libsolidity/experimental/analysis/TypeInference.cpp +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -621,45 +621,33 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) if (instantiationAnnotation.type) return false; instantiationAnnotation.type = m_voidType; - std::optional typeClass = std::visit(util::GenericVisitor{ - [&](ASTPointer _typeClassName) -> std::optional { - if (auto const* typeClassDefinition = dynamic_cast(_typeClassName->annotation().referencedDeclaration)) - { - // visiting the type class will re-visit this instantiation - typeClassDefinition->accept(*this); - // TODO: more error handling? Should be covered by the visit above. - solAssert(m_analysis.annotation(*typeClassDefinition).typeClass.has_value()); - return m_analysis.annotation(*typeClassDefinition).typeClass.value(); - } - else - { - m_errorReporter.typeError(9817_error, _typeClassInstantiation.typeClass().location(), "Expected type class."); - return std::nullopt; - } + TypeClass typeClass = std::visit(util::GenericVisitor{ + [&](ASTPointer _typeClassName) -> TypeClass { + auto const* typeClassDefinition = dynamic_cast(_typeClassName->annotation().referencedDeclaration); + solAssert(typeClassDefinition); + + // visiting the type class will re-visit this instantiation + typeClassDefinition->accept(*this); + // TODO: more error handling? Should be covered by the visit above. + solAssert(m_analysis.annotation(*typeClassDefinition).typeClass.has_value()); + return m_analysis.annotation(*typeClassDefinition).typeClass.value(); }, - [&](Token _token) -> std::optional { - if (auto builtinClass = builtinClassFromToken(_token)) - if (auto typeClass = util::valueOrNullptr(annotation().builtinClasses, *builtinClass)) - return *typeClass; - m_errorReporter.typeError(2658_error, _typeClassInstantiation.location(), "Invalid type class name."); - return std::nullopt; + [&](Token _token) -> TypeClass { + std::optional builtinClass = builtinClassFromToken(_token); + solAssert(builtinClass.has_value()); + solAssert(annotation().builtinClasses.count(*builtinClass) != 0); + return annotation().builtinClasses.at(*builtinClass); } }, _typeClassInstantiation.typeClass().name()); - if (!typeClass) - return false; // TODO: _typeClassInstantiation.typeConstructor().accept(*this); ? auto typeConstructor = m_analysis.annotation(_typeClassInstantiation.typeConstructor()).typeConstructor; - if (!typeConstructor) - { - m_errorReporter.typeError(2138_error, _typeClassInstantiation.typeConstructor().location(), "Invalid type constructor."); - return false; - } + solAssert(typeConstructor); std::vector arguments; Arity arity{ {}, - *typeClass + typeClass }; { @@ -692,10 +680,10 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) if (auto error = m_typeSystem.instantiateClass(instanceType, arity)) m_errorReporter.typeError(5094_error, _typeClassInstantiation.location(), *error); - auto const& classFunctions = annotation().typeClassFunctions.at(*typeClass); + auto const& classFunctions = annotation().typeClassFunctions.at(typeClass); TypeEnvironment newEnv = m_env->clone(); - if (!newEnv.unify(m_typeSystem.typeClassVariable(*typeClass), instanceType).empty()) + if (!newEnv.unify(m_typeSystem.typeClassVariable(typeClass), instanceType).empty()) { m_errorReporter.typeError(4686_error, _typeClassInstantiation.location(), "Unification of class and instance variable failed."); return false; diff --git a/scripts/error_codes.py b/scripts/error_codes.py index d4e9e5486665..8ffb7f2160f4 100755 --- a/scripts/error_codes.py +++ b/scripts/error_codes.py @@ -275,18 +275,15 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): "1801", "1807", "2015", - "2138", "2345", "2399", "2599", "2655", - "2658", "2934", "3101", "3111", "3195", "3520", - "3570", "3573", "3654", "4316", @@ -329,8 +326,6 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): "9282", "9603", "9658", - "9817", - "9831", "9988", } diff --git a/test/libsolidity/syntaxTests/experimental/inference/instantiation_invalid_type_constructor.sol b/test/libsolidity/syntaxTests/experimental/inference/instantiation_invalid_type_constructor.sol new file mode 100644 index 000000000000..0245deb1f401 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/instantiation_invalid_type_constructor.sol @@ -0,0 +1,13 @@ +pragma experimental solidity; + +class Self: C {} + +function f() {} + +instantiation f: C {} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 9831: (80-81): Expected type declaration. diff --git a/test/libsolidity/syntaxTests/experimental/inference/instantiation_not_for_class.sol b/test/libsolidity/syntaxTests/experimental/inference/instantiation_not_for_class.sol new file mode 100644 index 000000000000..c48fff74f94e --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/instantiation_not_for_class.sol @@ -0,0 +1,12 @@ +pragma experimental solidity; + +type T; +type U; + +instantiation T: U {} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 3570: (65-66): Expected a type class. From 9d80e0e3ee42acfcef6926fefc64ccd21afbce2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 8 Jan 2024 19:02:04 +0100 Subject: [PATCH 081/189] Yul proto fuzzer: Add mcopy builtin to generator. --- test/tools/ossfuzz/protoToYul.cpp | 15 ++++++++++++++- test/tools/ossfuzz/protoToYul.h | 4 +++- test/tools/ossfuzz/yulProto.proto | 1 + 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/test/tools/ossfuzz/protoToYul.cpp b/test/tools/ossfuzz/protoToYul.cpp index 6c8f291d29a3..75074db3b064 100644 --- a/test/tools/ossfuzz/protoToYul.cpp +++ b/test/tools/ossfuzz/protoToYul.cpp @@ -796,6 +796,10 @@ void ProtoConverter::visit(CopyFunc const& _x) if (type == CopyFunc::RETURNDATA && !m_evmVersion.supportsReturndata()) return; + // Bail out if MCOPY is not supported for fuzzed EVM version + if (type == CopyFunc::MEMORY && !m_evmVersion.hasMcopy()) + return; + // Code copy may change state if e.g., some byte of code // is stored to storage via a sequence of mload and sstore. if (m_filterStatefulInstructions && type == CopyFunc::CODE) @@ -816,13 +820,22 @@ void ProtoConverter::visit(CopyFunc const& _x) case CopyFunc::DATA: m_output << "datacopy"; break; + case CopyFunc::MEMORY: + m_output << "mcopy"; } m_output << "("; m_output << "mod("; visit(_x.target()); m_output << ", " << to_string(s_maxMemory - s_maxSize) << ")"; m_output << ", "; - visit(_x.source()); + if (type == CopyFunc::MEMORY) + { + m_output << "mod("; + visit(_x.source()); + m_output << ", " << to_string(s_maxMemory - s_maxSize) << ")"; + } + else + visit(_x.source()); m_output << ", "; m_output << "mod("; visit(_x.size()); diff --git a/test/tools/ossfuzz/protoToYul.h b/test/tools/ossfuzz/protoToYul.h index b7f7c72e5a8e..c264dd51af13 100644 --- a/test/tools/ossfuzz/protoToYul.h +++ b/test/tools/ossfuzz/protoToYul.h @@ -346,7 +346,9 @@ class ProtoConverter static auto constexpr s_dataIdentifier = "datablock"; /// Upper bound on memory writes is 64KB in order to /// preserve semantic equivalence in the presence of - /// memory guard + /// memory guard. Note that s_maxMemory must be much larger + /// than s_maxSize to create tests without significant overlap + /// of I/O memory regions. static unsigned constexpr s_maxMemory = 65536; /// Upper bound on size for range copy functions static unsigned constexpr s_maxSize = 32768; diff --git a/test/tools/ossfuzz/yulProto.proto b/test/tools/ossfuzz/yulProto.proto index 7552fb23adde..afe70363ae2b 100644 --- a/test/tools/ossfuzz/yulProto.proto +++ b/test/tools/ossfuzz/yulProto.proto @@ -186,6 +186,7 @@ message CopyFunc { CODE = 1; RETURNDATA = 2; DATA = 3; + MEMORY = 4; } required CopyType ct = 1; required Expression target = 2; From fb7c2b4b9083a041e4ec3ab13265eed3015d6b7b Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Thu, 25 Jan 2024 06:54:08 +0100 Subject: [PATCH 082/189] Yul fuzzer: Support transient storage. --- test/tools/ossfuzz/protoToYul.cpp | 35 +++++++++++++++++++++++++------ test/tools/ossfuzz/yulProto.proto | 14 +++++++------ 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/test/tools/ossfuzz/protoToYul.cpp b/test/tools/ossfuzz/protoToYul.cpp index 6c8f291d29a3..7ad35d66d358 100644 --- a/test/tools/ossfuzz/protoToYul.cpp +++ b/test/tools/ossfuzz/protoToYul.cpp @@ -593,6 +593,12 @@ void ProtoConverter::visit(UnaryOp const& _x) return; } + if (op == UnaryOp::TLOAD && !m_evmVersion.supportsTransientStorage()) + { + m_output << dictionaryToken(); + return; + } + // The following instructions may lead to change of EVM state and are hence // excluded to avoid false positives. if ( @@ -620,6 +626,9 @@ void ProtoConverter::visit(UnaryOp const& _x) case UnaryOp::SLOAD: m_output << "sload"; break; + case UnaryOp::TLOAD: + m_output << "tload"; + break; case UnaryOp::ISZERO: m_output << "iszero"; break; @@ -1141,9 +1150,12 @@ void ProtoConverter::visit(StoreFunc const& _x) case StoreFunc::MSTORE8: m_output << "mstore8("; break; + case StoreFunc::TSTORE: + m_output << "tstore("; + break; } // Write to memory within bounds, storage is unbounded - if (storeType == StoreFunc::SSTORE) + if (storeType == StoreFunc::SSTORE || storeType == StoreFunc::TSTORE) visit(_x.loc()); else if (storeType == StoreFunc::MSTORE8) { @@ -1755,18 +1767,29 @@ void ProtoConverter::fillFunctionCallInput(unsigned _numInParams) void ProtoConverter::saveFunctionCallOutput(vector const& _varsVec) { - for (auto const& var: _varsVec) + constexpr auto numSlots = 10; + constexpr auto slotSize = 32; + + for (string const& var: _varsVec) { // Flip a dice to choose whether to save output values // in storage or memory. - bool coinFlip = counter() % 2 == 0; + unsigned diceThrow = counter() % (m_evmVersion.supportsTransientStorage() ? 3 : 2); // Pseudo-randomly choose one of the first ten 32-byte // aligned slots. - string slot = to_string((counter() % 10) * 32); - if (coinFlip) + string slot = std::to_string((counter() % numSlots) * slotSize); + if (diceThrow == 0) m_output << "sstore(" << slot << ", " << var << ")\n"; - else + else if (diceThrow == 1) m_output << "mstore(" << slot << ", " << var << ")\n"; + else + { + yulAssert( + m_evmVersion.supportsTransientStorage(), + "Proto fuzzer: Invalid evm version" + ); + m_output << "tstore(" << slot << ", " << var << ")\n"; + } } } diff --git a/test/tools/ossfuzz/yulProto.proto b/test/tools/ossfuzz/yulProto.proto index 7552fb23adde..f4eabab64aed 100644 --- a/test/tools/ossfuzz/yulProto.proto +++ b/test/tools/ossfuzz/yulProto.proto @@ -149,12 +149,13 @@ message UnaryOp { NOT = 0; MLOAD = 1; SLOAD = 2; - ISZERO = 3; - CALLDATALOAD = 4; - EXTCODESIZE = 5; - EXTCODEHASH = 6; - BALANCE = 7; - BLOCKHASH = 8; + TLOAD = 3; + ISZERO = 4; + CALLDATALOAD = 5; + EXTCODESIZE = 6; + EXTCODEHASH = 7; + BALANCE = 8; + BLOCKHASH = 9; } required UOp op = 1; required Expression operand = 2; @@ -230,6 +231,7 @@ message StoreFunc { MSTORE = 0; SSTORE = 1; MSTORE8 = 2; + TSTORE = 3; } required Expression loc = 1; required Expression val = 2; From e545e936d20375757ef25d473c113616bccaae0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 30 Oct 2023 10:41:11 +0100 Subject: [PATCH 083/189] TypeSystemHelper: substitute() --- .../experimental/ast/TypeSystemHelper.cpp | 27 ++++++++++++++++++- .../experimental/ast/TypeSystemHelper.h | 2 ++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/libsolidity/experimental/ast/TypeSystemHelper.cpp b/libsolidity/experimental/ast/TypeSystemHelper.cpp index bc7dd4fcc35a..36576d9cd8cc 100644 --- a/libsolidity/experimental/ast/TypeSystemHelper.cpp +++ b/libsolidity/experimental/ast/TypeSystemHelper.cpp @@ -287,9 +287,34 @@ std::vector TypeEnvironmentHelpers::typeVars(Type _type) con }; typeVarsImpl(_type, typeVarsImpl); return typeVars; - } +experimental::Type TypeEnvironmentHelpers::substitute( + Type const& _type, + Type const& _partToReplace, + Type const& _replacement +) const +{ + using ranges::views::transform; + using ranges::to; + + if (env.typeEquals(_type, _partToReplace)) + return _replacement; + + auto recurse = [&](Type const& _t) { return substitute(_t, _partToReplace, _replacement); }; + + return visit(util::GenericVisitor{ + [&](TypeConstant const& _typeConstant) -> Type { + return TypeConstant{ + _typeConstant.constructor, + _typeConstant.arguments | transform(recurse) | to>, + }; + }, + [&](auto const& _type) -> Type { + return _type; + }, + }, env.resolve(_type)); +} std::string TypeSystemHelpers::sortToString(Sort _sort) const { diff --git a/libsolidity/experimental/ast/TypeSystemHelper.h b/libsolidity/experimental/ast/TypeSystemHelper.h index fd55c1bf842e..d8a3092d23f2 100644 --- a/libsolidity/experimental/ast/TypeSystemHelper.h +++ b/libsolidity/experimental/ast/TypeSystemHelper.h @@ -55,6 +55,8 @@ struct TypeEnvironmentHelpers std::string typeToString(Type const& _type) const; std::string canonicalTypeName(Type _type) const; std::vector typeVars(Type _type) const; + + Type substitute(Type const& _type, Type const& _partToReplace, Type const& _replacement) const; }; } From 44ffdb1eaf9693ba0c776a7f3977dac0ff5e3a71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 30 Oct 2023 10:56:26 +0100 Subject: [PATCH 084/189] Introduce fixed type variables --- libsolidity/experimental/ast/TypeSystem.cpp | 40 ++++++++++++++----- libsolidity/experimental/ast/TypeSystem.h | 11 +++++ .../experimental/ast/TypeSystemHelper.cpp | 13 ++++++ .../experimental/ast/TypeSystemHelper.h | 2 + 4 files changed, 56 insertions(+), 10 deletions(-) diff --git a/libsolidity/experimental/ast/TypeSystem.cpp b/libsolidity/experimental/ast/TypeSystem.cpp index 680edce845f6..fac5073bb0ca 100644 --- a/libsolidity/experimental/ast/TypeSystem.cpp +++ b/libsolidity/experimental/ast/TypeSystem.cpp @@ -50,18 +50,21 @@ std::vector TypeEnvironment::unify(Type _a, [&](TypeVariable _left, TypeVariable _right) { if (_left.index() == _right.index()) solAssert(_left.sort() == _right.sort()); + else if (isFixedTypeVar(_left) && isFixedTypeVar(_right)) + unificationFailure(); + else if (isFixedTypeVar(_left)) + failures += instantiate(_right, _left); + else if (isFixedTypeVar(_right)) + failures += instantiate(_left, _right); + else if (_left.sort() <= _right.sort()) + failures += instantiate(_left, _right); + else if (_right.sort() <= _left.sort()) + failures += instantiate(_right, _left); else { - if (_left.sort() <= _right.sort()) - failures += instantiate(_left, _right); - else if (_right.sort() <= _left.sort()) - failures += instantiate(_right, _left); - else - { - Type newVar = m_typeSystem.freshVariable(_left.sort() + _right.sort()); - failures += instantiate(_left, newVar); - failures += instantiate(_right, newVar); - } + Type newVar = m_typeSystem.freshVariable(_left.sort() + _right.sort()); + failures += instantiate(_left, newVar); + failures += instantiate(_right, newVar); } }, [&](TypeVariable _var, auto) { @@ -112,6 +115,23 @@ bool TypeEnvironment::typeEquals(Type _lhs, Type _rhs) const }, resolve(_lhs), resolve(_rhs)); } + +bool TypeEnvironment::isFixedTypeVar(Type const& _typeVar) const +{ + return + std::holds_alternative(_typeVar) && + m_fixedTypeVariables.count(std::get(_typeVar).index()) != 0; +} + +void TypeEnvironment::fixTypeVars(std::vector const& _typeVars) +{ + for (Type const& typeVar: _typeVars) + { + solAssert(std::holds_alternative(typeVar)); + m_fixedTypeVariables.insert(std::get(typeVar).index()); + } +} + TypeEnvironment TypeEnvironment::clone() const { TypeEnvironment result{m_typeSystem}; diff --git a/libsolidity/experimental/ast/TypeSystem.h b/libsolidity/experimental/ast/TypeSystem.h index cac0feb81fe2..c3aa5001a16d 100644 --- a/libsolidity/experimental/ast/TypeSystem.h +++ b/libsolidity/experimental/ast/TypeSystem.h @@ -74,6 +74,9 @@ class TypeEnvironment TypeSystem& typeSystem() { return m_typeSystem; } TypeSystem const& typeSystem() const { return m_typeSystem; } + bool isFixedTypeVar(Type const& _typeVar) const; + void fixTypeVars(std::vector const& _typeVars); + private: TypeEnvironment(TypeEnvironment&& _env): m_typeSystem(_env.m_typeSystem), @@ -83,7 +86,15 @@ class TypeEnvironment [[nodiscard]] std::vector instantiate(TypeVariable _variable, Type _type); TypeSystem& m_typeSystem; + + /// For each @a TypeVariable (identified by its index) stores the type is has been successfully + /// unified with. Used for type resolution. Note that @a Type may itself be a type variable + /// or may contain type variables so resolution must be recursive. std::map m_typeVariables; + + /// Type variables marked as fixed free type variables (as opposed to generic type variables). + /// Identified by their indices. + std::set m_fixedTypeVariables; }; class TypeSystem diff --git a/libsolidity/experimental/ast/TypeSystemHelper.cpp b/libsolidity/experimental/ast/TypeSystemHelper.cpp index 36576d9cd8cc..0924754cd51b 100644 --- a/libsolidity/experimental/ast/TypeSystemHelper.cpp +++ b/libsolidity/experimental/ast/TypeSystemHelper.cpp @@ -25,6 +25,7 @@ #include +#include #include #include #include @@ -289,6 +290,18 @@ std::vector TypeEnvironmentHelpers::typeVars(Type _type) con return typeVars; } + +bool TypeEnvironmentHelpers::hasGenericTypeVars(Type const& _type) const +{ + return ranges::any_of( + TypeEnvironmentHelpers{*this}.typeVars(_type), + [&](Type const& _maybeTypeVar) { + solAssert(std::holds_alternative(_maybeTypeVar)); + return !env.isFixedTypeVar(_maybeTypeVar); + } + ); +} + experimental::Type TypeEnvironmentHelpers::substitute( Type const& _type, Type const& _partToReplace, diff --git a/libsolidity/experimental/ast/TypeSystemHelper.h b/libsolidity/experimental/ast/TypeSystemHelper.h index d8a3092d23f2..ba395ace3a62 100644 --- a/libsolidity/experimental/ast/TypeSystemHelper.h +++ b/libsolidity/experimental/ast/TypeSystemHelper.h @@ -56,6 +56,8 @@ struct TypeEnvironmentHelpers std::string canonicalTypeName(Type _type) const; std::vector typeVars(Type _type) const; + bool hasGenericTypeVars(Type const& _type) const; + Type substitute(Type const& _type, Type const& _partToReplace, Type const& _replacement) const; }; From fd3c393f9a753b491964f3301c7eb4d5357d8a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 30 Oct 2023 10:56:47 +0100 Subject: [PATCH 085/189] Use different prefixes when printing fixed and generic type variables --- .../experimental/ast/TypeSystemHelper.cpp | 2 +- .../builtin/builtin_type_definition.sol | 20 +-- .../inference/polymorphic_function_call.sol | 24 ++-- .../inference/polymorphic_type.sol | 42 +++--- .../polymorphic_type_abs_and_rep.sol | 42 +++--- ...rphic_type_instantiation_and_operators.sol | 124 +++++++++--------- 6 files changed, 127 insertions(+), 127 deletions(-) diff --git a/libsolidity/experimental/ast/TypeSystemHelper.cpp b/libsolidity/experimental/ast/TypeSystemHelper.cpp index 0924754cd51b..9ad18e1608bc 100644 --- a/libsolidity/experimental/ast/TypeSystemHelper.cpp +++ b/libsolidity/experimental/ast/TypeSystemHelper.cpp @@ -419,7 +419,7 @@ std::string TypeEnvironmentHelpers::typeToString(Type const& _type) const while (index /= 26) varName += static_cast('a' + (index%26)); reverse(varName.begin(), varName.end()); - stream << '\'' << varName; + stream << (env.isFixedTypeVar(_type) ? "'" : "?") << varName; switch (_type.sort().classes.size()) { case 0: diff --git a/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition.sol b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition.sol index fc4783ceba19..fb76e1948b87 100644 --- a/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition.sol +++ b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition.sol @@ -40,14 +40,14 @@ contract C { // Info 4164: (94-124): Inferred type: word // Info 4164: (125-161): Inferred type: integer // Info 4164: (162-192): Inferred type: () -// Info 4164: (194-228): Inferred type: tfun(('u:type, 'v:type), 'u:type -> 'v:type) -// Info 4164: (202-208): Inferred type: ('s:type, 't:type) -// Info 4164: (203-204): Inferred type: 's:type -// Info 4164: (206-207): Inferred type: 't:type -// Info 4164: (229-265): Inferred type: tfun(('y:type, 'z:type), ('y:type, 'z:type)) -// Info 4164: (238-244): Inferred type: ('w:type, 'x:type) -// Info 4164: (239-240): Inferred type: 'w:type -// Info 4164: (242-243): Inferred type: 'x:type +// Info 4164: (194-228): Inferred type: tfun((?u:type, ?v:type), ?u:type -> ?v:type) +// Info 4164: (202-208): Inferred type: (?s:type, ?t:type) +// Info 4164: (203-204): Inferred type: ?s:type +// Info 4164: (206-207): Inferred type: ?t:type +// Info 4164: (229-265): Inferred type: tfun((?y:type, ?z:type), (?y:type, ?z:type)) +// Info 4164: (238-244): Inferred type: (?w:type, ?x:type) +// Info 4164: (239-240): Inferred type: ?w:type +// Info 4164: (242-243): Inferred type: ?x:type // Info 4164: (284-584): Inferred type: () -> () // Info 4164: (292-294): Inferred type: () // Info 4164: (318-325): Inferred type: void @@ -84,9 +84,9 @@ contract C { // Info 4164: (525-529): Inferred type: word // Info 4164: (540-553): Inferred type: bool // Info 4164: (540-550): Inferred type: (bool, word) -> bool -// Info 4164: (540-544): Inferred type: ('bl:type, 'bm:type) +// Info 4164: (540-544): Inferred type: (?bl:type, ?bm:type) // Info 4164: (551-552): Inferred type: (bool, word) // Info 4164: (563-577): Inferred type: word // Info 4164: (563-574): Inferred type: (bool, word) -> word -// Info 4164: (563-567): Inferred type: ('bq:type, 'br:type) +// Info 4164: (563-567): Inferred type: (?bq:type, ?br:type) // Info 4164: (575-576): Inferred type: (bool, word) diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol index eacc895202f3..487aea090581 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol @@ -15,18 +15,18 @@ function run(a: T, b: U(T), c: U(U(T))) { // ---- // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. // Info 4164: (31-38): Inferred type: T -// Info 4164: (39-49): Inferred type: tfun('u:type, U('u:type)) -// Info 4164: (45-48): Inferred type: 't:type -// Info 4164: (46-47): Inferred type: 't:type -// Info 4164: (51-82): Inferred type: ('x:type, 'y:type, U('ba:type)) -> () -// Info 4164: (61-79): Inferred type: ('x:type, 'y:type, U('ba:type)) -// Info 4164: (62-63): Inferred type: 'x:type -// Info 4164: (65-69): Inferred type: 'y:type -// Info 4164: (68-69): Inferred type: 'y:type -// Info 4164: (71-78): Inferred type: U('ba:type) -// Info 4164: (74-78): Inferred type: U('ba:type) -// Info 4164: (74-75): Inferred type: tfun('ba:type, U('ba:type)) -// Info 4164: (76-77): Inferred type: 'ba:type +// Info 4164: (39-49): Inferred type: tfun(?u:type, U(?u:type)) +// Info 4164: (45-48): Inferred type: ?t:type +// Info 4164: (46-47): Inferred type: ?t:type +// Info 4164: (51-82): Inferred type: (?x:type, ?y:type, U(?ba:type)) -> () +// Info 4164: (61-79): Inferred type: (?x:type, ?y:type, U(?ba:type)) +// Info 4164: (62-63): Inferred type: ?x:type +// Info 4164: (65-69): Inferred type: ?y:type +// Info 4164: (68-69): Inferred type: ?y:type +// Info 4164: (71-78): Inferred type: U(?ba:type) +// Info 4164: (74-78): Inferred type: U(?ba:type) +// Info 4164: (74-75): Inferred type: tfun(?ba:type, U(?ba:type)) +// Info 4164: (76-77): Inferred type: ?ba:type // Info 4164: (84-159): Inferred type: (T, U(T), U(U(T))) -> () // Info 4164: (96-123): Inferred type: (T, U(T), U(U(T))) // Info 4164: (97-101): Inferred type: T diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol index b597a5b25295..fa68ad308a83 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol @@ -16,32 +16,32 @@ function run() { // compileViaYul: true // ---- // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. -// Info 4164: (31-47): Inferred type: tfun(('ba:type, 'bb:type, 'bc:type), T('ba:type, 'bb:type, 'bc:type)) -// Info 4164: (37-46): Inferred type: ('x:type, 'y:type, 'z:type) -// Info 4164: (38-39): Inferred type: 'x:type -// Info 4164: (41-42): Inferred type: 'y:type -// Info 4164: (44-45): Inferred type: 'z:type +// Info 4164: (31-47): Inferred type: tfun((?ba:type, ?bb:type, ?bc:type), T(?ba:type, ?bb:type, ?bc:type)) +// Info 4164: (37-46): Inferred type: (?x:type, ?y:type, ?z:type) +// Info 4164: (38-39): Inferred type: ?x:type +// Info 4164: (41-42): Inferred type: ?y:type +// Info 4164: (44-45): Inferred type: ?z:type // Info 4164: (48-55): Inferred type: U // Info 4164: (56-63): Inferred type: V // Info 4164: (65-81): Inferred type: C -// Info 4164: (71-75): Inferred type: 'be:(type, C) +// Info 4164: (71-75): Inferred type: ?be:(type, C) // Info 4164: (82-98): Inferred type: D -// Info 4164: (88-92): Inferred type: 'bg:(type, D) +// Info 4164: (88-92): Inferred type: ?bg:(type, D) // Info 4164: (100-170): Inferred type: () -> () // Info 4164: (112-114): Inferred type: () -// Info 4164: (125-141): Inferred type: T(U, 'bm:type, 'bo:(type, C)) -// Info 4164: (128-141): Inferred type: T(U, 'bm:type, 'bo:(type, C)) -// Info 4164: (128-129): Inferred type: tfun((U, 'bm:type, 'bo:(type, C)), T(U, 'bm:type, 'bo:(type, C))) +// Info 4164: (125-141): Inferred type: T(U, ?bm:type, ?bo:(type, C)) +// Info 4164: (128-141): Inferred type: T(U, ?bm:type, ?bo:(type, C)) +// Info 4164: (128-129): Inferred type: tfun((U, ?bm:type, ?bo:(type, C)), T(U, ?bm:type, ?bo:(type, C))) // Info 4164: (130-131): Inferred type: U -// Info 4164: (133-134): Inferred type: 'bm:type -// Info 4164: (136-140): Inferred type: 'bo:(type, C) -// Info 4164: (136-137): Inferred type: 'bo:(type, C) -// Info 4164: (139-140): Inferred type: 'bo:(type, C) -// Info 4164: (151-167): Inferred type: T(V, 'bt:type, 'bv:(type, D)) -// Info 4164: (154-167): Inferred type: T(V, 'bt:type, 'bv:(type, D)) -// Info 4164: (154-155): Inferred type: tfun((V, 'bt:type, 'bv:(type, D)), T(V, 'bt:type, 'bv:(type, D))) +// Info 4164: (133-134): Inferred type: ?bm:type +// Info 4164: (136-140): Inferred type: ?bo:(type, C) +// Info 4164: (136-137): Inferred type: ?bo:(type, C) +// Info 4164: (139-140): Inferred type: ?bo:(type, C) +// Info 4164: (151-167): Inferred type: T(V, ?bt:type, ?bv:(type, D)) +// Info 4164: (154-167): Inferred type: T(V, ?bt:type, ?bv:(type, D)) +// Info 4164: (154-155): Inferred type: tfun((V, ?bt:type, ?bv:(type, D)), T(V, ?bt:type, ?bv:(type, D))) // Info 4164: (156-157): Inferred type: V -// Info 4164: (159-160): Inferred type: 'bt:type -// Info 4164: (162-166): Inferred type: 'bv:(type, D) -// Info 4164: (162-163): Inferred type: 'bv:(type, D) -// Info 4164: (165-166): Inferred type: 'bv:(type, D) +// Info 4164: (159-160): Inferred type: ?bt:type +// Info 4164: (162-166): Inferred type: ?bv:(type, D) +// Info 4164: (162-163): Inferred type: ?bv:(type, D) +// Info 4164: (165-166): Inferred type: ?bv:(type, D) 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 89f3f5f1a8b9..27631d064d18 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 @@ -24,15 +24,15 @@ function fun() { // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. // Info 4164: (31-41): Inferred type: uint // Info 4164: (42-54): Inferred type: string -// Info 4164: (56-66): Inferred type: tfun('v:type, T('v:type)) -// Info 4164: (62-65): Inferred type: 'u:type -// Info 4164: (63-64): Inferred type: 'u:type -// Info 4164: (67-84): Inferred type: tfun('x:type, U('x:type)) -// Info 4164: (73-76): Inferred type: 'w:type -// Info 4164: (74-75): Inferred type: 'w:type -// Info 4164: (79-83): Inferred type: T('w:type) -// Info 4164: (79-80): Inferred type: tfun('w:type, T('w:type)) -// Info 4164: (81-82): Inferred type: 'w:type +// Info 4164: (56-66): Inferred type: tfun(?v:type, T(?v:type)) +// Info 4164: (62-65): Inferred type: ?u:type +// Info 4164: (63-64): Inferred type: ?u:type +// Info 4164: (67-84): Inferred type: tfun(?x:type, U(?x:type)) +// Info 4164: (73-76): Inferred type: ?w:type +// Info 4164: (74-75): Inferred type: ?w:type +// Info 4164: (79-83): Inferred type: T(?w:type) +// Info 4164: (79-80): Inferred type: tfun(?w:type, T(?w:type)) +// Info 4164: (81-82): Inferred type: ?w:type // Info 4164: (86-245): Inferred type: () -> () // Info 4164: (98-100): Inferred type: () // Info 4164: (111-121): Inferred type: U(uint) @@ -43,13 +43,13 @@ function fun() { // Info 4164: (134-141): Inferred type: T(uint) // Info 4164: (134-135): Inferred type: tfun(uint, T(uint)) // Info 4164: (136-140): Inferred type: uint -// Info 4164: (147-155): Inferred type: T('bi:type) -// Info 4164: (147-152): Inferred type: U(uint) -> T('bi:type) -// Info 4164: (147-148): Inferred type: U('bg:type) +// Info 4164: (147-155): Inferred type: T(?bi:type) +// Info 4164: (147-152): Inferred type: U(uint) -> T(?bi:type) +// Info 4164: (147-148): Inferred type: U(?bg:type) // Info 4164: (153-154): Inferred type: U(uint) -// Info 4164: (161-169): Inferred type: U('bm:type) -// Info 4164: (161-166): Inferred type: T(uint) -> U('bm:type) -// Info 4164: (161-162): Inferred type: U('bk:type) +// Info 4164: (161-169): Inferred type: U(?bm:type) +// Info 4164: (161-166): Inferred type: T(uint) -> U(?bm:type) +// Info 4164: (161-162): Inferred type: U(?bk:type) // Info 4164: (167-168): Inferred type: T(uint) // Info 4164: (180-192): Inferred type: U(string) // Info 4164: (183-192): Inferred type: U(string) @@ -59,11 +59,11 @@ function fun() { // Info 4164: (205-214): Inferred type: T(string) // Info 4164: (205-206): Inferred type: tfun(string, T(string)) // Info 4164: (207-213): Inferred type: string -// Info 4164: (220-228): Inferred type: T('bu:type) -// Info 4164: (220-225): Inferred type: U(string) -> T('bu:type) -// Info 4164: (220-221): Inferred type: U('bs:type) +// Info 4164: (220-228): Inferred type: T(?bu:type) +// Info 4164: (220-225): Inferred type: U(string) -> T(?bu:type) +// Info 4164: (220-221): Inferred type: U(?bs:type) // Info 4164: (226-227): Inferred type: U(string) -// Info 4164: (234-242): Inferred type: U('by:type) -// Info 4164: (234-239): Inferred type: T(string) -> U('by:type) -// Info 4164: (234-235): Inferred type: U('bw:type) +// Info 4164: (234-242): Inferred type: U(?by:type) +// Info 4164: (234-239): Inferred type: T(string) -> U(?by:type) +// Info 4164: (234-235): Inferred type: U(?bw:type) // Info 4164: (240-241): Inferred type: T(string) 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 25ab330f3b32..3bff1f4d0ad0 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 @@ -51,28 +51,28 @@ function fun(a: T(int: P3), b: T(str: P4)) { // ---- // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. // Info 4164: (31-61): Inferred type: bool -// Info 4164: (63-73): Inferred type: tfun('z:type, T('z:type)) -// Info 4164: (69-72): Inferred type: 'y:type -// Info 4164: (70-71): Inferred type: 'y:type +// Info 4164: (63-73): Inferred type: tfun(?z:type, T(?z:type)) +// Info 4164: (69-72): Inferred type: ?y:type +// Info 4164: (70-71): Inferred type: ?y:type // Info 4164: (74-83): Inferred type: int // Info 4164: (84-93): Inferred type: str // Info 4164: (95-156): Inferred type: C -// Info 4164: (101-105): Inferred type: 'bd:(type, C) -// Info 4164: (115-154): Inferred type: ('bd:(type, C), 'bd:(type, C)) -> 'bd:(type, C) -// Info 4164: (127-145): Inferred type: ('bd:(type, C), 'bd:(type, C)) -// Info 4164: (128-135): Inferred type: 'bd:(type, C) -// Info 4164: (131-135): Inferred type: 'bd:(type, C) -// Info 4164: (137-144): Inferred type: 'bd:(type, C) -// Info 4164: (140-144): Inferred type: 'bd:(type, C) -// Info 4164: (149-153): Inferred type: 'bd:(type, C) +// Info 4164: (101-105): Inferred type: ?bd:(type, C) +// Info 4164: (115-154): Inferred type: (?bd:(type, C), ?bd:(type, C)) -> ?bd:(type, C) +// Info 4164: (127-145): Inferred type: (?bd:(type, C), ?bd:(type, C)) +// Info 4164: (128-135): Inferred type: ?bd:(type, C) +// Info 4164: (131-135): Inferred type: ?bd:(type, C) +// Info 4164: (137-144): Inferred type: ?bd:(type, C) +// Info 4164: (140-144): Inferred type: ?bd:(type, C) +// Info 4164: (149-153): Inferred type: ?bd:(type, C) // Info 4164: (158-175): Inferred type: P1 -// Info 4164: (164-168): Inferred type: 'bg:(type, P1) +// Info 4164: (164-168): Inferred type: ?bg:(type, P1) // Info 4164: (176-193): Inferred type: P2 -// Info 4164: (182-186): Inferred type: 'bj:(type, P2) +// Info 4164: (182-186): Inferred type: ?bj:(type, P2) // Info 4164: (194-211): Inferred type: P3 -// Info 4164: (200-204): Inferred type: 'bw:(type, P3) +// Info 4164: (200-204): Inferred type: ?bw:(type, P3) // Info 4164: (212-229): Inferred type: P4 -// Info 4164: (218-222): Inferred type: 'by:(type, P4) +// Info 4164: (218-222): Inferred type: ?by:(type, P4) // Info 4164: (231-255): Inferred type: void // Info 4164: (256-280): Inferred type: void // Info 4164: (281-305): Inferred type: void @@ -80,56 +80,56 @@ function fun(a: T(int: P3), b: T(str: P4)) { // Info 4164: (332-356): Inferred type: void // Info 4164: (357-381): Inferred type: void // Info 4164: (383-458): Inferred type: void -// Info 4164: (398-405): Inferred type: 'ca:(type, P1) -// Info 4164: (399-404): Inferred type: 'ca:(type, P1) -// Info 4164: (402-404): Inferred type: 'ca:(type, P1) -// Info 4164: (415-456): Inferred type: (T('ca:(type, P1)), T('ca:(type, P1))) -> T('ca:(type, P1)) -// Info 4164: (427-445): Inferred type: (T('ca:(type, P1)), T('ca:(type, P1))) -// Info 4164: (428-435): Inferred type: T('ca:(type, P1)) -// Info 4164: (431-435): Inferred type: T('ca:(type, P1)) -// Info 4164: (431-432): Inferred type: tfun('ca:(type, P1), T('ca:(type, P1))) -// Info 4164: (433-434): Inferred type: 'ca:(type, P1) -// Info 4164: (437-444): Inferred type: T('ca:(type, P1)) -// Info 4164: (440-444): Inferred type: T('ca:(type, P1)) -// Info 4164: (440-441): Inferred type: tfun('ca:(type, P1), T('ca:(type, P1))) -// Info 4164: (442-443): Inferred type: 'ca:(type, P1) -// Info 4164: (449-453): Inferred type: T('ca:(type, P1)) -// Info 4164: (449-450): Inferred type: tfun('ca:(type, P1), T('ca:(type, P1))) -// Info 4164: (451-452): Inferred type: 'ca:(type, P1) +// Info 4164: (398-405): Inferred type: ?ca:(type, P1) +// Info 4164: (399-404): Inferred type: ?ca:(type, P1) +// Info 4164: (402-404): Inferred type: ?ca:(type, P1) +// Info 4164: (415-456): Inferred type: (T(?ca:(type, P1)), T(?ca:(type, P1))) -> T(?ca:(type, P1)) +// Info 4164: (427-445): Inferred type: (T(?ca:(type, P1)), T(?ca:(type, P1))) +// Info 4164: (428-435): Inferred type: T(?ca:(type, P1)) +// Info 4164: (431-435): Inferred type: T(?ca:(type, P1)) +// Info 4164: (431-432): Inferred type: tfun(?ca:(type, P1), T(?ca:(type, P1))) +// Info 4164: (433-434): Inferred type: ?ca:(type, P1) +// Info 4164: (437-444): Inferred type: T(?ca:(type, P1)) +// Info 4164: (440-444): Inferred type: T(?ca:(type, P1)) +// Info 4164: (440-441): Inferred type: tfun(?ca:(type, P1), T(?ca:(type, P1))) +// Info 4164: (442-443): Inferred type: ?ca:(type, P1) +// Info 4164: (449-453): Inferred type: T(?ca:(type, P1)) +// Info 4164: (449-450): Inferred type: tfun(?ca:(type, P1), T(?ca:(type, P1))) +// Info 4164: (451-452): Inferred type: ?ca:(type, P1) // Info 4164: (460-535): Inferred type: void -// Info 4164: (475-482): Inferred type: 'ck:(type, P2) -// Info 4164: (476-481): Inferred type: 'ck:(type, P2) -// Info 4164: (479-481): Inferred type: 'ck:(type, P2) -// Info 4164: (493-533): Inferred type: (T('ck:(type, P2)), T('ck:(type, P2))) -> bool -// Info 4164: (504-522): Inferred type: (T('ck:(type, P2)), T('ck:(type, P2))) -// Info 4164: (505-512): Inferred type: T('ck:(type, P2)) -// Info 4164: (508-512): Inferred type: T('ck:(type, P2)) -// Info 4164: (508-509): Inferred type: tfun('ck:(type, P2), T('ck:(type, P2))) -// Info 4164: (510-511): Inferred type: 'ck:(type, P2) -// Info 4164: (514-521): Inferred type: T('ck:(type, P2)) -// Info 4164: (517-521): Inferred type: T('ck:(type, P2)) -// Info 4164: (517-518): Inferred type: tfun('ck:(type, P2), T('ck:(type, P2))) -// Info 4164: (519-520): Inferred type: 'ck:(type, P2) +// Info 4164: (475-482): Inferred type: ?ck:(type, P2) +// Info 4164: (476-481): Inferred type: ?ck:(type, P2) +// Info 4164: (479-481): Inferred type: ?ck:(type, P2) +// Info 4164: (493-533): Inferred type: (T(?ck:(type, P2)), T(?ck:(type, P2))) -> bool +// Info 4164: (504-522): Inferred type: (T(?ck:(type, P2)), T(?ck:(type, P2))) +// Info 4164: (505-512): Inferred type: T(?ck:(type, P2)) +// Info 4164: (508-512): Inferred type: T(?ck:(type, P2)) +// Info 4164: (508-509): Inferred type: tfun(?ck:(type, P2), T(?ck:(type, P2))) +// Info 4164: (510-511): Inferred type: ?ck:(type, P2) +// Info 4164: (514-521): Inferred type: T(?ck:(type, P2)) +// Info 4164: (517-521): Inferred type: T(?ck:(type, P2)) +// Info 4164: (517-518): Inferred type: tfun(?ck:(type, P2), T(?ck:(type, P2))) +// Info 4164: (519-520): Inferred type: ?ck:(type, P2) // Info 4164: (526-530): Inferred type: bool // Info 4164: (537-618): Inferred type: void -// Info 4164: (552-565): Inferred type: 'bm:(type, P1, P2) -// Info 4164: (553-564): Inferred type: 'bm:(type, P1, P2) -// Info 4164: (556-564): Inferred type: 'bm:(type, P1, P2) -// Info 4164: (557-559): Inferred type: 'bm:(type, P1, P2) -// Info 4164: (561-563): Inferred type: 'bm:(type, P1, P2) -// Info 4164: (575-616): Inferred type: (T('bm:(type, P1, P2)), T('bm:(type, P1, P2))) -> T('bm:(type, P1, P2)) -// Info 4164: (587-605): Inferred type: (T('bm:(type, P1, P2)), T('bm:(type, P1, P2))) -// Info 4164: (588-595): Inferred type: T('bm:(type, P1, P2)) -// Info 4164: (591-595): Inferred type: T('bm:(type, P1, P2)) -// Info 4164: (591-592): Inferred type: tfun('bm:(type, P1, P2), T('bm:(type, P1, P2))) -// Info 4164: (593-594): Inferred type: 'bm:(type, P1, P2) -// Info 4164: (597-604): Inferred type: T('bm:(type, P1, P2)) -// Info 4164: (600-604): Inferred type: T('bm:(type, P1, P2)) -// Info 4164: (600-601): Inferred type: tfun('bm:(type, P1, P2), T('bm:(type, P1, P2))) -// Info 4164: (602-603): Inferred type: 'bm:(type, P1, P2) -// Info 4164: (609-613): Inferred type: T('bm:(type, P1, P2)) -// Info 4164: (609-610): Inferred type: tfun('bm:(type, P1, P2), T('bm:(type, P1, P2))) -// Info 4164: (611-612): Inferred type: 'bm:(type, P1, P2) +// Info 4164: (552-565): Inferred type: ?bm:(type, P1, P2) +// Info 4164: (553-564): Inferred type: ?bm:(type, P1, P2) +// Info 4164: (556-564): Inferred type: ?bm:(type, P1, P2) +// Info 4164: (557-559): Inferred type: ?bm:(type, P1, P2) +// Info 4164: (561-563): Inferred type: ?bm:(type, P1, P2) +// Info 4164: (575-616): Inferred type: (T(?bm:(type, P1, P2)), T(?bm:(type, P1, P2))) -> T(?bm:(type, P1, P2)) +// Info 4164: (587-605): Inferred type: (T(?bm:(type, P1, P2)), T(?bm:(type, P1, P2))) +// Info 4164: (588-595): Inferred type: T(?bm:(type, P1, P2)) +// Info 4164: (591-595): Inferred type: T(?bm:(type, P1, P2)) +// Info 4164: (591-592): Inferred type: tfun(?bm:(type, P1, P2), T(?bm:(type, P1, P2))) +// Info 4164: (593-594): Inferred type: ?bm:(type, P1, P2) +// Info 4164: (597-604): Inferred type: T(?bm:(type, P1, P2)) +// Info 4164: (600-604): Inferred type: T(?bm:(type, P1, P2)) +// Info 4164: (600-601): Inferred type: tfun(?bm:(type, P1, P2), T(?bm:(type, P1, P2))) +// Info 4164: (602-603): Inferred type: ?bm:(type, P1, P2) +// Info 4164: (609-613): Inferred type: T(?bm:(type, P1, P2)) +// Info 4164: (609-610): Inferred type: tfun(?bm:(type, P1, P2), T(?bm:(type, P1, P2))) +// Info 4164: (611-612): Inferred type: ?bm:(type, P1, P2) // Info 4164: (620-748): Inferred type: (T(int), T(str)) -> () // Info 4164: (632-662): Inferred type: (T(int), T(str)) // Info 4164: (633-646): Inferred type: T(int) From fce70ef0e34b7e6cc8d22b0fc08784758503e8d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 30 Oct 2023 11:09:04 +0100 Subject: [PATCH 086/189] Fixing type variables in defined type classes and functions --- .../experimental/analysis/TypeInference.cpp | 34 +++-- .../experimental/analysis/TypeInference.h | 1 + libsolidity/experimental/ast/TypeSystem.cpp | 2 + ...member_type_does_not_match_declaration.sol | 18 +++ .../inference/polymorphic_function_call.sol | 18 +-- .../inference/polymorphic_type.sol | 32 ++--- ...rphic_type_instantiation_and_operators.sol | 118 +++++++++--------- 7 files changed, 130 insertions(+), 93 deletions(-) create mode 100644 test/libsolidity/syntaxTests/experimental/inference/instantiation_member_type_does_not_match_declaration.sol diff --git a/libsolidity/experimental/analysis/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp index 9ec7c9786724..cb2de35d331c 100644 --- a/libsolidity/experimental/analysis/TypeInference.cpp +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -166,6 +166,13 @@ bool TypeInference::visit(FunctionDefinition const& _functionDefinition) return false; } +void TypeInference::endVisit(FunctionDefinition const& _functionDefinition) +{ + solAssert(m_expressionContext == ExpressionContext::Term); + + m_env->fixTypeVars(TypeEnvironmentHelpers{*m_env}.typeVars(type(_functionDefinition))); +} + void TypeInference::endVisit(Return const& _return) { solAssert(m_currentFunctionType); @@ -204,6 +211,8 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) solAssert(m_analysis.annotation(_typeClassDefinition).typeClass.has_value()); TypeClass typeClass = m_analysis.annotation(_typeClassDefinition).typeClass.value(); Type typeVar = m_typeSystem.typeClassVariable(typeClass); + unify(type(_typeClassDefinition.typeVariable()), typeVar, _typeClassDefinition.location()); + auto& typeMembersAnnotation = annotation().members[typeConstructor(&_typeClassDefinition)]; for (auto subNode: _typeClassDefinition.subNodes()) @@ -235,7 +244,6 @@ bool TypeInference::visit(TypeClassDefinition const& _typeClassDefinition) m_errorReporter.typeError(1807_error, _typeClassDefinition.location(), "Function " + functionName + " depends on invalid type variable."); } - unify(type(_typeClassDefinition.typeVariable()), m_typeSystem.freshTypeVariable({{typeClass}}), _typeClassDefinition.location()); for (auto instantiation: m_analysis.annotation(_typeClassDefinition).instantiations | ranges::views::values) // TODO: recursion-safety? Order of instantiation? instantiation->accept(*this); @@ -663,6 +671,7 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) }) | ranges::to>; } } + m_env->fixTypeVars(arguments); Type instanceType{TypeConstant{*typeConstructor, arguments}}; @@ -682,12 +691,8 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) auto const& classFunctions = annotation().typeClassFunctions.at(typeClass); - TypeEnvironment newEnv = m_env->clone(); - if (!newEnv.unify(m_typeSystem.typeClassVariable(typeClass), instanceType).empty()) - { - m_errorReporter.typeError(4686_error, _typeClassInstantiation.location(), "Unification of class and instance variable failed."); - return false; - } + solAssert(std::holds_alternative(m_typeSystem.typeClassVariable(typeClass))); + TypeVariable classVar = std::get(m_typeSystem.typeClassVariable(typeClass)); for (auto [name, classFunctionType]: classFunctions) { @@ -696,11 +701,22 @@ bool TypeInference::visit(TypeClassInstantiation const& _typeClassInstantiation) m_errorReporter.typeError(6948_error, _typeClassInstantiation.location(), "Missing function: " + name); continue; } + Type instantiatedClassFunctionType = TypeEnvironmentHelpers{*m_env}.substitute(classFunctionType, classVar, instanceType); + Type instanceFunctionType = functionTypes.at(name); functionTypes.erase(name); - if (!newEnv.typeEquals(instanceFunctionType, classFunctionType)) - m_errorReporter.typeError(7428_error, _typeClassInstantiation.location(), "Type mismatch for function " + name + " " + TypeEnvironmentHelpers{newEnv}.typeToString(instanceFunctionType) + " != " + TypeEnvironmentHelpers{newEnv}.typeToString(classFunctionType)); + if (!m_env->typeEquals(instanceFunctionType, instantiatedClassFunctionType)) + m_errorReporter.typeError( + 7428_error, + _typeClassInstantiation.location(), + fmt::format( + "Instantiation function '{}' does not match the declaration in the type class ({} != {}).", + name, + TypeEnvironmentHelpers{*m_env}.typeToString(instanceFunctionType), + TypeEnvironmentHelpers{*m_env}.typeToString(instantiatedClassFunctionType) + ) + ); } if (!functionTypes.empty()) diff --git a/libsolidity/experimental/analysis/TypeInference.h b/libsolidity/experimental/analysis/TypeInference.h index df9d0610a9a5..3eb2ad94cf71 100644 --- a/libsolidity/experimental/analysis/TypeInference.h +++ b/libsolidity/experimental/analysis/TypeInference.h @@ -57,6 +57,7 @@ class TypeInference: public ASTConstVisitor bool visit(VariableDeclaration const& _variableDeclaration) override; bool visit(FunctionDefinition const& _functionDefinition) override; + void endVisit(FunctionDefinition const& _functionDefinition) override; bool visit(ParameterList const&) override { return true; } void endVisit(ParameterList const& _parameterList) override; bool visit(SourceUnit const&) override { return true; } diff --git a/libsolidity/experimental/ast/TypeSystem.cpp b/libsolidity/experimental/ast/TypeSystem.cpp index fac5073bb0ca..4ddb2bf7c86c 100644 --- a/libsolidity/experimental/ast/TypeSystem.cpp +++ b/libsolidity/experimental/ast/TypeSystem.cpp @@ -301,6 +301,8 @@ std::variant TypeSystem::declareTypeClass(std::string _n Type typeVariable = (_primitive ? freshVariable({{typeClass}}) : freshTypeVariable({{typeClass}})); solAssert(std::holds_alternative(typeVariable)); + m_globalTypeEnvironment.fixTypeVars({typeVariable}); + m_typeClasses.emplace_back(TypeClassInfo{ typeVariable, _name, 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 new file mode 100644 index 000000000000..1c845b8eeece --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/instantiation_member_type_does_not_match_declaration.sol @@ -0,0 +1,18 @@ +pragma experimental solidity; + +type T; +type U; + +class Self: C { + function f(self: Self); +} + +instantiation T: C { + function f(self: U) {} +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 7428: (95-144): Instantiation function 'f' does not match the declaration in the type class (U -> () != T -> ()). diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol index 487aea090581..f85f7fd4121e 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol @@ -18,15 +18,15 @@ function run(a: T, b: U(T), c: U(U(T))) { // Info 4164: (39-49): Inferred type: tfun(?u:type, U(?u:type)) // Info 4164: (45-48): Inferred type: ?t:type // Info 4164: (46-47): Inferred type: ?t:type -// Info 4164: (51-82): Inferred type: (?x:type, ?y:type, U(?ba:type)) -> () -// Info 4164: (61-79): Inferred type: (?x:type, ?y:type, U(?ba:type)) -// Info 4164: (62-63): Inferred type: ?x:type -// Info 4164: (65-69): Inferred type: ?y:type -// Info 4164: (68-69): Inferred type: ?y:type -// Info 4164: (71-78): Inferred type: U(?ba:type) -// Info 4164: (74-78): Inferred type: U(?ba:type) -// Info 4164: (74-75): Inferred type: tfun(?ba:type, U(?ba:type)) -// Info 4164: (76-77): Inferred type: ?ba:type +// Info 4164: (51-82): Inferred type: ('x:type, 'y:type, U('ba:type)) -> () +// Info 4164: (61-79): Inferred type: ('x:type, 'y:type, U('ba:type)) +// Info 4164: (62-63): Inferred type: 'x:type +// Info 4164: (65-69): Inferred type: 'y:type +// Info 4164: (68-69): Inferred type: 'y:type +// Info 4164: (71-78): Inferred type: U('ba:type) +// Info 4164: (74-78): Inferred type: U('ba:type) +// Info 4164: (74-75): Inferred type: tfun('ba:type, U('ba:type)) +// Info 4164: (76-77): Inferred type: 'ba:type // Info 4164: (84-159): Inferred type: (T, U(T), U(U(T))) -> () // Info 4164: (96-123): Inferred type: (T, U(T), U(U(T))) // Info 4164: (97-101): Inferred type: T diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol index fa68ad308a83..5aa190ecef5c 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol @@ -24,24 +24,24 @@ function run() { // Info 4164: (48-55): Inferred type: U // Info 4164: (56-63): Inferred type: V // Info 4164: (65-81): Inferred type: C -// Info 4164: (71-75): Inferred type: ?be:(type, C) +// Info 4164: (71-75): Inferred type: 'k:(type, C) // Info 4164: (82-98): Inferred type: D -// Info 4164: (88-92): Inferred type: ?bg:(type, D) +// Info 4164: (88-92): Inferred type: 'l:(type, D) // Info 4164: (100-170): Inferred type: () -> () // Info 4164: (112-114): Inferred type: () -// Info 4164: (125-141): Inferred type: T(U, ?bm:type, ?bo:(type, C)) -// Info 4164: (128-141): Inferred type: T(U, ?bm:type, ?bo:(type, C)) -// Info 4164: (128-129): Inferred type: tfun((U, ?bm:type, ?bo:(type, C)), T(U, ?bm:type, ?bo:(type, C))) +// Info 4164: (125-141): Inferred type: T(U, ?bk:type, ?bm:(type, C)) +// Info 4164: (128-141): Inferred type: T(U, ?bk:type, ?bm:(type, C)) +// Info 4164: (128-129): Inferred type: tfun((U, ?bk:type, ?bm:(type, C)), T(U, ?bk:type, ?bm:(type, C))) // Info 4164: (130-131): Inferred type: U -// Info 4164: (133-134): Inferred type: ?bm:type -// Info 4164: (136-140): Inferred type: ?bo:(type, C) -// Info 4164: (136-137): Inferred type: ?bo:(type, C) -// Info 4164: (139-140): Inferred type: ?bo:(type, C) -// Info 4164: (151-167): Inferred type: T(V, ?bt:type, ?bv:(type, D)) -// Info 4164: (154-167): Inferred type: T(V, ?bt:type, ?bv:(type, D)) -// Info 4164: (154-155): Inferred type: tfun((V, ?bt:type, ?bv:(type, D)), T(V, ?bt:type, ?bv:(type, D))) +// Info 4164: (133-134): Inferred type: ?bk:type +// Info 4164: (136-140): Inferred type: ?bm:(type, C) +// Info 4164: (136-137): Inferred type: ?bm:(type, C) +// Info 4164: (139-140): Inferred type: ?bm:(type, C) +// Info 4164: (151-167): Inferred type: T(V, ?br:type, ?bt:(type, D)) +// Info 4164: (154-167): Inferred type: T(V, ?br:type, ?bt:(type, D)) +// Info 4164: (154-155): Inferred type: tfun((V, ?br:type, ?bt:(type, D)), T(V, ?br:type, ?bt:(type, D))) // Info 4164: (156-157): Inferred type: V -// Info 4164: (159-160): Inferred type: ?bt:type -// Info 4164: (162-166): Inferred type: ?bv:(type, D) -// Info 4164: (162-163): Inferred type: ?bv:(type, D) -// Info 4164: (165-166): Inferred type: ?bv:(type, D) +// Info 4164: (159-160): Inferred type: ?br:type +// Info 4164: (162-166): Inferred type: ?bt:(type, D) +// Info 4164: (162-163): Inferred type: ?bt:(type, D) +// Info 4164: (165-166): Inferred type: ?bt:(type, D) 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 3bff1f4d0ad0..22c4796e0fcd 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 @@ -57,22 +57,22 @@ function fun(a: T(int: P3), b: T(str: P4)) { // Info 4164: (74-83): Inferred type: int // Info 4164: (84-93): Inferred type: str // Info 4164: (95-156): Inferred type: C -// Info 4164: (101-105): Inferred type: ?bd:(type, C) -// Info 4164: (115-154): Inferred type: (?bd:(type, C), ?bd:(type, C)) -> ?bd:(type, C) -// Info 4164: (127-145): Inferred type: (?bd:(type, C), ?bd:(type, C)) -// Info 4164: (128-135): Inferred type: ?bd:(type, C) -// Info 4164: (131-135): Inferred type: ?bd:(type, C) -// Info 4164: (137-144): Inferred type: ?bd:(type, C) -// Info 4164: (140-144): Inferred type: ?bd:(type, C) -// Info 4164: (149-153): Inferred type: ?bd:(type, C) +// Info 4164: (101-105): Inferred type: 'k:(type, C) +// Info 4164: (115-154): Inferred type: ('k:(type, C), 'k:(type, C)) -> 'k:(type, C) +// Info 4164: (127-145): Inferred type: ('k:(type, C), 'k:(type, C)) +// Info 4164: (128-135): Inferred type: 'k:(type, C) +// Info 4164: (131-135): Inferred type: 'k:(type, C) +// Info 4164: (137-144): Inferred type: 'k:(type, C) +// Info 4164: (140-144): Inferred type: 'k:(type, C) +// Info 4164: (149-153): Inferred type: 'k:(type, C) // Info 4164: (158-175): Inferred type: P1 -// Info 4164: (164-168): Inferred type: ?bg:(type, P1) +// Info 4164: (164-168): Inferred type: 'l:(type, P1) // Info 4164: (176-193): Inferred type: P2 -// Info 4164: (182-186): Inferred type: ?bj:(type, P2) +// Info 4164: (182-186): Inferred type: 'm:(type, P2) // Info 4164: (194-211): Inferred type: P3 -// Info 4164: (200-204): Inferred type: ?bw:(type, P3) +// Info 4164: (200-204): Inferred type: 'n:(type, P3) // Info 4164: (212-229): Inferred type: P4 -// Info 4164: (218-222): Inferred type: ?by:(type, P4) +// Info 4164: (218-222): Inferred type: 'o:(type, P4) // Info 4164: (231-255): Inferred type: void // Info 4164: (256-280): Inferred type: void // Info 4164: (281-305): Inferred type: void @@ -80,56 +80,56 @@ function fun(a: T(int: P3), b: T(str: P4)) { // Info 4164: (332-356): Inferred type: void // Info 4164: (357-381): Inferred type: void // Info 4164: (383-458): Inferred type: void -// Info 4164: (398-405): Inferred type: ?ca:(type, P1) -// Info 4164: (399-404): Inferred type: ?ca:(type, P1) -// Info 4164: (402-404): Inferred type: ?ca:(type, P1) -// Info 4164: (415-456): Inferred type: (T(?ca:(type, P1)), T(?ca:(type, P1))) -> T(?ca:(type, P1)) -// Info 4164: (427-445): Inferred type: (T(?ca:(type, P1)), T(?ca:(type, P1))) -// Info 4164: (428-435): Inferred type: T(?ca:(type, P1)) -// Info 4164: (431-435): Inferred type: T(?ca:(type, P1)) -// Info 4164: (431-432): Inferred type: tfun(?ca:(type, P1), T(?ca:(type, P1))) -// Info 4164: (433-434): Inferred type: ?ca:(type, P1) -// Info 4164: (437-444): Inferred type: T(?ca:(type, P1)) -// Info 4164: (440-444): Inferred type: T(?ca:(type, P1)) -// Info 4164: (440-441): Inferred type: tfun(?ca:(type, P1), T(?ca:(type, P1))) -// Info 4164: (442-443): Inferred type: ?ca:(type, P1) -// Info 4164: (449-453): Inferred type: T(?ca:(type, P1)) -// Info 4164: (449-450): Inferred type: tfun(?ca:(type, P1), T(?ca:(type, P1))) -// Info 4164: (451-452): Inferred type: ?ca:(type, P1) +// Info 4164: (398-405): Inferred type: 'bv:(type, P1) +// Info 4164: (399-404): Inferred type: 'bv:(type, P1) +// Info 4164: (402-404): Inferred type: 'bv:(type, P1) +// Info 4164: (415-456): Inferred type: (T('bv:(type, P1)), T('bv:(type, P1))) -> T('bv:(type, P1)) +// Info 4164: (427-445): Inferred type: (T('bv:(type, P1)), T('bv:(type, P1))) +// Info 4164: (428-435): Inferred type: T('bv:(type, P1)) +// Info 4164: (431-435): Inferred type: T('bv:(type, P1)) +// Info 4164: (431-432): Inferred type: tfun('bv:(type, P1), T('bv:(type, P1))) +// Info 4164: (433-434): Inferred type: 'bv:(type, P1) +// Info 4164: (437-444): Inferred type: T('bv:(type, P1)) +// Info 4164: (440-444): Inferred type: T('bv:(type, P1)) +// Info 4164: (440-441): Inferred type: tfun('bv:(type, P1), T('bv:(type, P1))) +// Info 4164: (442-443): Inferred type: 'bv:(type, P1) +// Info 4164: (449-453): Inferred type: T('bv:(type, P1)) +// Info 4164: (449-450): Inferred type: tfun('bv:(type, P1), T('bv:(type, P1))) +// Info 4164: (451-452): Inferred type: 'bv:(type, P1) // Info 4164: (460-535): Inferred type: void -// Info 4164: (475-482): Inferred type: ?ck:(type, P2) -// Info 4164: (476-481): Inferred type: ?ck:(type, P2) -// Info 4164: (479-481): Inferred type: ?ck:(type, P2) -// Info 4164: (493-533): Inferred type: (T(?ck:(type, P2)), T(?ck:(type, P2))) -> bool -// Info 4164: (504-522): Inferred type: (T(?ck:(type, P2)), T(?ck:(type, P2))) -// Info 4164: (505-512): Inferred type: T(?ck:(type, P2)) -// Info 4164: (508-512): Inferred type: T(?ck:(type, P2)) -// Info 4164: (508-509): Inferred type: tfun(?ck:(type, P2), T(?ck:(type, P2))) -// Info 4164: (510-511): Inferred type: ?ck:(type, P2) -// Info 4164: (514-521): Inferred type: T(?ck:(type, P2)) -// Info 4164: (517-521): Inferred type: T(?ck:(type, P2)) -// Info 4164: (517-518): Inferred type: tfun(?ck:(type, P2), T(?ck:(type, P2))) -// Info 4164: (519-520): Inferred type: ?ck:(type, P2) +// Info 4164: (475-482): Inferred type: 'cf:(type, P2) +// Info 4164: (476-481): Inferred type: 'cf:(type, P2) +// Info 4164: (479-481): Inferred type: 'cf:(type, P2) +// Info 4164: (493-533): Inferred type: (T('cf:(type, P2)), T('cf:(type, P2))) -> bool +// Info 4164: (504-522): Inferred type: (T('cf:(type, P2)), T('cf:(type, P2))) +// Info 4164: (505-512): Inferred type: T('cf:(type, P2)) +// Info 4164: (508-512): Inferred type: T('cf:(type, P2)) +// Info 4164: (508-509): Inferred type: tfun('cf:(type, P2), T('cf:(type, P2))) +// Info 4164: (510-511): Inferred type: 'cf:(type, P2) +// Info 4164: (514-521): Inferred type: T('cf:(type, P2)) +// Info 4164: (517-521): Inferred type: T('cf:(type, P2)) +// Info 4164: (517-518): Inferred type: tfun('cf:(type, P2), T('cf:(type, P2))) +// Info 4164: (519-520): Inferred type: 'cf:(type, P2) // Info 4164: (526-530): Inferred type: bool // Info 4164: (537-618): Inferred type: void -// Info 4164: (552-565): Inferred type: ?bm:(type, P1, P2) -// Info 4164: (553-564): Inferred type: ?bm:(type, P1, P2) -// Info 4164: (556-564): Inferred type: ?bm:(type, P1, P2) -// Info 4164: (557-559): Inferred type: ?bm:(type, P1, P2) -// Info 4164: (561-563): Inferred type: ?bm:(type, P1, P2) -// Info 4164: (575-616): Inferred type: (T(?bm:(type, P1, P2)), T(?bm:(type, P1, P2))) -> T(?bm:(type, P1, P2)) -// Info 4164: (587-605): Inferred type: (T(?bm:(type, P1, P2)), T(?bm:(type, P1, P2))) -// Info 4164: (588-595): Inferred type: T(?bm:(type, P1, P2)) -// Info 4164: (591-595): Inferred type: T(?bm:(type, P1, P2)) -// Info 4164: (591-592): Inferred type: tfun(?bm:(type, P1, P2), T(?bm:(type, P1, P2))) -// Info 4164: (593-594): Inferred type: ?bm:(type, P1, P2) -// Info 4164: (597-604): Inferred type: T(?bm:(type, P1, P2)) -// Info 4164: (600-604): Inferred type: T(?bm:(type, P1, P2)) -// Info 4164: (600-601): Inferred type: tfun(?bm:(type, P1, P2), T(?bm:(type, P1, P2))) -// Info 4164: (602-603): Inferred type: ?bm:(type, P1, P2) -// Info 4164: (609-613): Inferred type: T(?bm:(type, P1, P2)) -// Info 4164: (609-610): Inferred type: tfun(?bm:(type, P1, P2), T(?bm:(type, P1, P2))) -// Info 4164: (611-612): Inferred type: ?bm:(type, P1, P2) +// Info 4164: (552-565): Inferred type: 'bj:(type, P1, P2) +// Info 4164: (553-564): Inferred type: 'bj:(type, P1, P2) +// Info 4164: (556-564): Inferred type: 'bj:(type, P1, P2) +// Info 4164: (557-559): Inferred type: 'bj:(type, P1, P2) +// Info 4164: (561-563): Inferred type: 'bj:(type, P1, P2) +// Info 4164: (575-616): Inferred type: (T('bj:(type, P1, P2)), T('bj:(type, P1, P2))) -> T('bj:(type, P1, P2)) +// Info 4164: (587-605): Inferred type: (T('bj:(type, P1, P2)), T('bj:(type, P1, P2))) +// Info 4164: (588-595): Inferred type: T('bj:(type, P1, P2)) +// Info 4164: (591-595): Inferred type: T('bj:(type, P1, P2)) +// Info 4164: (591-592): Inferred type: tfun('bj:(type, P1, P2), T('bj:(type, P1, P2))) +// Info 4164: (593-594): Inferred type: 'bj:(type, P1, P2) +// Info 4164: (597-604): Inferred type: T('bj:(type, P1, P2)) +// Info 4164: (600-604): Inferred type: T('bj:(type, P1, P2)) +// Info 4164: (600-601): Inferred type: tfun('bj:(type, P1, P2), T('bj:(type, P1, P2))) +// Info 4164: (602-603): Inferred type: 'bj:(type, P1, P2) +// Info 4164: (609-613): Inferred type: T('bj:(type, P1, P2)) +// Info 4164: (609-610): Inferred type: tfun('bj:(type, P1, P2), T('bj:(type, P1, P2))) +// Info 4164: (611-612): Inferred type: 'bj:(type, P1, P2) // Info 4164: (620-748): Inferred type: (T(int), T(str)) -> () // Info 4164: (632-662): Inferred type: (T(int), T(str)) // Info 4164: (633-646): Inferred type: T(int) From 8e210d2f8aa17a71b9306a232db7f4685b99bd13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 7 Nov 2023 17:27:06 +0100 Subject: [PATCH 087/189] Fixing type variables in type definitions --- .../experimental/analysis/TypeInference.cpp | 17 ++- libsolidity/experimental/ast/TypeSystem.cpp | 1 + .../builtin/builtin_type_definition.sol | 20 ++-- .../inference/polymorphic_function_call.sol | 24 ++--- .../inference/polymorphic_type.sol | 38 +++---- .../polymorphic_type_abs_and_rep.sol | 42 ++++---- ...rphic_type_instantiation_and_operators.sol | 100 +++++++++--------- 7 files changed, 128 insertions(+), 114 deletions(-) diff --git a/libsolidity/experimental/analysis/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp index cb2de35d331c..63a377f90d56 100644 --- a/libsolidity/experimental/analysis/TypeInference.cpp +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -33,6 +33,7 @@ #include #include + #include using namespace solidity; @@ -776,12 +777,21 @@ bool TypeInference::visit(TypeDefinition const& _typeDefinition) return false; if (_typeDefinition.arguments()) + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; _typeDefinition.arguments()->accept(*this); + } std::vector arguments; if (_typeDefinition.arguments()) - for (size_t i = 0; i < _typeDefinition.arguments()->parameters().size(); ++i) - arguments.emplace_back(m_typeSystem.freshTypeVariable({})); + for (ASTPointer argumentDeclaration: _typeDefinition.arguments()->parameters()) + { + solAssert(argumentDeclaration); + Type typeVar = type(*argumentDeclaration); + solAssert(std::holds_alternative(typeVar)); + arguments.emplace_back(typeVar); + } + m_env->fixTypeVars(arguments); Type definedType = type(&_typeDefinition, arguments); if (arguments.empty()) @@ -807,6 +817,9 @@ bool TypeInference::visit(TypeDefinition const& _typeDefinition) solAssert(newlyInserted, fmt::format("Members of type '{}' are already defined.", m_typeSystem.constructorInfo(constructor).name)); if (underlyingType) { + // Undeclared type variables are not allowed in type definitions and we fixed all the declared ones. + solAssert(!TypeEnvironmentHelpers{*m_env}.hasGenericTypeVars(*underlyingType)); + members->second.emplace("abs", TypeMember{helper.functionType(*underlyingType, definedType)}); members->second.emplace("rep", TypeMember{helper.functionType(definedType, *underlyingType)}); } diff --git a/libsolidity/experimental/ast/TypeSystem.cpp b/libsolidity/experimental/ast/TypeSystem.cpp index 4ddb2bf7c86c..059054dcdbce 100644 --- a/libsolidity/experimental/ast/TypeSystem.cpp +++ b/libsolidity/experimental/ast/TypeSystem.cpp @@ -283,6 +283,7 @@ TypeConstructor TypeSystem::declareTypeConstructor(std::string _name, std::strin std::generate_n(std::back_inserter(argumentSorts), _arguments, [&](){ return Sort{{primitiveClass(PrimitiveClass::Type)}}; }); std::vector argumentTypes; std::generate_n(std::back_inserter(argumentTypes), _arguments, [&](){ return freshVariable({}); }); + m_globalTypeEnvironment.fixTypeVars(argumentTypes); auto error = instantiateClass(type(constructor, argumentTypes), Arity{argumentSorts, primitiveClass(PrimitiveClass::Type)}); solAssert(!error, *error); } diff --git a/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition.sol b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition.sol index fb76e1948b87..373d76c339ff 100644 --- a/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition.sol +++ b/test/libsolidity/syntaxTests/experimental/builtin/builtin_type_definition.sol @@ -40,14 +40,14 @@ contract C { // Info 4164: (94-124): Inferred type: word // Info 4164: (125-161): Inferred type: integer // Info 4164: (162-192): Inferred type: () -// Info 4164: (194-228): Inferred type: tfun((?u:type, ?v:type), ?u:type -> ?v:type) -// Info 4164: (202-208): Inferred type: (?s:type, ?t:type) -// Info 4164: (203-204): Inferred type: ?s:type -// Info 4164: (206-207): Inferred type: ?t:type -// Info 4164: (229-265): Inferred type: tfun((?y:type, ?z:type), (?y:type, ?z:type)) -// Info 4164: (238-244): Inferred type: (?w:type, ?x:type) -// Info 4164: (239-240): Inferred type: ?w:type -// Info 4164: (242-243): Inferred type: ?x:type +// Info 4164: (194-228): Inferred type: tfun(('s:type, 't:type), 's:type -> 't:type) +// Info 4164: (202-208): Inferred type: ('s:type, 't:type) +// Info 4164: (203-204): Inferred type: 's:type +// Info 4164: (206-207): Inferred type: 't:type +// Info 4164: (229-265): Inferred type: tfun(('u:type, 'v:type), ('u:type, 'v:type)) +// Info 4164: (238-244): Inferred type: ('u:type, 'v:type) +// Info 4164: (239-240): Inferred type: 'u:type +// Info 4164: (242-243): Inferred type: 'v:type // Info 4164: (284-584): Inferred type: () -> () // Info 4164: (292-294): Inferred type: () // Info 4164: (318-325): Inferred type: void @@ -84,9 +84,9 @@ contract C { // Info 4164: (525-529): Inferred type: word // Info 4164: (540-553): Inferred type: bool // Info 4164: (540-550): Inferred type: (bool, word) -> bool -// Info 4164: (540-544): Inferred type: (?bl:type, ?bm:type) +// Info 4164: (540-544): Inferred type: (?bh:type, ?bi:type) // Info 4164: (551-552): Inferred type: (bool, word) // Info 4164: (563-577): Inferred type: word // Info 4164: (563-574): Inferred type: (bool, word) -> word -// Info 4164: (563-567): Inferred type: (?bq:type, ?br:type) +// Info 4164: (563-567): Inferred type: (?bm:type, ?bn:type) // Info 4164: (575-576): Inferred type: (bool, word) diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol index f85f7fd4121e..c73a857a2fcb 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol @@ -15,18 +15,18 @@ function run(a: T, b: U(T), c: U(U(T))) { // ---- // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. // Info 4164: (31-38): Inferred type: T -// Info 4164: (39-49): Inferred type: tfun(?u:type, U(?u:type)) -// Info 4164: (45-48): Inferred type: ?t:type -// Info 4164: (46-47): Inferred type: ?t:type -// Info 4164: (51-82): Inferred type: ('x:type, 'y:type, U('ba:type)) -> () -// Info 4164: (61-79): Inferred type: ('x:type, 'y:type, U('ba:type)) -// Info 4164: (62-63): Inferred type: 'x:type -// Info 4164: (65-69): Inferred type: 'y:type -// Info 4164: (68-69): Inferred type: 'y:type -// Info 4164: (71-78): Inferred type: U('ba:type) -// Info 4164: (74-78): Inferred type: U('ba:type) -// Info 4164: (74-75): Inferred type: tfun('ba:type, U('ba:type)) -// Info 4164: (76-77): Inferred type: 'ba:type +// Info 4164: (39-49): Inferred type: tfun('t:type, U('t:type)) +// Info 4164: (45-48): Inferred type: 't:type +// Info 4164: (46-47): Inferred type: 't:type +// Info 4164: (51-82): Inferred type: ('w:type, 'x:type, U('z:type)) -> () +// Info 4164: (61-79): Inferred type: ('w:type, 'x:type, U('z:type)) +// Info 4164: (62-63): Inferred type: 'w:type +// Info 4164: (65-69): Inferred type: 'x:type +// Info 4164: (68-69): Inferred type: 'x:type +// Info 4164: (71-78): Inferred type: U('z:type) +// Info 4164: (74-78): Inferred type: U('z:type) +// Info 4164: (74-75): Inferred type: tfun('z:type, U('z:type)) +// Info 4164: (76-77): Inferred type: 'z:type // Info 4164: (84-159): Inferred type: (T, U(T), U(U(T))) -> () // Info 4164: (96-123): Inferred type: (T, U(T), U(U(T))) // Info 4164: (97-101): Inferred type: T diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol index 5aa190ecef5c..ca83ca2ec7fc 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol @@ -16,11 +16,11 @@ function run() { // compileViaYul: true // ---- // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. -// Info 4164: (31-47): Inferred type: tfun((?ba:type, ?bb:type, ?bc:type), T(?ba:type, ?bb:type, ?bc:type)) -// Info 4164: (37-46): Inferred type: (?x:type, ?y:type, ?z:type) -// Info 4164: (38-39): Inferred type: ?x:type -// Info 4164: (41-42): Inferred type: ?y:type -// Info 4164: (44-45): Inferred type: ?z:type +// Info 4164: (31-47): Inferred type: tfun(('x:type, 'y:type, 'z:type), T('x:type, 'y:type, 'z:type)) +// Info 4164: (37-46): Inferred type: ('x:type, 'y:type, 'z:type) +// Info 4164: (38-39): Inferred type: 'x:type +// Info 4164: (41-42): Inferred type: 'y:type +// Info 4164: (44-45): Inferred type: 'z:type // Info 4164: (48-55): Inferred type: U // Info 4164: (56-63): Inferred type: V // Info 4164: (65-81): Inferred type: C @@ -29,19 +29,19 @@ function run() { // Info 4164: (88-92): Inferred type: 'l:(type, D) // Info 4164: (100-170): Inferred type: () -> () // Info 4164: (112-114): Inferred type: () -// Info 4164: (125-141): Inferred type: T(U, ?bk:type, ?bm:(type, C)) -// Info 4164: (128-141): Inferred type: T(U, ?bk:type, ?bm:(type, C)) -// Info 4164: (128-129): Inferred type: tfun((U, ?bk:type, ?bm:(type, C)), T(U, ?bk:type, ?bm:(type, C))) +// Info 4164: (125-141): Inferred type: T(U, ?bh:type, ?bj:(type, C)) +// Info 4164: (128-141): Inferred type: T(U, ?bh:type, ?bj:(type, C)) +// Info 4164: (128-129): Inferred type: tfun((U, ?bh:type, ?bj:(type, C)), T(U, ?bh:type, ?bj:(type, C))) // Info 4164: (130-131): Inferred type: U -// Info 4164: (133-134): Inferred type: ?bk:type -// Info 4164: (136-140): Inferred type: ?bm:(type, C) -// Info 4164: (136-137): Inferred type: ?bm:(type, C) -// Info 4164: (139-140): Inferred type: ?bm:(type, C) -// Info 4164: (151-167): Inferred type: T(V, ?br:type, ?bt:(type, D)) -// Info 4164: (154-167): Inferred type: T(V, ?br:type, ?bt:(type, D)) -// Info 4164: (154-155): Inferred type: tfun((V, ?br:type, ?bt:(type, D)), T(V, ?br:type, ?bt:(type, D))) +// Info 4164: (133-134): Inferred type: ?bh:type +// Info 4164: (136-140): Inferred type: ?bj:(type, C) +// Info 4164: (136-137): Inferred type: ?bj:(type, C) +// Info 4164: (139-140): Inferred type: ?bj:(type, C) +// Info 4164: (151-167): Inferred type: T(V, ?bo:type, ?bq:(type, D)) +// Info 4164: (154-167): Inferred type: T(V, ?bo:type, ?bq:(type, D)) +// Info 4164: (154-155): Inferred type: tfun((V, ?bo:type, ?bq:(type, D)), T(V, ?bo:type, ?bq:(type, D))) // Info 4164: (156-157): Inferred type: V -// Info 4164: (159-160): Inferred type: ?br:type -// Info 4164: (162-166): Inferred type: ?bt:(type, D) -// Info 4164: (162-163): Inferred type: ?bt:(type, D) -// Info 4164: (165-166): Inferred type: ?bt:(type, D) +// Info 4164: (159-160): Inferred type: ?bo:type +// Info 4164: (162-166): Inferred type: ?bq:(type, D) +// Info 4164: (162-163): Inferred type: ?bq:(type, D) +// Info 4164: (165-166): Inferred type: ?bq:(type, D) 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 27631d064d18..ebebf6dafc3c 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 @@ -24,15 +24,15 @@ function fun() { // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. // Info 4164: (31-41): Inferred type: uint // Info 4164: (42-54): Inferred type: string -// Info 4164: (56-66): Inferred type: tfun(?v:type, T(?v:type)) -// Info 4164: (62-65): Inferred type: ?u:type -// Info 4164: (63-64): Inferred type: ?u:type -// Info 4164: (67-84): Inferred type: tfun(?x:type, U(?x:type)) -// Info 4164: (73-76): Inferred type: ?w:type -// Info 4164: (74-75): Inferred type: ?w:type -// Info 4164: (79-83): Inferred type: T(?w:type) -// Info 4164: (79-80): Inferred type: tfun(?w:type, T(?w:type)) -// Info 4164: (81-82): Inferred type: ?w:type +// Info 4164: (56-66): Inferred type: tfun('u:type, T('u:type)) +// Info 4164: (62-65): Inferred type: 'u:type +// Info 4164: (63-64): Inferred type: 'u:type +// Info 4164: (67-84): Inferred type: tfun('v:type, U('v:type)) +// Info 4164: (73-76): Inferred type: 'v:type +// Info 4164: (74-75): Inferred type: 'v:type +// Info 4164: (79-83): Inferred type: T('v:type) +// Info 4164: (79-80): Inferred type: tfun('v:type, T('v:type)) +// Info 4164: (81-82): Inferred type: 'v:type // Info 4164: (86-245): Inferred type: () -> () // Info 4164: (98-100): Inferred type: () // Info 4164: (111-121): Inferred type: U(uint) @@ -43,13 +43,13 @@ function fun() { // Info 4164: (134-141): Inferred type: T(uint) // Info 4164: (134-135): Inferred type: tfun(uint, T(uint)) // Info 4164: (136-140): Inferred type: uint -// Info 4164: (147-155): Inferred type: T(?bi:type) -// Info 4164: (147-152): Inferred type: U(uint) -> T(?bi:type) -// Info 4164: (147-148): Inferred type: U(?bg:type) +// Info 4164: (147-155): Inferred type: T(uint) +// Info 4164: (147-152): Inferred type: U(uint) -> T(uint) +// Info 4164: (147-148): Inferred type: U(?be:type) // Info 4164: (153-154): Inferred type: U(uint) -// Info 4164: (161-169): Inferred type: U(?bm:type) -// Info 4164: (161-166): Inferred type: T(uint) -> U(?bm:type) -// Info 4164: (161-162): Inferred type: U(?bk:type) +// Info 4164: (161-169): Inferred type: U(uint) +// Info 4164: (161-166): Inferred type: T(uint) -> U(uint) +// Info 4164: (161-162): Inferred type: U(?bh:type) // Info 4164: (167-168): Inferred type: T(uint) // Info 4164: (180-192): Inferred type: U(string) // Info 4164: (183-192): Inferred type: U(string) @@ -59,11 +59,11 @@ function fun() { // Info 4164: (205-214): Inferred type: T(string) // Info 4164: (205-206): Inferred type: tfun(string, T(string)) // Info 4164: (207-213): Inferred type: string -// Info 4164: (220-228): Inferred type: T(?bu:type) -// Info 4164: (220-225): Inferred type: U(string) -> T(?bu:type) -// Info 4164: (220-221): Inferred type: U(?bs:type) +// Info 4164: (220-228): Inferred type: T(string) +// Info 4164: (220-225): Inferred type: U(string) -> T(string) +// Info 4164: (220-221): Inferred type: U(?bo:type) // Info 4164: (226-227): Inferred type: U(string) -// Info 4164: (234-242): Inferred type: U(?by:type) -// Info 4164: (234-239): Inferred type: T(string) -> U(?by:type) -// Info 4164: (234-235): Inferred type: U(?bw:type) +// Info 4164: (234-242): Inferred type: U(string) +// Info 4164: (234-239): Inferred type: T(string) -> U(string) +// Info 4164: (234-235): Inferred type: U(?br:type) // Info 4164: (240-241): Inferred type: T(string) 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 22c4796e0fcd..c26436e06cca 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 @@ -51,9 +51,9 @@ function fun(a: T(int: P3), b: T(str: P4)) { // ---- // Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. // Info 4164: (31-61): Inferred type: bool -// Info 4164: (63-73): Inferred type: tfun(?z:type, T(?z:type)) -// Info 4164: (69-72): Inferred type: ?y:type -// Info 4164: (70-71): Inferred type: ?y:type +// Info 4164: (63-73): Inferred type: tfun('y:type, T('y:type)) +// Info 4164: (69-72): Inferred type: 'y:type +// Info 4164: (70-71): Inferred type: 'y:type // Info 4164: (74-83): Inferred type: int // Info 4164: (84-93): Inferred type: str // Info 4164: (95-156): Inferred type: C @@ -80,56 +80,56 @@ function fun(a: T(int: P3), b: T(str: P4)) { // Info 4164: (332-356): Inferred type: void // Info 4164: (357-381): Inferred type: void // Info 4164: (383-458): Inferred type: void -// Info 4164: (398-405): Inferred type: 'bv:(type, P1) -// Info 4164: (399-404): Inferred type: 'bv:(type, P1) -// Info 4164: (402-404): Inferred type: 'bv:(type, P1) -// Info 4164: (415-456): Inferred type: (T('bv:(type, P1)), T('bv:(type, P1))) -> T('bv:(type, P1)) -// Info 4164: (427-445): Inferred type: (T('bv:(type, P1)), T('bv:(type, P1))) -// Info 4164: (428-435): Inferred type: T('bv:(type, P1)) -// Info 4164: (431-435): Inferred type: T('bv:(type, P1)) -// Info 4164: (431-432): Inferred type: tfun('bv:(type, P1), T('bv:(type, P1))) -// Info 4164: (433-434): Inferred type: 'bv:(type, P1) -// Info 4164: (437-444): Inferred type: T('bv:(type, P1)) -// Info 4164: (440-444): Inferred type: T('bv:(type, P1)) -// Info 4164: (440-441): Inferred type: tfun('bv:(type, P1), T('bv:(type, P1))) -// Info 4164: (442-443): Inferred type: 'bv:(type, P1) -// Info 4164: (449-453): Inferred type: T('bv:(type, P1)) -// Info 4164: (449-450): Inferred type: tfun('bv:(type, P1), T('bv:(type, P1))) -// Info 4164: (451-452): Inferred type: 'bv:(type, P1) +// Info 4164: (398-405): Inferred type: 'bu:(type, P1) +// Info 4164: (399-404): Inferred type: 'bu:(type, P1) +// Info 4164: (402-404): Inferred type: 'bu:(type, P1) +// Info 4164: (415-456): Inferred type: (T('bu:(type, P1)), T('bu:(type, P1))) -> T('bu:(type, P1)) +// Info 4164: (427-445): Inferred type: (T('bu:(type, P1)), T('bu:(type, P1))) +// Info 4164: (428-435): Inferred type: T('bu:(type, P1)) +// Info 4164: (431-435): Inferred type: T('bu:(type, P1)) +// Info 4164: (431-432): Inferred type: tfun('bu:(type, P1), T('bu:(type, P1))) +// Info 4164: (433-434): Inferred type: 'bu:(type, P1) +// Info 4164: (437-444): Inferred type: T('bu:(type, P1)) +// Info 4164: (440-444): Inferred type: T('bu:(type, P1)) +// Info 4164: (440-441): Inferred type: tfun('bu:(type, P1), T('bu:(type, P1))) +// Info 4164: (442-443): Inferred type: 'bu:(type, P1) +// Info 4164: (449-453): Inferred type: T('bu:(type, P1)) +// Info 4164: (449-450): Inferred type: tfun('bu:(type, P1), T('bu:(type, P1))) +// Info 4164: (451-452): Inferred type: 'bu:(type, P1) // Info 4164: (460-535): Inferred type: void -// Info 4164: (475-482): Inferred type: 'cf:(type, P2) -// Info 4164: (476-481): Inferred type: 'cf:(type, P2) -// Info 4164: (479-481): Inferred type: 'cf:(type, P2) -// Info 4164: (493-533): Inferred type: (T('cf:(type, P2)), T('cf:(type, P2))) -> bool -// Info 4164: (504-522): Inferred type: (T('cf:(type, P2)), T('cf:(type, P2))) -// Info 4164: (505-512): Inferred type: T('cf:(type, P2)) -// Info 4164: (508-512): Inferred type: T('cf:(type, P2)) -// Info 4164: (508-509): Inferred type: tfun('cf:(type, P2), T('cf:(type, P2))) -// Info 4164: (510-511): Inferred type: 'cf:(type, P2) -// Info 4164: (514-521): Inferred type: T('cf:(type, P2)) -// Info 4164: (517-521): Inferred type: T('cf:(type, P2)) -// Info 4164: (517-518): Inferred type: tfun('cf:(type, P2), T('cf:(type, P2))) -// Info 4164: (519-520): Inferred type: 'cf:(type, P2) +// Info 4164: (475-482): Inferred type: 'ce:(type, P2) +// Info 4164: (476-481): Inferred type: 'ce:(type, P2) +// Info 4164: (479-481): Inferred type: 'ce:(type, P2) +// Info 4164: (493-533): Inferred type: (T('ce:(type, P2)), T('ce:(type, P2))) -> bool +// Info 4164: (504-522): Inferred type: (T('ce:(type, P2)), T('ce:(type, P2))) +// Info 4164: (505-512): Inferred type: T('ce:(type, P2)) +// Info 4164: (508-512): Inferred type: T('ce:(type, P2)) +// Info 4164: (508-509): Inferred type: tfun('ce:(type, P2), T('ce:(type, P2))) +// Info 4164: (510-511): Inferred type: 'ce:(type, P2) +// Info 4164: (514-521): Inferred type: T('ce:(type, P2)) +// Info 4164: (517-521): Inferred type: T('ce:(type, P2)) +// Info 4164: (517-518): Inferred type: tfun('ce:(type, P2), T('ce:(type, P2))) +// Info 4164: (519-520): Inferred type: 'ce:(type, P2) // Info 4164: (526-530): Inferred type: bool // Info 4164: (537-618): Inferred type: void -// Info 4164: (552-565): Inferred type: 'bj:(type, P1, P2) -// Info 4164: (553-564): Inferred type: 'bj:(type, P1, P2) -// Info 4164: (556-564): Inferred type: 'bj:(type, P1, P2) -// Info 4164: (557-559): Inferred type: 'bj:(type, P1, P2) -// Info 4164: (561-563): Inferred type: 'bj:(type, P1, P2) -// Info 4164: (575-616): Inferred type: (T('bj:(type, P1, P2)), T('bj:(type, P1, P2))) -> T('bj:(type, P1, P2)) -// Info 4164: (587-605): Inferred type: (T('bj:(type, P1, P2)), T('bj:(type, P1, P2))) -// Info 4164: (588-595): Inferred type: T('bj:(type, P1, P2)) -// Info 4164: (591-595): Inferred type: T('bj:(type, P1, P2)) -// Info 4164: (591-592): Inferred type: tfun('bj:(type, P1, P2), T('bj:(type, P1, P2))) -// Info 4164: (593-594): Inferred type: 'bj:(type, P1, P2) -// Info 4164: (597-604): Inferred type: T('bj:(type, P1, P2)) -// Info 4164: (600-604): Inferred type: T('bj:(type, P1, P2)) -// Info 4164: (600-601): Inferred type: tfun('bj:(type, P1, P2), T('bj:(type, P1, P2))) -// Info 4164: (602-603): Inferred type: 'bj:(type, P1, P2) -// Info 4164: (609-613): Inferred type: T('bj:(type, P1, P2)) -// Info 4164: (609-610): Inferred type: tfun('bj:(type, P1, P2), T('bj:(type, P1, P2))) -// Info 4164: (611-612): Inferred type: 'bj:(type, P1, P2) +// Info 4164: (552-565): Inferred type: 'bi:(type, P1, P2) +// Info 4164: (553-564): Inferred type: 'bi:(type, P1, P2) +// Info 4164: (556-564): Inferred type: 'bi:(type, P1, P2) +// Info 4164: (557-559): Inferred type: 'bi:(type, P1, P2) +// Info 4164: (561-563): Inferred type: 'bi:(type, P1, P2) +// Info 4164: (575-616): Inferred type: (T('bi:(type, P1, P2)), T('bi:(type, P1, P2))) -> T('bi:(type, P1, P2)) +// Info 4164: (587-605): Inferred type: (T('bi:(type, P1, P2)), T('bi:(type, P1, P2))) +// Info 4164: (588-595): Inferred type: T('bi:(type, P1, P2)) +// Info 4164: (591-595): Inferred type: T('bi:(type, P1, P2)) +// Info 4164: (591-592): Inferred type: tfun('bi:(type, P1, P2), T('bi:(type, P1, P2))) +// Info 4164: (593-594): Inferred type: 'bi:(type, P1, P2) +// Info 4164: (597-604): Inferred type: T('bi:(type, P1, P2)) +// Info 4164: (600-604): Inferred type: T('bi:(type, P1, P2)) +// Info 4164: (600-601): Inferred type: tfun('bi:(type, P1, P2), T('bi:(type, P1, P2))) +// Info 4164: (602-603): Inferred type: 'bi:(type, P1, P2) +// Info 4164: (609-613): Inferred type: T('bi:(type, P1, P2)) +// Info 4164: (609-610): Inferred type: tfun('bi:(type, P1, P2), T('bi:(type, P1, P2))) +// Info 4164: (611-612): Inferred type: 'bi:(type, P1, P2) // Info 4164: (620-748): Inferred type: (T(int), T(str)) -> () // Info 4164: (632-662): Inferred type: (T(int), T(str)) // Info 4164: (633-646): Inferred type: T(int) From 7bf8ff29328de69a443d6e5e90459001f188cfce Mon Sep 17 00:00:00 2001 From: r0qs Date: Mon, 5 Feb 2024 13:51:40 +0100 Subject: [PATCH 088/189] Fix openzeppelin external test --- test/externalTests/zeppelin.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/externalTests/zeppelin.sh b/test/externalTests/zeppelin.sh index 54d753347573..64e6f5f0ec90 100755 --- a/test/externalTests/zeppelin.sh +++ b/test/externalTests/zeppelin.sh @@ -106,6 +106,10 @@ function zeppelin_test force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" npm install + # TODO: We fix the version to 2.0.3 instead of 2.0.4 since the latter does not work with ethers.js 6.10.0 + # Maybe related to the use of dynamic imports here: https://github.com/NomicFoundation/hardhat/commit/16ae15642951ac324ef7093a3342f7cf3a2a49a4 + npm install @nomicfoundation/hardhat-chai-matchers@2.0.3 + replace_version_pragmas for preset in $SELECTED_PRESETS; do From a3e5f3f43154382118d25a0937dec67d3b062f5a Mon Sep 17 00:00:00 2001 From: Jeason Date: Fri, 19 Jan 2024 08:13:06 +0000 Subject: [PATCH 089/189] feat: add new rule name `usingAliases` for solidity `usingDirective` --- docs/grammar/SolidityParser.g4 | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/grammar/SolidityParser.g4 b/docs/grammar/SolidityParser.g4 index 4a756029c2ef..c46cefd2fa34 100644 --- a/docs/grammar/SolidityParser.g4 +++ b/docs/grammar/SolidityParser.g4 @@ -337,7 +337,14 @@ userDefinableOperator: * Using directive to attach library functions and free functions to types. * Can occur within contracts and libraries and at the file level. */ -usingDirective: Using (identifierPath | (LBrace identifierPath (As userDefinableOperator)? (Comma identifierPath (As userDefinableOperator)?)* RBrace)) For (Mul | typeName) Global? Semicolon; +usingDirective: + Using ( + identifierPath + | (LBrace usingAliases (Comma usingAliases)* RBrace) + ) For (Mul | typeName) Global? Semicolon; + +usingAliases: identifierPath (As userDefinableOperator)?; + /** * A type name can be an elementary type, a function type, a mapping type, a user-defined type * (e.g. a contract or struct) or an array type. From ed96eb256f7519f350abc83b2eb7c712efe992ce Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Thu, 25 Jan 2024 06:36:50 +0100 Subject: [PATCH 090/189] Yul fuzzer: Support blobhash(uint) global function --- test/tools/ossfuzz/protoToYul.cpp | 9 +++++++++ test/tools/ossfuzz/yulProto.proto | 1 + 2 files changed, 10 insertions(+) diff --git a/test/tools/ossfuzz/protoToYul.cpp b/test/tools/ossfuzz/protoToYul.cpp index 1719dec46c98..1ba0d599d4b7 100644 --- a/test/tools/ossfuzz/protoToYul.cpp +++ b/test/tools/ossfuzz/protoToYul.cpp @@ -599,6 +599,12 @@ void ProtoConverter::visit(UnaryOp const& _x) return; } + if (op == UnaryOp::BLOBHASH && !m_evmVersion.hasBlobHash()) + { + m_output << dictionaryToken(); + return; + } + // The following instructions may lead to change of EVM state and are hence // excluded to avoid false positives. if ( @@ -647,6 +653,9 @@ void ProtoConverter::visit(UnaryOp const& _x) case UnaryOp::BLOCKHASH: m_output << "blockhash"; break; + case UnaryOp::BLOBHASH: + m_output << "blobhash"; + break; } m_output << "("; if (op == UnaryOp::MLOAD) diff --git a/test/tools/ossfuzz/yulProto.proto b/test/tools/ossfuzz/yulProto.proto index 2c10329de683..cf14b887c1b3 100644 --- a/test/tools/ossfuzz/yulProto.proto +++ b/test/tools/ossfuzz/yulProto.proto @@ -156,6 +156,7 @@ message UnaryOp { EXTCODEHASH = 7; BALANCE = 8; BLOCKHASH = 9; + BLOBHASH = 10; } required UOp op = 1; required Expression operand = 2; From c7db606144fd426d561196f6e38006073cfb18dc Mon Sep 17 00:00:00 2001 From: pgebal Date: Tue, 6 Feb 2024 11:43:42 +0100 Subject: [PATCH 091/189] Print a warning about function parameter names being used instead of values in SMTChecker (#14832) --- Changelog.md | 1 + libsolidity/formal/Predicate.cpp | 11 +++++++++-- test/libsolidity/smtCheckerTests/types/string_1.sol | 3 ++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 7ab13de8fe4f..d20c266d8384 100644 --- a/Changelog.md +++ b/Changelog.md @@ -23,6 +23,7 @@ Language Features: Compiler Features: * EVM: Support for the EVM Version "Cancun". * SMTChecker: Support `bytes.concat` except when string literals are passed as arguments. + * SMTChecker: Print a message that function parameter name was used instead of a concrete value in a counterexample when the concrete value found by the solver is too long to print. * Standard JSON Interface: Add experimental support to import EVM assembly in the format used by ``--asm-json``. * TypeChecker: Comparison of internal function pointers now yields a warning, as it can produce unexpected results with the legacy pipeline enabled. diff --git a/libsolidity/formal/Predicate.cpp b/libsolidity/formal/Predicate.cpp index 58a5a5a3e721..01316d75ff43 100644 --- a/libsolidity/formal/Predicate.cpp +++ b/libsolidity/formal/Predicate.cpp @@ -316,11 +316,14 @@ std::string Predicate::formatSummaryCall( auto const& params = fun->parameters(); solAssert(params.size() == functionArgsCex.size(), ""); + bool paramNameInsteadOfValue = false; for (unsigned i = 0; i < params.size(); ++i) if (params.at(i) && functionArgsCex.at(i)) functionArgs.emplace_back(*functionArgsCex.at(i)); - else + else { + paramNameInsteadOfValue = true; functionArgs.emplace_back(params[i]->name()); + } std::string fName = fun->isConstructor() ? "constructor" : fun->isFallback() ? "fallback" : @@ -335,7 +338,11 @@ std::string Predicate::formatSummaryCall( solAssert(fun->annotation().contract, ""); prefix = fun->annotation().contract->name() + "."; } - return prefix + fName + "(" + boost::algorithm::join(functionArgs, ", ") + ")" + txModel; + + std::string summary = prefix + fName + "(" + boost::algorithm::join(functionArgs, ", ") + ")" + txModel; + if (paramNameInsteadOfValue) + summary += " -- counterexample incomplete; parameter name used instead of value"; + return summary; } std::vector> Predicate::summaryStateValues(std::vector const& _args) const diff --git a/test/libsolidity/smtCheckerTests/types/string_1.sol b/test/libsolidity/smtCheckerTests/types/string_1.sol index 8209f992805d..3a74a8e1ce31 100644 --- a/test/libsolidity/smtCheckerTests/types/string_1.sol +++ b/test/libsolidity/smtCheckerTests/types/string_1.sol @@ -6,5 +6,6 @@ contract C } // ==== // SMTEngine: all +// SMTIgnoreCex: no // ---- -// Warning 6328: (77-121): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.f(s1, s2) +// Warning 6328: (77-121): CHC: Assertion violation happens here.\nCounterexample:\n\n\nTransaction trace:\nC.constructor()\nC.f(s1, s2) -- counterexample incomplete; parameter name used instead of value From 1c8072a2099e17a2467356fc862cae9b2074a3ff Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 7 Feb 2024 11:56:50 +0100 Subject: [PATCH 092/189] Remove dynamic byte arrays from value types section --- docs/types/value-types.rst | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index d44d2fd70ad8..12fcb0b171d6 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -415,14 +415,6 @@ Members: .. note:: Prior to version 0.8.0, ``byte`` used to be an alias for ``bytes1``. -Dynamically-sized byte array ----------------------------- - -``bytes``: - Dynamically-sized byte array, see :ref:`arrays`. Not a value-type! -``string``: - Dynamically-sized UTF-8-encoded string, see :ref:`arrays`. Not a value-type! - .. index:: address, ! literal;address .. _address_literals: From 71a05dfe538c980c8987a5cdb04b273d6f0d755c Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 11 Jan 2024 13:04:52 +0100 Subject: [PATCH 093/189] Purge using namespace std from test/yulPhaser --- scripts/check_style.sh | 1 + test/yulPhaser/AlgorithmRunner.cpp | 125 ++++++++++++------------ test/yulPhaser/Chromosome.cpp | 19 ++-- test/yulPhaser/Common.cpp | 25 +++-- test/yulPhaser/FitnessMetrics.cpp | 35 ++++--- test/yulPhaser/GeneticAlgorithms.cpp | 83 ++++++++-------- test/yulPhaser/Mutations.cpp | 139 +++++++++++++-------------- test/yulPhaser/PairSelections.cpp | 52 +++++----- test/yulPhaser/Phaser.cpp | 85 ++++++++-------- test/yulPhaser/Population.cpp | 51 +++++----- test/yulPhaser/Program.cpp | 75 +++++++-------- test/yulPhaser/ProgramCache.cpp | 39 ++++---- test/yulPhaser/Selections.cpp | 93 +++++++++--------- test/yulPhaser/SimulationRNG.cpp | 44 ++++----- test/yulPhaser/TestHelpers.cpp | 27 +++--- test/yulPhaser/TestHelpersTest.cpp | 19 ++-- 16 files changed, 448 insertions(+), 464 deletions(-) diff --git a/scripts/check_style.sh b/scripts/check_style.sh index 7bcd8cca36c9..fe4d19734fc1 100755 --- a/scripts/check_style.sh +++ b/scripts/check_style.sh @@ -50,6 +50,7 @@ NAMESPACE_STD_FREE_FILES=( test/libyul/* test/solc/* test/tools/yulInterpreter/* + test/yulPhaser/* ) ( diff --git a/test/yulPhaser/AlgorithmRunner.cpp b/test/yulPhaser/AlgorithmRunner.cpp index 003403929eb2..56868f1ce76e 100644 --- a/test/yulPhaser/AlgorithmRunner.cpp +++ b/test/yulPhaser/AlgorithmRunner.cpp @@ -35,7 +35,6 @@ #include #include -using namespace std; using namespace boost::unit_test::framework; using namespace boost::test_tools; using namespace solidity::langutil; @@ -74,44 +73,44 @@ class AlgorithmRunnerFixture { protected: // NOTE: Regexes here should not contain spaces because we strip them before matching - regex RoundSummaryRegex{R"(-+ROUND\d+\[round:[0-9.]+s,total:[0-9.]+s\]-+)"}; - regex InitialPopulationHeaderRegex{"-+INITIALPOPULATION-+"}; + std::regex RoundSummaryRegex{R"(-+ROUND\d+\[round:[0-9.]+s,total:[0-9.]+s\]-+)"}; + std::regex InitialPopulationHeaderRegex{"-+INITIALPOPULATION-+"}; - string individualPattern(Individual const& individual) const + std::string individualPattern(Individual const& individual) const { - ostringstream output; + std::ostringstream output; output << individual.fitness << individual.chromosome; return output.str(); } - string topChromosomePattern(size_t roundNumber, Individual const& individual) const + std::string topChromosomePattern(size_t roundNumber, Individual const& individual) const { - ostringstream output; + std::ostringstream output; output << roundNumber << R"(\|[0-9.]+\|)" << individualPattern(individual); return output.str(); } - bool nextLineMatches(stringstream& stream, regex const& pattern) const + bool nextLineMatches(std::stringstream& stream, std::regex const& pattern) const { - string line; - if (getline(stream, line).fail()) + std::string line; + if (std::getline(stream, line).fail()) return false; - return regex_match(stripWhitespace(line), pattern); + return std::regex_match(stripWhitespace(line), pattern); } - shared_ptr m_fitnessMetric = make_shared(); + std::shared_ptr m_fitnessMetric = std::make_shared(); Population const m_population = Population::makeRandom(m_fitnessMetric, 5, 0, 20); - stringstream m_output; + std::stringstream m_output; AlgorithmRunner::Options m_options; }; class AlgorithmRunnerAutosaveFixture: public AlgorithmRunnerFixture { public: - static vector chromosomeStrings(Population const& _population) + static std::vector chromosomeStrings(Population const& _population) { - vector lines; + std::vector lines; for (auto const& individual: _population.individuals()) lines.push_back(toString(individual.chromosome)); @@ -120,7 +119,7 @@ class AlgorithmRunnerAutosaveFixture: public AlgorithmRunnerFixture protected: TemporaryDirectory m_tempDir; - string const m_autosavePath = (m_tempDir.path() / "population-autosave.txt").string(); + std::string const m_autosavePath = (m_tempDir.path() / "population-autosave.txt").string(); RandomisingAlgorithm m_algorithm; }; @@ -153,12 +152,12 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_round_summary_after_each_round, Algorit runner.run(algorithm); BOOST_TEST(nextLineMatches(m_output, RoundSummaryRegex)); for (auto const& individual: runner.population().individuals()) - BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); + BOOST_TEST(nextLineMatches(m_output, std::regex(individualPattern(individual)))); runner.run(algorithm); BOOST_TEST(nextLineMatches(m_output, RoundSummaryRegex)); for (auto const& individual: runner.population().individuals()) - BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); + BOOST_TEST(nextLineMatches(m_output, std::regex(individualPattern(individual)))); BOOST_TEST(m_output.peek() == EOF); } @@ -172,9 +171,9 @@ BOOST_FIXTURE_TEST_CASE(run_should_not_print_round_summary_if_not_requested, Alg RandomisingAlgorithm algorithm; runner.run(algorithm); - BOOST_TEST(nextLineMatches(m_output, regex(""))); + BOOST_TEST(nextLineMatches(m_output, std::regex(""))); for (auto const& individual: runner.population().individuals()) - BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); + BOOST_TEST(nextLineMatches(m_output, std::regex(individualPattern(individual)))); BOOST_TEST(m_output.peek() == EOF); } @@ -202,7 +201,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_only_top_chromosome_if_requested, Algor RandomisingAlgorithm algorithm; runner.run(algorithm); - BOOST_TEST(nextLineMatches(m_output, regex(topChromosomePattern(1, runner.population().individuals()[0])))); + BOOST_TEST(nextLineMatches(m_output, std::regex(topChromosomePattern(1, runner.population().individuals()[0])))); BOOST_TEST(m_output.peek() == EOF); } @@ -216,7 +215,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_not_print_round_number_for_top_chromosome_if_ RandomisingAlgorithm algorithm; runner.run(algorithm); - BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(runner.population().individuals()[0])))); + BOOST_TEST(nextLineMatches(m_output, std::regex(individualPattern(runner.population().individuals()[0])))); BOOST_TEST(m_output.peek() == EOF); } @@ -246,7 +245,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_initial_population_if_requested, Algori BOOST_TEST(nextLineMatches(m_output, InitialPopulationHeaderRegex)); for (auto const& individual: m_population.individuals()) - BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); + BOOST_TEST(nextLineMatches(m_output, std::regex(individualPattern(individual)))); BOOST_TEST(m_output.peek() == EOF); } @@ -277,7 +276,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_whole_initial_population_even_if_only_t BOOST_TEST(nextLineMatches(m_output, InitialPopulationHeaderRegex)); for (auto const& individual: m_population.individuals()) - BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); + BOOST_TEST(nextLineMatches(m_output, std::regex(individualPattern(individual)))); BOOST_TEST(m_output.peek() == EOF); } @@ -290,21 +289,21 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_cache_stats_if_requested, AlgorithmRunn m_options.showCacheStats = true; RandomisingAlgorithm algorithm; - vector sourceStreams = { + std::vector sourceStreams = { CharStream("{mstore(10, 20)}", ""), CharStream("{mstore(10, 20)\nsstore(10, 20)}", ""), }; - vector programs = { + std::vector programs = { get(Program::load(sourceStreams[0])), get(Program::load(sourceStreams[1])), }; - vector> caches = { - make_shared(programs[0]), - make_shared(programs[1]), + std::vector> caches = { + std::make_shared(programs[0]), + std::make_shared(programs[1]), }; - shared_ptr fitnessMetric = make_shared(vector>{ - make_shared(nullopt, caches[0], CodeWeights{}), - make_shared(nullopt, caches[1], CodeWeights{}), + std::shared_ptr fitnessMetric = std::make_shared(std::vector>{ + std::make_shared(std::nullopt, caches[0], CodeWeights{}), + std::make_shared(std::nullopt, caches[1], CodeWeights{}), }); Population population = Population::makeRandom(fitnessMetric, 2, 0, 5); @@ -318,14 +317,14 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_cache_stats_if_requested, AlgorithmRunn for (size_t i = 0; i < m_options.maxRounds.value() - 1; ++i) { - BOOST_TEST(nextLineMatches(m_output, regex(".*"))); - BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+"))); + BOOST_TEST(nextLineMatches(m_output, std::regex(".*"))); + BOOST_TEST(nextLineMatches(m_output, std::regex("-+CACHESTATS-+"))); if (i > 0) - BOOST_TEST(nextLineMatches(m_output, regex(R"(Round\d+:\d+entries)"))); - BOOST_TEST(nextLineMatches(m_output, regex(R"(Round\d+:\d+entries)"))); - BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalhits:\d+)"))); - BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalmisses:\d+)"))); - BOOST_TEST(nextLineMatches(m_output, regex(R"(Sizeofcachedcode:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, std::regex(R"(Round\d+:\d+entries)"))); + BOOST_TEST(nextLineMatches(m_output, std::regex(R"(Round\d+:\d+entries)"))); + BOOST_TEST(nextLineMatches(m_output, std::regex(R"(Totalhits:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, std::regex(R"(Totalmisses:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, std::regex(R"(Sizeofcachedcode:\d+)"))); } BOOST_REQUIRE(stats.roundEntryCounts.size() == 2); @@ -333,13 +332,13 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_cache_stats_if_requested, AlgorithmRunn BOOST_REQUIRE(stats.roundEntryCounts.count(m_options.maxRounds.value()) == 1); size_t round = m_options.maxRounds.value(); - BOOST_TEST(nextLineMatches(m_output, regex(".*"))); - BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+"))); - BOOST_TEST(nextLineMatches(m_output, regex("Round" + toString(round - 1) + ":" + toString(stats.roundEntryCounts[round - 1]) + "entries"))); - BOOST_TEST(nextLineMatches(m_output, regex("Round" + toString(round) + ":" + toString(stats.roundEntryCounts[round]) + "entries"))); - BOOST_TEST(nextLineMatches(m_output, regex("Totalhits:" + toString(stats.hits)))); - BOOST_TEST(nextLineMatches(m_output, regex("Totalmisses:" + toString(stats.misses)))); - BOOST_TEST(nextLineMatches(m_output, regex("Sizeofcachedcode:" + toString(stats.totalCodeSize)))); + BOOST_TEST(nextLineMatches(m_output, std::regex(".*"))); + BOOST_TEST(nextLineMatches(m_output, std::regex("-+CACHESTATS-+"))); + BOOST_TEST(nextLineMatches(m_output, std::regex("Round" + toString(round - 1) + ":" + toString(stats.roundEntryCounts[round - 1]) + "entries"))); + BOOST_TEST(nextLineMatches(m_output, std::regex("Round" + toString(round) + ":" + toString(stats.roundEntryCounts[round]) + "entries"))); + BOOST_TEST(nextLineMatches(m_output, std::regex("Totalhits:" + toString(stats.hits)))); + BOOST_TEST(nextLineMatches(m_output, std::regex("Totalmisses:" + toString(stats.misses)))); + BOOST_TEST(nextLineMatches(m_output, std::regex("Sizeofcachedcode:" + toString(stats.totalCodeSize)))); BOOST_TEST(m_output.peek() == EOF); } @@ -355,9 +354,9 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_message_if_cache_stats_requested_but_ca AlgorithmRunner runner(m_population, {nullptr}, m_options, m_output); runner.run(algorithm); - BOOST_TEST(nextLineMatches(m_output, regex(".*"))); - BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+"))); - BOOST_TEST(nextLineMatches(m_output, regex(stripWhitespace("Program cache disabled")))); + BOOST_TEST(nextLineMatches(m_output, std::regex(".*"))); + BOOST_TEST(nextLineMatches(m_output, std::regex("-+CACHESTATS-+"))); + BOOST_TEST(nextLineMatches(m_output, std::regex(stripWhitespace("Program cache disabled")))); BOOST_TEST(m_output.peek() == EOF); } @@ -371,18 +370,18 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_partial_stats_and_message_if_some_cache RandomisingAlgorithm algorithm; CharStream sourceStream = CharStream("{}", ""); - shared_ptr cache = make_shared(get(Program::load(sourceStream))); + std::shared_ptr cache = std::make_shared(std::get(Program::load(sourceStream))); AlgorithmRunner runner(m_population, {cache, nullptr}, m_options, m_output); BOOST_REQUIRE(cache->gatherStats().roundEntryCounts.size() == 0); runner.run(algorithm); - BOOST_TEST(nextLineMatches(m_output, regex(".*"))); - BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+"))); - BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalhits:\d+)"))); - BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalmisses:\d+)"))); - BOOST_TEST(nextLineMatches(m_output, regex(R"(Sizeofcachedcode:\d+)"))); - BOOST_TEST(nextLineMatches(m_output, regex(stripWhitespace("Program cache disabled for 1 out of 2 programs")))); + BOOST_TEST(nextLineMatches(m_output, std::regex(".*"))); + BOOST_TEST(nextLineMatches(m_output, std::regex("-+CACHESTATS-+"))); + BOOST_TEST(nextLineMatches(m_output, std::regex(R"(Totalhits:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, std::regex(R"(Totalmisses:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, std::regex(R"(Sizeofcachedcode:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, std::regex(stripWhitespace("Program cache disabled for 1 out of 2 programs")))); BOOST_TEST(m_output.peek() == EOF); } @@ -421,10 +420,10 @@ BOOST_FIXTURE_TEST_CASE(run_should_overwrite_existing_file_if_autosave_file_spec AlgorithmRunner runner(m_population, {}, m_options, m_output); assert(!fs::exists(m_autosavePath)); - vector originalContent = {"Original content"}; + std::vector originalContent = {"Original content"}; { - ofstream tmpFile(m_autosavePath); - tmpFile << originalContent[0] << endl; + std::ofstream tmpFile(m_autosavePath); + tmpFile << originalContent[0] << std::endl; } assert(fs::exists(m_autosavePath)); assert(readLinesFromFile(m_autosavePath) == originalContent); @@ -438,7 +437,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_overwrite_existing_file_if_autosave_file_spec BOOST_FIXTURE_TEST_CASE(run_should_not_save_population_to_file_if_autosave_file_not_specified, AlgorithmRunnerAutosaveFixture) { m_options.maxRounds = 5; - m_options.populationAutosaveFile = nullopt; + m_options.populationAutosaveFile = std::nullopt; AlgorithmRunner runner(m_population, {}, m_options, m_output); assert(!fs::exists(m_autosavePath)); @@ -499,9 +498,9 @@ BOOST_FIXTURE_TEST_CASE(run_should_not_randomise_duplicate_chromosomes_if_not_re BOOST_FIXTURE_TEST_CASE(run_should_clear_cache_at_the_beginning_and_update_it_before_each_round, AlgorithmRunnerFixture) { CharStream sourceStream = CharStream("{}", current_test_case().p_name); - vector> caches = { - make_shared(get(Program::load(sourceStream))), - make_shared(get(Program::load(sourceStream))), + std::vector> caches = { + std::make_shared(std::get(Program::load(sourceStream))), + std::make_shared(std::get(Program::load(sourceStream))), }; m_options.maxRounds = 10; diff --git a/test/yulPhaser/Chromosome.cpp b/test/yulPhaser/Chromosome.cpp index 1d12bf18c161..da109086a916 100644 --- a/test/yulPhaser/Chromosome.cpp +++ b/test/yulPhaser/Chromosome.cpp @@ -40,14 +40,13 @@ #include -using namespace std; using namespace solidity::yul; using namespace solidity::util; namespace solidity::phaser::test { -vector const ChrOmOsoMeSteps{ +std::vector const ChrOmOsoMeSteps{ ConditionalSimplifier::name, FunctionHoister::name, UnusedAssignEliminator::name, @@ -76,10 +75,10 @@ BOOST_AUTO_TEST_CASE(makeRandom_should_use_every_possible_step_with_the_same_pro constexpr int samplesPerStep = 500; constexpr double relativeTolerance = 0.02; - map stepIndices = enumerateOptmisationSteps(); + std::map stepIndices = enumerateOptmisationSteps(); auto chromosome = Chromosome::makeRandom(stepIndices.size() * samplesPerStep); - vector samples; + std::vector samples; for (auto& step: chromosome.optimisationSteps()) samples.push_back(stepIndices.at(step)); @@ -97,7 +96,7 @@ BOOST_AUTO_TEST_CASE(constructor_should_store_genes) BOOST_AUTO_TEST_CASE(constructor_should_store_optimisation_steps) { - vector steps = { + std::vector steps = { StructuralSimplifier::name, BlockFlattener::name, UnusedPruner::name, @@ -108,7 +107,7 @@ BOOST_AUTO_TEST_CASE(constructor_should_store_optimisation_steps) BOOST_AUTO_TEST_CASE(constructor_should_allow_duplicate_steps) { - vector steps = { + std::vector steps = { StructuralSimplifier::name, StructuralSimplifier::name, BlockFlattener::name, @@ -131,7 +130,7 @@ BOOST_AUTO_TEST_CASE(constructor_should_allow_genes_that_do_not_correspond_to_an BOOST_AUTO_TEST_CASE(output_operator_should_create_concise_and_unambiguous_string_representation) { - vector allSteps; + std::vector allSteps; for (auto const& step: OptimiserSuite::allSteps()) allSteps.push_back(step.first); Chromosome chromosome(allSteps); @@ -152,8 +151,8 @@ BOOST_AUTO_TEST_CASE(randomOptimisationStep_should_return_each_step_with_same_pr constexpr int samplesPerStep = 500; constexpr double relativeTolerance = 0.02; - map stepIndices = enumerateOptmisationSteps(); - vector samples; + std::map stepIndices = enumerateOptmisationSteps(); + std::vector samples; for (size_t i = 0; i <= stepIndices.size() * samplesPerStep; ++i) samples.push_back(stepIndices.at(Chromosome::randomOptimisationStep())); @@ -172,7 +171,7 @@ BOOST_AUTO_TEST_CASE(stepsToGenes_should_translate_optimisation_step_names_to_ab BOOST_AUTO_TEST_CASE(genesToSteps_should_translate_optimisation_step_abbreviations_to_names) { - BOOST_TEST(Chromosome::genesToSteps("") == vector{}); + BOOST_TEST(Chromosome::genesToSteps("") == std::vector{}); BOOST_TEST(Chromosome::genesToSteps("ChrOmOsoMe") == ChrOmOsoMeSteps); } diff --git a/test/yulPhaser/Common.cpp b/test/yulPhaser/Common.cpp index 734738483547..c4842045fa3f 100644 --- a/test/yulPhaser/Common.cpp +++ b/test/yulPhaser/Common.cpp @@ -30,7 +30,6 @@ #include #include -using namespace std; using namespace boost::test_tools; using namespace solidity::util; @@ -56,7 +55,7 @@ enum class TestEnum GH, }; -map const TestEnumToStringMap = +std::map const TestEnumToStringMap = { {TestEnum::A, "a"}, {TestEnum::B, "b"}, @@ -64,7 +63,7 @@ map const TestEnumToStringMap = {TestEnum::CD, "c-d"}, {TestEnum::EF, "e f"}, }; -map const StringToTestEnumMap = invertMap(TestEnumToStringMap); +std::map const StringToTestEnumMap = invertMap(TestEnumToStringMap); } @@ -74,29 +73,29 @@ BOOST_AUTO_TEST_SUITE(CommonTest) BOOST_FIXTURE_TEST_CASE(readLinesFromFile_should_return_all_lines_from_a_text_file_as_strings_without_newlines, ReadLinesFromFileFixture) { { - ofstream tmpFile((m_tempDir.path() / "test-file.txt").string()); - tmpFile << endl << "Line 1" << endl << endl << endl << "Line 2" << endl << "#" << endl << endl; + std::ofstream tmpFile((m_tempDir.path() / "test-file.txt").string()); + tmpFile << std::endl << "Line 1" << std::endl << std::endl << std::endl << "Line 2" << std::endl << "#" << std::endl << std::endl; } - vector lines = readLinesFromFile((m_tempDir.path() / "test-file.txt").string()); - BOOST_TEST((lines == vector{"", "Line 1", "", "", "Line 2", "#", ""})); + std::vector lines = readLinesFromFile((m_tempDir.path() / "test-file.txt").string()); + BOOST_TEST((lines == std::vector{"", "Line 1", "", "", "Line 2", "#", ""})); } BOOST_AUTO_TEST_CASE(deserializeChoice_should_convert_string_to_enum) { - istringstream aStream("a"); + std::istringstream aStream("a"); TestEnum aResult; deserializeChoice(aStream, aResult, StringToTestEnumMap); BOOST_CHECK(aResult == TestEnum::A); BOOST_TEST(!aStream.fail()); - istringstream bStream("b"); + std::istringstream bStream("b"); TestEnum bResult; deserializeChoice(bStream, bResult, StringToTestEnumMap); BOOST_CHECK(bResult == TestEnum::B); BOOST_TEST(!bStream.fail()); - istringstream cdStream("c-d"); + std::istringstream cdStream("c-d"); TestEnum cdResult; deserializeChoice(cdStream, cdResult, StringToTestEnumMap); BOOST_CHECK(cdResult == TestEnum::CD); @@ -105,7 +104,7 @@ BOOST_AUTO_TEST_CASE(deserializeChoice_should_convert_string_to_enum) BOOST_AUTO_TEST_CASE(deserializeChoice_should_set_failbit_if_there_is_no_enum_corresponding_to_string) { - istringstream xyzStream("xyz"); + std::istringstream xyzStream("xyz"); TestEnum xyzResult; deserializeChoice(xyzStream, xyzResult, StringToTestEnumMap); BOOST_TEST(xyzStream.fail()); @@ -113,13 +112,13 @@ BOOST_AUTO_TEST_CASE(deserializeChoice_should_set_failbit_if_there_is_no_enum_co BOOST_AUTO_TEST_CASE(deserializeChoice_does_not_have_to_support_strings_with_spaces) { - istringstream abStream("a b"); + std::istringstream abStream("a b"); TestEnum abResult; deserializeChoice(abStream, abResult, StringToTestEnumMap); BOOST_CHECK(abResult == TestEnum::A); BOOST_TEST(!abStream.fail()); - istringstream efStream("e f"); + std::istringstream efStream("e f"); TestEnum efResult; deserializeChoice(efStream, efResult, StringToTestEnumMap); BOOST_TEST(efStream.fail()); diff --git a/test/yulPhaser/FitnessMetrics.cpp b/test/yulPhaser/FitnessMetrics.cpp index bde15a537fd8..7fab2e18935e 100644 --- a/test/yulPhaser/FitnessMetrics.cpp +++ b/test/yulPhaser/FitnessMetrics.cpp @@ -29,7 +29,6 @@ #include -using namespace std; using namespace solidity::langutil; using namespace solidity::util; using namespace solidity::yul; @@ -74,22 +73,22 @@ class ProgramBasedMetricFixture } CharStream m_sourceStream = CharStream(SampleSourceCode, ""); - Chromosome m_chromosome{vector{UnusedPruner::name, EquivalentFunctionCombiner::name}}; - Program m_program = get(Program::load(m_sourceStream)); + Chromosome m_chromosome{std::vector{UnusedPruner::name, EquivalentFunctionCombiner::name}}; + Program m_program = std::get(Program::load(m_sourceStream)); Program m_optimisedProgram = optimisedProgram(m_program); - shared_ptr m_programCache = make_shared(m_program); + std::shared_ptr m_programCache = std::make_shared(m_program); static constexpr CodeWeights m_weights{}; }; class FitnessMetricCombinationFixture: public ProgramBasedMetricFixture { protected: - vector> m_simpleMetrics = { - make_shared(m_program, nullptr, m_weights, 1), - make_shared(m_program, nullptr, m_weights, 2), - make_shared(m_program, nullptr, m_weights, 3), + std::vector> m_simpleMetrics = { + std::make_shared(m_program, nullptr, m_weights, 1), + std::make_shared(m_program, nullptr, m_weights, 2), + std::make_shared(m_program, nullptr, m_weights, 3), }; - vector m_fitness = { + std::vector m_fitness = { m_simpleMetrics[0]->evaluate(m_chromosome), m_simpleMetrics[1]->evaluate(m_chromosome), m_simpleMetrics[2]->evaluate(m_chromosome), @@ -102,7 +101,7 @@ BOOST_AUTO_TEST_SUITE(ProgramBasedMetricTest) BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_return_optimised_program_even_if_cache_not_available, ProgramBasedMetricFixture) { - string code = toString(DummyProgramBasedMetric(m_program, nullptr, m_weights).optimisedProgram(m_chromosome)); + std::string code = toString(DummyProgramBasedMetric(m_program, nullptr, m_weights).optimisedProgram(m_chromosome)); BOOST_TEST(code != toString(m_program)); BOOST_TEST(code == toString(m_optimisedProgram)); @@ -110,7 +109,7 @@ BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_return_optimised_program_even_if BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_use_cache_if_available, ProgramBasedMetricFixture) { - string code = toString(DummyProgramBasedMetric(nullopt, m_programCache, m_weights).optimisedProgram(m_chromosome)); + std::string code = toString(DummyProgramBasedMetric(std::nullopt, m_programCache, m_weights).optimisedProgram(m_chromosome)); BOOST_TEST(code != toString(m_program)); BOOST_TEST(code == toString(m_optimisedProgram)); @@ -119,7 +118,7 @@ BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_use_cache_if_available, ProgramB BOOST_FIXTURE_TEST_CASE(optimisedProgramNoCache_should_return_optimised_program_even_if_cache_not_available, ProgramBasedMetricFixture) { - string code = toString(DummyProgramBasedMetric(m_program, nullptr, m_weights).optimisedProgramNoCache(m_chromosome)); + std::string code = toString(DummyProgramBasedMetric(m_program, nullptr, m_weights).optimisedProgramNoCache(m_chromosome)); BOOST_TEST(code != toString(m_program)); BOOST_TEST(code == toString(m_optimisedProgram)); @@ -127,7 +126,7 @@ BOOST_FIXTURE_TEST_CASE(optimisedProgramNoCache_should_return_optimised_program_ BOOST_FIXTURE_TEST_CASE(optimisedProgramNoCache_should_not_use_cache_even_if_available, ProgramBasedMetricFixture) { - string code = toString(DummyProgramBasedMetric(nullopt, m_programCache, m_weights).optimisedProgramNoCache(m_chromosome)); + std::string code = toString(DummyProgramBasedMetric(std::nullopt, m_programCache, m_weights).optimisedProgramNoCache(m_chromosome)); BOOST_TEST(code != toString(m_program)); BOOST_TEST(code == toString(m_optimisedProgram)); @@ -147,7 +146,7 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_size_of_the_optimised_program, P BOOST_FIXTURE_TEST_CASE(evaluate_should_be_able_to_use_program_cache_if_available, ProgramBasedMetricFixture) { - size_t fitness = ProgramSize(nullopt, m_programCache, m_weights).evaluate(m_chromosome); + size_t fitness = ProgramSize(std::nullopt, m_programCache, m_weights).evaluate(m_chromosome); BOOST_TEST(fitness != m_program.codeSize(m_weights)); BOOST_TEST(fitness == m_optimisedProgram.codeSize(m_weights)); @@ -190,7 +189,7 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_the_size_ratio_between_optimised BOOST_FIXTURE_TEST_CASE(evaluate_should_be_able_to_use_program_cache_if_available, ProgramBasedMetricFixture) { BOOST_TEST( - RelativeProgramSize(nullopt, m_programCache, 3, m_weights).evaluate(m_chromosome) == + RelativeProgramSize(std::nullopt, m_programCache, 3, m_weights).evaluate(m_chromosome) == round(1000.0 * double(m_optimisedProgram.codeSize(m_weights)) / double(m_program.codeSize(m_weights))) ); BOOST_TEST(m_programCache->size() == m_chromosome.length()); @@ -219,7 +218,7 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_number_of_repetitions_is_z BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_the_original_program_size_is_zero, ProgramBasedMetricFixture) { CharStream sourceStream = CharStream("{}", ""); - Program program = get(Program::load(sourceStream)); + Program program = std::get(Program::load(sourceStream)); RelativeProgramSize metric(program, nullptr, 3, m_weights); @@ -264,7 +263,7 @@ BOOST_FIXTURE_TEST_CASE(FitnessMetricMaximum_evaluate_should_compute_maximum_of_ FitnessMetricMaximum metric(m_simpleMetrics); assert(m_simpleMetrics.size() == 3); - BOOST_TEST(metric.evaluate(m_chromosome) == max(m_fitness[0], max(m_fitness[1], m_fitness[2]))); + BOOST_TEST(metric.evaluate(m_chromosome) == std::max(m_fitness[0], std::max(m_fitness[1], m_fitness[2]))); BOOST_TEST(metric.metrics() == m_simpleMetrics); } @@ -273,7 +272,7 @@ BOOST_FIXTURE_TEST_CASE(FitnessMetricMinimum_evaluate_should_compute_minimum_of_ FitnessMetricMinimum metric(m_simpleMetrics); assert(m_simpleMetrics.size() == 3); - BOOST_TEST(metric.evaluate(m_chromosome) == min(m_fitness[0], min(m_fitness[1], m_fitness[2]))); + BOOST_TEST(metric.evaluate(m_chromosome) == std::min(m_fitness[0], std::min(m_fitness[1], m_fitness[2]))); BOOST_TEST(metric.metrics() == m_simpleMetrics); } diff --git a/test/yulPhaser/GeneticAlgorithms.cpp b/test/yulPhaser/GeneticAlgorithms.cpp index 29c9cfa2ecaf..c3b661334499 100644 --- a/test/yulPhaser/GeneticAlgorithms.cpp +++ b/test/yulPhaser/GeneticAlgorithms.cpp @@ -29,7 +29,6 @@ #include #include -using namespace std; using namespace boost::unit_test::framework; using namespace boost::test_tools; using namespace solidity::util; @@ -40,7 +39,7 @@ namespace solidity::phaser::test class GeneticAlgorithmFixture { protected: - shared_ptr m_fitnessMetric = make_shared(); + std::shared_ptr m_fitnessMetric = std::make_shared(); }; class ClassicGeneticAlgorithmFixture: public GeneticAlgorithmFixture @@ -64,41 +63,41 @@ BOOST_AUTO_TEST_SUITE(RandomAlgorithmTest) BOOST_FIXTURE_TEST_CASE(runNextRound_should_preserve_elite_and_randomise_rest_of_population, GeneticAlgorithmFixture) { auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); - assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + assert((chromosomeLengths(population) == std::vector{3, 3, 3, 3, 5, 5, 5, 5})); RandomAlgorithm algorithm({0.5, 1, 1}); Population newPopulation = algorithm.runNextRound(population); - BOOST_TEST((chromosomeLengths(newPopulation) == vector{1, 1, 1, 1, 3, 3, 3, 3})); + BOOST_TEST((chromosomeLengths(newPopulation) == std::vector{1, 1, 1, 1, 3, 3, 3, 3})); } BOOST_FIXTURE_TEST_CASE(runNextRound_should_not_replace_elite_with_worse_individuals, GeneticAlgorithmFixture) { auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); - assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + assert((chromosomeLengths(population) == std::vector{3, 3, 3, 3, 5, 5, 5, 5})); RandomAlgorithm algorithm({0.5, 7, 7}); Population newPopulation = algorithm.runNextRound(population); - BOOST_TEST((chromosomeLengths(newPopulation) == vector{3, 3, 3, 3, 7, 7, 7, 7})); + BOOST_TEST((chromosomeLengths(newPopulation) == std::vector{3, 3, 3, 3, 7, 7, 7, 7})); } BOOST_FIXTURE_TEST_CASE(runNextRound_should_replace_all_chromosomes_if_zero_size_elite, GeneticAlgorithmFixture) { auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); - assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + assert((chromosomeLengths(population) == std::vector{3, 3, 3, 3, 5, 5, 5, 5})); RandomAlgorithm algorithm({0.0, 1, 1}); Population newPopulation = algorithm.runNextRound(population); - BOOST_TEST((chromosomeLengths(newPopulation) == vector{1, 1, 1, 1, 1, 1, 1, 1})); + BOOST_TEST((chromosomeLengths(newPopulation) == std::vector{1, 1, 1, 1, 1, 1, 1, 1})); } BOOST_FIXTURE_TEST_CASE(runNextRound_should_not_replace_any_chromosomes_if_whole_population_is_the_elite, GeneticAlgorithmFixture) { auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); - assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + assert((chromosomeLengths(population) == std::vector{3, 3, 3, 3, 5, 5, 5, 5})); RandomAlgorithm algorithm({1.0, 1, 1}); Population newPopulation = algorithm.runNextRound(population); - BOOST_TEST((chromosomeLengths(newPopulation) == vector{3, 3, 3, 3, 5, 5, 5, 5})); + BOOST_TEST((chromosomeLengths(newPopulation) == std::vector{3, 3, 3, 3, 5, 5, 5, 5})); } BOOST_AUTO_TEST_SUITE_END() @@ -107,7 +106,7 @@ BOOST_AUTO_TEST_SUITE(GenerationalElitistWithExclusivePoolsTest) BOOST_FIXTURE_TEST_CASE(runNextRound_should_preserve_elite_and_regenerate_rest_of_population, GeneticAlgorithmFixture) { auto population = Population::makeRandom(m_fitnessMetric, 6, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); - assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 3, 3, 5, 5, 5, 5})); + assert((chromosomeLengths(population) == std::vector{3, 3, 3, 3, 3, 3, 5, 5, 5, 5})); GenerationalElitistWithExclusivePools::Options options = { /* mutationPoolSize = */ 0.2, @@ -123,13 +122,13 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_preserve_elite_and_regenerate_rest_o Population newPopulation = algorithm.runNextRound(population); - BOOST_TEST((chromosomeLengths(newPopulation) == vector{0, 0, 3, 3, 3, 3, 3, 3, 3, 3})); + BOOST_TEST((chromosomeLengths(newPopulation) == std::vector{0, 0, 3, 3, 3, 3, 3, 3, 3, 3})); } BOOST_FIXTURE_TEST_CASE(runNextRound_should_not_replace_elite_with_worse_individuals, GeneticAlgorithmFixture) { auto population = Population::makeRandom(m_fitnessMetric, 6, 3, 3) + Population::makeRandom(m_fitnessMetric, 4, 5, 5); - assert(chromosomeLengths(population) == (vector{3, 3, 3, 3, 3, 3, 5, 5, 5, 5})); + assert(chromosomeLengths(population) == (std::vector{3, 3, 3, 3, 3, 3, 5, 5, 5, 5})); GenerationalElitistWithExclusivePools::Options options = { /* mutationPoolSize = */ 0.2, @@ -145,7 +144,7 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_not_replace_elite_with_worse_individ Population newPopulation = algorithm.runNextRound(population); - BOOST_TEST((chromosomeLengths(newPopulation) == vector{3, 3, 3, 3, 3, 3, 3, 3, 7, 7})); + BOOST_TEST((chromosomeLengths(newPopulation) == std::vector{3, 3, 3, 3, 3, 3, 3, 3, 7, 7})); } BOOST_FIXTURE_TEST_CASE(runNextRound_should_generate_individuals_in_the_crossover_pool_by_mutating_the_elite, GeneticAlgorithmFixture) @@ -169,7 +168,7 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_generate_individuals_in_the_crossove BOOST_TEST(( chromosomeLengths(newPopulation) == - vector{0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 11, 11, 11} + std::vector{0, 0, 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 11, 11, 11} )); } @@ -179,7 +178,7 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_generate_individuals_in_the_crossove Population(m_fitnessMetric, {Chromosome("aa"), Chromosome("ff")}) + Population::makeRandom(m_fitnessMetric, 8, 6, 6) ); - assert((chromosomeLengths(population) == vector{2, 2, 6, 6, 6, 6, 6, 6, 6, 6})); + assert((chromosomeLengths(population) == std::vector{2, 2, 6, 6, 6, 6, 6, 6, 6, 6})); GenerationalElitistWithExclusivePools::Options options = { /* mutationPoolSize = */ 0.0, @@ -196,8 +195,8 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_generate_individuals_in_the_crossove SimulationRNG::reset(1); Population newPopulation = algorithm.runNextRound(population); - vector const& newIndividuals = newPopulation.individuals(); - BOOST_TEST((chromosomeLengths(newPopulation) == vector{2, 2, 2, 2, 2, 2, 2, 2, 2, 2})); + std::vector const& newIndividuals = newPopulation.individuals(); + BOOST_TEST((chromosomeLengths(newPopulation) == std::vector{2, 2, 2, 2, 2, 2, 2, 2, 2, 2})); for (auto& individual: newIndividuals) BOOST_TEST(( individual.chromosome == Chromosome("aa") || @@ -228,7 +227,7 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_select_individuals_with_probability_ Population::makeRandom(m_fitnessMetric, populationSize / 4, 2, 2) + Population::makeRandom(m_fitnessMetric, populationSize / 4, 3, 3); - map expectedProbabilities = { + std::map expectedProbabilities = { {0, 4.0 / (4 + 3 + 2 + 1)}, {1, 3.0 / (4 + 3 + 2 + 1)}, {2, 2.0 / (4 + 3 + 2 + 1)}, @@ -252,9 +251,9 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_select_individuals_with_probability_ BOOST_TEST(newPopulation.individuals().size() == population.individuals().size()); - vector newFitness = chromosomeLengths(newPopulation); - BOOST_TEST(abs(mean(newFitness) - expectedValue) < expectedValue * relativeTolerance); - BOOST_TEST(abs(meanSquaredError(newFitness, expectedValue) - variance) < variance * relativeTolerance); + std::vector newFitness = chromosomeLengths(newPopulation); + BOOST_TEST(std::abs(mean(newFitness) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(std::abs(meanSquaredError(newFitness, expectedValue) - variance) < variance * relativeTolerance); } BOOST_FIXTURE_TEST_CASE(runNextRound_should_select_only_individuals_existing_in_the_original_population, ClassicGeneticAlgorithmFixture) @@ -262,7 +261,7 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_select_only_individuals_existing_in_ constexpr size_t populationSize = 1000; auto population = Population::makeRandom(m_fitnessMetric, populationSize, 1, 10); - set originalSteps; + std::set originalSteps; for (auto const& individual: population.individuals()) originalSteps.insert(toString(individual.chromosome)); @@ -281,8 +280,8 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_crossover, ClassicGeneticAlgorith Chromosome("gg"), Chromosome("gg"), Chromosome("gg"), }); - set originalSteps{"aa", "ff", "gg"}; - set crossedSteps{"af", "fa", "fg", "gf", "ga", "ag"}; + std::set originalSteps{"aa", "ff", "gg"}; + std::set crossedSteps{"af", "fa", "fg", "gf", "ga", "ag"}; m_options.crossoverChance = 0.8; ClassicGeneticAlgorithm algorithm(m_options); @@ -312,22 +311,22 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_mutation, ClassicGeneticAlgorithm double const variance = m_options.mutationChance * (1 - m_options.mutationChance); Chromosome chromosome("aaaaaaaaaa"); - vector chromosomes(populationSize, chromosome); + std::vector chromosomes(populationSize, chromosome); Population population(m_fitnessMetric, chromosomes); SimulationRNG::reset(1); Population newPopulation = algorithm.runNextRound(population); - vector bernoulliTrials; + std::vector bernoulliTrials; for (auto const& individual: newPopulation.individuals()) { - string steps = toString(individual.chromosome); + std::string steps = toString(individual.chromosome); for (char step: steps) bernoulliTrials.push_back(static_cast(step != 'a')); } - BOOST_TEST(abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance); - BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance); + BOOST_TEST(std::abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(std::abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance); } BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_deletion, ClassicGeneticAlgorithmFixture) @@ -341,22 +340,22 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_deletion, ClassicGeneticAlgorithm double const variance = m_options.deletionChance * (1 - m_options.deletionChance); Chromosome chromosome("aaaaaaaaaa"); - vector chromosomes(populationSize, chromosome); + std::vector chromosomes(populationSize, chromosome); Population population(m_fitnessMetric, chromosomes); SimulationRNG::reset(1); Population newPopulation = algorithm.runNextRound(population); - vector bernoulliTrials; + std::vector bernoulliTrials; for (auto const& individual: newPopulation.individuals()) { - string steps = toString(individual.chromosome); + std::string steps = toString(individual.chromosome); for (size_t i = 0; i < chromosome.length(); ++i) bernoulliTrials.push_back(static_cast(i >= steps.size())); } - BOOST_TEST(abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance); - BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance); + BOOST_TEST(std::abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(std::abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance); } BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_addition, ClassicGeneticAlgorithmFixture) @@ -370,16 +369,16 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_addition, ClassicGeneticAlgorithm double const variance = m_options.additionChance * (1 - m_options.additionChance); Chromosome chromosome("aaaaaaaaaa"); - vector chromosomes(populationSize, chromosome); + std::vector chromosomes(populationSize, chromosome); Population population(m_fitnessMetric, chromosomes); SimulationRNG::reset(1); Population newPopulation = algorithm.runNextRound(population); - vector bernoulliTrials; + std::vector bernoulliTrials; for (auto const& individual: newPopulation.individuals()) { - string steps = toString(individual.chromosome); + std::string steps = toString(individual.chromosome); for (size_t i = 0; i < chromosome.length() + 1; ++i) { BOOST_REQUIRE(chromosome.length() <= steps.size() && steps.size() <= 2 * chromosome.length() + 1); @@ -387,21 +386,21 @@ BOOST_FIXTURE_TEST_CASE(runNextRound_should_do_addition, ClassicGeneticAlgorithm } } - BOOST_TEST(abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance); - BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance); + BOOST_TEST(std::abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(std::abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance); } BOOST_FIXTURE_TEST_CASE(runNextRound_should_preserve_elite, ClassicGeneticAlgorithmFixture) { auto population = Population::makeRandom(m_fitnessMetric, 4, 3, 3) + Population::makeRandom(m_fitnessMetric, 6, 5, 5); - assert((chromosomeLengths(population) == vector{3, 3, 3, 3, 5, 5, 5, 5, 5, 5})); + assert((chromosomeLengths(population) == std::vector{3, 3, 3, 3, 5, 5, 5, 5, 5, 5})); m_options.elitePoolSize = 0.5; m_options.deletionChance = 1.0; ClassicGeneticAlgorithm algorithm(m_options); Population newPopulation = algorithm.runNextRound(population); - BOOST_TEST((chromosomeLengths(newPopulation) == vector{0, 0, 0, 0, 0, 3, 3, 3, 3, 5})); + BOOST_TEST((chromosomeLengths(newPopulation) == std::vector{0, 0, 0, 0, 0, 3, 3, 3, 3, 5})); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/yulPhaser/Mutations.cpp b/test/yulPhaser/Mutations.cpp index 8e1d0238b3ef..a5a715e23a2a 100644 --- a/test/yulPhaser/Mutations.cpp +++ b/test/yulPhaser/Mutations.cpp @@ -30,7 +30,6 @@ #include #include -using namespace std; using namespace solidity::util; using namespace solidity::yul; @@ -48,7 +47,7 @@ BOOST_AUTO_TEST_CASE(geneRandomisation_should_iterate_over_genes_and_replace_the // Use genes that do not represent valid step abbreviations to be able to easily spot added steps. assert(OptimiserSuite::stepAbbreviationToNameMap().count('.') == 0); - Chromosome input = Chromosome(string(inputLength, '.')); + Chromosome input = Chromosome(std::string(inputLength, '.')); SimulationRNG::reset(1); for (size_t randomisationChancePercent = 20; randomisationChancePercent <= 100; randomisationChancePercent += 20) @@ -56,7 +55,7 @@ BOOST_AUTO_TEST_CASE(geneRandomisation_should_iterate_over_genes_and_replace_the double const randomisationChance = double(randomisationChancePercent) / 100.0; Chromosome output = geneRandomisation(randomisationChance)(input); - string outputGenes = output.genes(); + std::string outputGenes = output.genes(); BOOST_REQUIRE(output.length() == input.length()); double const expectedValue = randomisationChance; @@ -66,15 +65,15 @@ BOOST_AUTO_TEST_CASE(geneRandomisation_should_iterate_over_genes_and_replace_the (inputLength - randomisedGeneCount) * expectedValue * expectedValue + randomisedGeneCount * (1 - expectedValue) * (1 - expectedValue); - BOOST_TEST(abs(randomisedGeneCount / inputLength - expectedValue) < tolerance); - BOOST_TEST(abs(squaredError / inputLength - variance) < tolerance); + BOOST_TEST(std::abs(randomisedGeneCount / inputLength - expectedValue) < tolerance); + BOOST_TEST(std::abs(squaredError / inputLength - variance) < tolerance); } } BOOST_AUTO_TEST_CASE(geneRandomisation_should_return_identical_chromosome_if_probability_is_zero) { Chromosome chromosome("fcCUnDvejsrmV"); - function mutation = geneRandomisation(0.0); + std::function mutation = geneRandomisation(0.0); BOOST_TEST(mutation(chromosome) == chromosome); } @@ -86,7 +85,7 @@ BOOST_AUTO_TEST_CASE(geneDeletion_should_iterate_over_genes_and_delete_them_with // Use genes that do not represent valid step abbreviations to be able to easily spot added steps. assert(OptimiserSuite::stepAbbreviationToNameMap().count('.') == 0); - Chromosome input = Chromosome(string(inputLength, '.')); + Chromosome input = Chromosome(std::string(inputLength, '.')); SimulationRNG::reset(1); for (size_t deletionChancePercent = 20; deletionChancePercent < 100; deletionChancePercent += 20) @@ -94,7 +93,7 @@ BOOST_AUTO_TEST_CASE(geneDeletion_should_iterate_over_genes_and_delete_them_with double const deletionChance = double(deletionChancePercent) / 100.0; Chromosome output = geneDeletion(deletionChance)(input); - string outputGenes = output.genes(); + std::string outputGenes = output.genes(); BOOST_REQUIRE(output.length() <= input.length()); BOOST_REQUIRE(static_cast(count(outputGenes.begin(), outputGenes.end(), '.')) == output.length()); @@ -105,15 +104,15 @@ BOOST_AUTO_TEST_CASE(geneDeletion_should_iterate_over_genes_and_delete_them_with (double(inputLength) - deletedGeneCount) * expectedValue * expectedValue + deletedGeneCount * (1.0 - expectedValue) * (1.0 - expectedValue); - BOOST_TEST(abs(deletedGeneCount / double(inputLength) - expectedValue) < tolerance); - BOOST_TEST(abs(squaredError / double(inputLength) - variance) < tolerance); + BOOST_TEST(std::abs(deletedGeneCount / double(inputLength) - expectedValue) < tolerance); + BOOST_TEST(std::abs(squaredError / double(inputLength) - variance) < tolerance); } } BOOST_AUTO_TEST_CASE(geneDeletion_should_return_identical_chromosome_if_probability_is_zero) { Chromosome chromosome("fcCUnDvejsrmV"); - function mutation = geneDeletion(0.0); + std::function mutation = geneDeletion(0.0); BOOST_TEST(mutation(chromosome) == chromosome); } @@ -121,7 +120,7 @@ BOOST_AUTO_TEST_CASE(geneDeletion_should_return_identical_chromosome_if_probabil BOOST_AUTO_TEST_CASE(geneDeletion_should_delete_all_genes_if_probability_is_one) { Chromosome chromosome("fcCUnDvejsrmV"); - function mutation = geneDeletion(1.0); + std::function mutation = geneDeletion(1.0); BOOST_TEST(mutation(chromosome) == Chromosome("")); } @@ -134,7 +133,7 @@ BOOST_AUTO_TEST_CASE(geneAddition_should_iterate_over_gene_positions_and_insert_ // Use genes that do not represent valid step abbreviations to be able to easily spot added steps. assert(OptimiserSuite::stepAbbreviationToNameMap().count('.') == 0); - Chromosome input = Chromosome(string(inputLength, '.')); + Chromosome input = Chromosome(std::string(inputLength, '.')); SimulationRNG::reset(1); for (size_t additionChancePercent = 20; additionChancePercent < 100; additionChancePercent += 20) @@ -145,8 +144,8 @@ BOOST_AUTO_TEST_CASE(geneAddition_should_iterate_over_gene_positions_and_insert_ BOOST_REQUIRE(output.length() >= input.length()); BOOST_REQUIRE(output.length() <= inputLength + maxAdditions); - string_view outputGenes = output.genes(); - size_t preservedGeneCount = static_cast(count(outputGenes.begin(), outputGenes.end(), '.')); + std::string_view outputGenes = output.genes(); + size_t preservedGeneCount = static_cast(std::count(outputGenes.begin(), outputGenes.end(), '.')); BOOST_REQUIRE(preservedGeneCount == input.length()); double const expectedValue = additionChance; @@ -156,8 +155,8 @@ BOOST_AUTO_TEST_CASE(geneAddition_should_iterate_over_gene_positions_and_insert_ (double(maxAdditions) - addedGeneCount) * expectedValue * expectedValue + addedGeneCount * (1.0 - expectedValue) * (1.0 - expectedValue); - BOOST_TEST(abs(addedGeneCount / double(maxAdditions) - expectedValue) < tolerance); - BOOST_TEST(abs(squaredError / double(maxAdditions) - variance) < tolerance); + BOOST_TEST(std::abs(addedGeneCount / double(maxAdditions) - expectedValue) < tolerance); + BOOST_TEST(std::abs(squaredError / double(maxAdditions) - variance) < tolerance); } } @@ -165,7 +164,7 @@ BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_before_first_position { SimulationRNG::reset(7); Chromosome chromosome("fcCUnDvejs"); - function mutation = geneAddition(0.1); + std::function mutation = geneAddition(0.1); Chromosome mutatedChromosome = mutation(chromosome); BOOST_TEST(mutatedChromosome.length() > chromosome.length()); @@ -176,7 +175,7 @@ BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_after_last_position) { SimulationRNG::reset(81); Chromosome chromosome("fcCUnDvejs"); - function mutation = geneAddition(0.1); + std::function mutation = geneAddition(0.1); Chromosome mutatedChromosome = mutation(chromosome); BOOST_TEST(mutatedChromosome.length() > chromosome.length()); @@ -186,7 +185,7 @@ BOOST_AUTO_TEST_CASE(geneAddition_should_be_able_to_insert_after_last_position) BOOST_AUTO_TEST_CASE(geneAddition_should_return_identical_chromosome_if_probability_is_zero) { Chromosome chromosome("fcCUnDvejsrmV"); - function mutation = geneAddition(0.0); + std::function mutation = geneAddition(0.0); BOOST_TEST(mutation(chromosome) == chromosome); } @@ -194,12 +193,12 @@ BOOST_AUTO_TEST_CASE(geneAddition_should_return_identical_chromosome_if_probabil BOOST_AUTO_TEST_CASE(geneAddition_should_insert_genes_at_all_positions_if_probability_is_one) { Chromosome chromosome("fcCUnDvejsrmV"); - function mutation = geneAddition(1.0); + std::function mutation = geneAddition(1.0); Chromosome mutatedChromosome = mutation(chromosome); BOOST_TEST(mutatedChromosome.length() == chromosome.length() * 2 + 1); - vector originalGenes; + std::vector originalGenes; for (size_t i = 0; i < mutatedChromosome.length() - 1; ++i) if (i % 2 == 1) originalGenes.push_back(mutatedChromosome.optimisationSteps()[i]); @@ -211,7 +210,7 @@ BOOST_AUTO_TEST_CASE(alternativeMutations_should_choose_between_mutations_with_g { SimulationRNG::reset(1); Chromosome chromosome("a"); - function mutation = alternativeMutations( + std::function mutation = alternativeMutations( 0.8, wholeChromosomeReplacement(Chromosome("c")), wholeChromosomeReplacement(Chromosome("f")) @@ -234,7 +233,7 @@ BOOST_AUTO_TEST_CASE(alternativeMutations_should_choose_between_mutations_with_g BOOST_AUTO_TEST_CASE(alternativeMutations_should_always_choose_first_mutation_if_probability_is_one) { Chromosome chromosome("a"); - function mutation = alternativeMutations( + std::function mutation = alternativeMutations( 1.0, wholeChromosomeReplacement(Chromosome("c")), wholeChromosomeReplacement(Chromosome("f")) @@ -247,7 +246,7 @@ BOOST_AUTO_TEST_CASE(alternativeMutations_should_always_choose_first_mutation_if BOOST_AUTO_TEST_CASE(alternativeMutations_should_always_choose_second_mutation_if_probability_is_zero) { Chromosome chromosome("a"); - function mutation = alternativeMutations( + std::function mutation = alternativeMutations( 0.0, wholeChromosomeReplacement(Chromosome("c")), wholeChromosomeReplacement(Chromosome("f")) @@ -260,8 +259,8 @@ BOOST_AUTO_TEST_CASE(alternativeMutations_should_always_choose_second_mutation_i BOOST_AUTO_TEST_CASE(mutationSequence_should_apply_all_mutations) { Chromosome chromosome("aaaaa"); - vector steps = Chromosome::genesToSteps("gfc"); - function mutation = mutationSequence({ + std::vector steps = Chromosome::genesToSteps("gfc"); + std::function mutation = mutationSequence({ geneSubstitution(3, steps[0]), geneSubstitution(2, steps[1]), geneSubstitution(1, steps[2]), @@ -273,8 +272,8 @@ BOOST_AUTO_TEST_CASE(mutationSequence_should_apply_all_mutations) BOOST_AUTO_TEST_CASE(mutationSequence_apply_mutations_in_the_order_they_are_given) { Chromosome chromosome("aa"); - vector steps = Chromosome::genesToSteps("gcfo"); - function mutation = mutationSequence({ + std::vector steps = Chromosome::genesToSteps("gcfo"); + std::function mutation = mutationSequence({ geneSubstitution(0, steps[0]), geneSubstitution(1, steps[1]), geneSubstitution(0, steps[2]), @@ -287,14 +286,14 @@ BOOST_AUTO_TEST_CASE(mutationSequence_apply_mutations_in_the_order_they_are_give BOOST_AUTO_TEST_CASE(mutationSequence_should_return_unmodified_chromosome_if_given_no_mutations) { Chromosome chromosome("aa"); - function mutation = mutationSequence({}); + std::function mutation = mutationSequence({}); BOOST_TEST(mutation(chromosome) == chromosome); } BOOST_AUTO_TEST_CASE(randomPointCrossover_should_swap_chromosome_parts_at_random_point) { - function crossover = randomPointCrossover(); + std::function crossover = randomPointCrossover(); SimulationRNG::reset(1); Chromosome result1 = crossover(Chromosome("aaaaaaaaaa"), Chromosome("cccccc")); @@ -307,22 +306,22 @@ BOOST_AUTO_TEST_CASE(randomPointCrossover_should_swap_chromosome_parts_at_random BOOST_AUTO_TEST_CASE(symmetricRandomPointCrossover_should_swap_chromosome_parts_at_random_point) { - function crossover = symmetricRandomPointCrossover(); + std::function crossover = symmetricRandomPointCrossover(); SimulationRNG::reset(1); - tuple result1 = crossover(Chromosome("aaaaaaaaaa"), Chromosome("cccccc")); - tuple expectedPair1 = {Chromosome("aaaccc"), Chromosome("cccaaaaaaa")}; + std::tuple result1 = crossover(Chromosome("aaaaaaaaaa"), Chromosome("cccccc")); + std::tuple expectedPair1 = {Chromosome("aaaccc"), Chromosome("cccaaaaaaa")}; BOOST_TEST(result1 == expectedPair1); - tuple result2 = crossover(Chromosome("cccccc"), Chromosome("aaaaaaaaaa")); - tuple expectedPair2 = {Chromosome("ccccccaaaa"), Chromosome("aaaaaa")}; + std::tuple result2 = crossover(Chromosome("cccccc"), Chromosome("aaaaaaaaaa")); + std::tuple expectedPair2 = {Chromosome("ccccccaaaa"), Chromosome("aaaaaa")}; BOOST_TEST(result2 == expectedPair2); } BOOST_AUTO_TEST_CASE(randomPointCrossover_should_only_consider_points_available_on_both_chromosomes) { SimulationRNG::reset(1); - function crossover = randomPointCrossover(); + std::function crossover = randomPointCrossover(); for (size_t i = 0; i < 30; ++i) { @@ -346,7 +345,7 @@ BOOST_AUTO_TEST_CASE(randomPointCrossover_should_only_consider_points_available_ BOOST_AUTO_TEST_CASE(randomPointCrossover_should_never_split_at_position_zero_if_chromosomes_are_splittable) { SimulationRNG::reset(1); - function crossover = randomPointCrossover(); + std::function crossover = randomPointCrossover(); for (size_t i = 0; i < 30; ++i) { @@ -360,7 +359,7 @@ BOOST_AUTO_TEST_CASE(randomPointCrossover_should_never_split_at_position_zero_if BOOST_AUTO_TEST_CASE(randomPointCrossover_should_never_split_at_position_zero_if_chromosomes_are_not_empty) { SimulationRNG::reset(1); - function crossover = randomPointCrossover(); + std::function crossover = randomPointCrossover(); for (size_t i = 0; i < 30; ++i) { @@ -373,7 +372,7 @@ BOOST_AUTO_TEST_CASE(randomPointCrossover_should_never_split_at_position_zero_if BOOST_AUTO_TEST_CASE(randomPointCrossover_should_work_even_if_one_chromosome_is_unsplittable) { - function crossover = randomPointCrossover(); + std::function crossover = randomPointCrossover(); SimulationRNG::reset(1); BOOST_CHECK(crossover(Chromosome("ff"), Chromosome("a")) == Chromosome("f")); @@ -385,7 +384,7 @@ BOOST_AUTO_TEST_CASE(randomPointCrossover_should_split_at_position_zero_only_if_ Chromosome empty(""); Chromosome unsplittable("a"); Chromosome splittable("aaaa"); - function crossover = randomPointCrossover(); + std::function crossover = randomPointCrossover(); SimulationRNG::reset(1); BOOST_CHECK(crossover(empty, empty) == empty); @@ -447,13 +446,13 @@ BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_split_at_end_of_shorter_chromoso BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_select_correct_split_point_for_unsplittable_chromosomes) { - function crossover00 = fixedPointCrossover(0.0); + std::function crossover00 = fixedPointCrossover(0.0); BOOST_CHECK(crossover00(Chromosome("fff"), Chromosome("a")) == Chromosome("a")); BOOST_CHECK(crossover00(Chromosome("a"), Chromosome("fff")) == Chromosome("fff")); BOOST_CHECK(crossover00(Chromosome("f"), Chromosome("a")) == Chromosome("a")); - function crossover10 = fixedPointCrossover(1.0); + std::function crossover10 = fixedPointCrossover(1.0); BOOST_CHECK(crossover10(Chromosome("fff"), Chromosome("a")) == Chromosome("f")); BOOST_CHECK(crossover10(Chromosome("a"), Chromosome("fff")) == Chromosome("aff")); @@ -466,14 +465,14 @@ BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_always_use_position_zero_as_spli Chromosome unsplittable("f"); Chromosome splittable("aaaa"); - function crossover00 = fixedPointCrossover(0.0); + std::function crossover00 = fixedPointCrossover(0.0); BOOST_CHECK(crossover00(empty, empty) == empty); BOOST_CHECK(crossover00(unsplittable, empty) == empty); BOOST_CHECK(crossover00(empty, unsplittable) == unsplittable); BOOST_CHECK(crossover00(splittable, empty) == empty); BOOST_CHECK(crossover00(empty, splittable) == splittable); - function crossover10 = fixedPointCrossover(1.0); + std::function crossover10 = fixedPointCrossover(1.0); BOOST_CHECK(crossover10(empty, empty) == empty); BOOST_CHECK(crossover10(unsplittable, empty) == empty); BOOST_CHECK(crossover10(empty, unsplittable) == unsplittable); @@ -483,7 +482,7 @@ BOOST_AUTO_TEST_CASE(fixedPointCrossover_should_always_use_position_zero_as_spli BOOST_AUTO_TEST_CASE(randomTwoPointCrossover_should_swap_chromosome_parts_between_two_random_points) { - function crossover = randomTwoPointCrossover(); + std::function crossover = randomTwoPointCrossover(); SimulationRNG::reset(1); Chromosome result1 = crossover(Chromosome("aaaaaaaaaa"), Chromosome("cccccc")); @@ -496,21 +495,21 @@ BOOST_AUTO_TEST_CASE(randomTwoPointCrossover_should_swap_chromosome_parts_betwee BOOST_AUTO_TEST_CASE(symmetricRandomTwoPointCrossover_should_swap_chromosome_parts_at_random_point) { - function crossover = symmetricRandomTwoPointCrossover(); + std::function crossover = symmetricRandomTwoPointCrossover(); SimulationRNG::reset(1); - tuple result1 = crossover(Chromosome("aaaaaaaaaa"), Chromosome("cccccc")); - tuple expectedPair1 = {Chromosome("aaacccaaaa"), Chromosome("cccaaa")}; + std::tuple result1 = crossover(Chromosome("aaaaaaaaaa"), Chromosome("cccccc")); + std::tuple expectedPair1 = {Chromosome("aaacccaaaa"), Chromosome("cccaaa")}; BOOST_TEST(result1 == expectedPair1); - tuple result2 = crossover(Chromosome("cccccc"), Chromosome("aaaaaaaaaa")); - tuple expectedPair2 = {Chromosome("ccccca"), Chromosome("aaaaacaaaa")}; + std::tuple result2 = crossover(Chromosome("cccccc"), Chromosome("aaaaaaaaaa")); + std::tuple expectedPair2 = {Chromosome("ccccca"), Chromosome("aaaaacaaaa")}; BOOST_TEST(result2 == expectedPair2); } BOOST_AUTO_TEST_CASE(randomTwoPointCrossover_should_only_consider_points_available_on_both_chromosomes) { - function crossover = randomTwoPointCrossover(); + std::function crossover = randomTwoPointCrossover(); for (size_t i = 0; i < 30; ++i) { @@ -539,7 +538,7 @@ BOOST_AUTO_TEST_CASE(randomTwoPointCrossover_should_only_consider_points_availab BOOST_AUTO_TEST_CASE(uniformCrossover_should_swap_randomly_selected_genes) { - function crossover = uniformCrossover(0.7); + std::function crossover = uniformCrossover(0.7); SimulationRNG::reset(1); Chromosome result1 = crossover(Chromosome("aaaaaaaaaa"), Chromosome("cccccc")); @@ -552,23 +551,23 @@ BOOST_AUTO_TEST_CASE(uniformCrossover_should_swap_randomly_selected_genes) BOOST_AUTO_TEST_CASE(symmetricUniformCrossover_should_swap_randomly_selected_genes) { - function crossover = symmetricUniformCrossover(0.7); + std::function crossover = symmetricUniformCrossover(0.7); SimulationRNG::reset(1); - tuple result1 = crossover(Chromosome("aaaaaaaaaa"), Chromosome("cccccc")); - tuple expectedPair1 = {Chromosome("caaacc"), Chromosome("acccaaaaaa")}; + std::tuple result1 = crossover(Chromosome("aaaaaaaaaa"), Chromosome("cccccc")); + std::tuple expectedPair1 = {Chromosome("caaacc"), Chromosome("acccaaaaaa")}; BOOST_TEST(result1 == expectedPair1); - tuple result2 = crossover(Chromosome("cccccc"), Chromosome("aaaaaaaaaa")); - tuple expectedPair2 = {Chromosome("caaaaaaaaa"), Chromosome("accccc")}; + std::tuple result2 = crossover(Chromosome("cccccc"), Chromosome("aaaaaaaaaa")); + std::tuple expectedPair2 = {Chromosome("caaaaaaaaa"), Chromosome("accccc")}; BOOST_TEST(result2 == expectedPair2); } BOOST_AUTO_TEST_CASE(uniformCrossover_should_only_consider_points_available_on_both_chromosomes) { - function crossover = uniformCrossover(0.7); + std::function crossover = uniformCrossover(0.7); - set expectedPatterns = { + std::set expectedPatterns = { "TTTTTTTTTTTTTTTTTTTT", "aTTTTTTTTTTTTTTTTTTT", "TaTTTTTTTTTTTTTTTTTT", @@ -616,20 +615,20 @@ BOOST_AUTO_TEST_CASE(uniformCrossover_should_swap_genes_with_uniform_probability double const expectedValue = swapChance; double const variance = swapChance * (1 - swapChance); - function crossover = uniformCrossover(swapChance); + std::function crossover = uniformCrossover(swapChance); Chromosome chromosome1("aaaaaaaaaa"); Chromosome chromosome2("cccccccccc"); - vector bernoulliTrials; + std::vector bernoulliTrials; for (size_t i = 0; i < operationCount; ++i) { - string genes = toString(crossover(chromosome1, chromosome2)); + std::string genes = toString(crossover(chromosome1, chromosome2)); for (size_t j = 0; j < chromosome1.length(); ++j) bernoulliTrials.push_back(static_cast(genes[j] == 'c')); } - BOOST_TEST(abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance); - BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance); + BOOST_TEST(std::abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(std::abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance); } BOOST_AUTO_TEST_CASE(uniformCrossover_should_swap_tail_with_uniform_probability) @@ -640,22 +639,22 @@ BOOST_AUTO_TEST_CASE(uniformCrossover_should_swap_tail_with_uniform_probability) double const expectedValue = swapChance; double const variance = swapChance * (1 - swapChance); - function crossover = uniformCrossover(swapChance); + std::function crossover = uniformCrossover(swapChance); Chromosome chromosome1("aaaaa"); Chromosome chromosome2("cccccccccc"); SimulationRNG::reset(1); - vector bernoulliTrials; + std::vector bernoulliTrials; for (size_t i = 0; i < operationCount; ++i) { - string genes = toString(crossover(chromosome1, chromosome2)); + std::string genes = toString(crossover(chromosome1, chromosome2)); BOOST_REQUIRE(genes.size() == 5 || genes.size() == 10); bernoulliTrials.push_back(static_cast(genes.size() == 10)); } - BOOST_TEST(abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance); - BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance); + BOOST_TEST(std::abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(std::abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/yulPhaser/PairSelections.cpp b/test/yulPhaser/PairSelections.cpp index f3d13a5b3b27..82b9a900b68c 100644 --- a/test/yulPhaser/PairSelections.cpp +++ b/test/yulPhaser/PairSelections.cpp @@ -27,8 +27,6 @@ #include #include -using namespace std; - namespace solidity::phaser::test { @@ -45,35 +43,35 @@ BOOST_AUTO_TEST_CASE(materialise_should_return_random_values_with_equal_probabil constexpr double variance = (collectionSize * collectionSize - 1) / 12.0; SimulationRNG::reset(1); - vector> pairs = RandomPairSelection(selectionSize).materialise(collectionSize); - vector samples; + std::vector> pairs = RandomPairSelection(selectionSize).materialise(collectionSize); + std::vector samples; for (auto& [first, second]: pairs) { samples.push_back(first); samples.push_back(second); } - BOOST_TEST(abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); - BOOST_TEST(abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); + BOOST_TEST(std::abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(std::abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); } BOOST_AUTO_TEST_CASE(materialise_should_return_only_values_that_can_be_used_as_collection_indices) { const size_t collectionSize = 200; - vector> pairs = RandomPairSelection(0.5).materialise(collectionSize); + std::vector> pairs = RandomPairSelection(0.5).materialise(collectionSize); BOOST_TEST(pairs.size() == 100); - BOOST_TEST(all_of(pairs.begin(), pairs.end(), [&](auto const& pair){ return get<0>(pair) <= collectionSize; })); - BOOST_TEST(all_of(pairs.begin(), pairs.end(), [&](auto const& pair){ return get<1>(pair) <= collectionSize; })); + BOOST_TEST(all_of(pairs.begin(), pairs.end(), [&](auto const& pair){ return std::get<0>(pair) <= collectionSize; })); + BOOST_TEST(all_of(pairs.begin(), pairs.end(), [&](auto const& pair){ return std::get<1>(pair) <= collectionSize; })); } BOOST_AUTO_TEST_CASE(materialise_should_never_return_a_pair_of_identical_indices) { - vector> pairs = RandomPairSelection(0.5).materialise(100); + std::vector> pairs = RandomPairSelection(0.5).materialise(100); BOOST_TEST(pairs.size() == 50); - BOOST_TEST(all_of(pairs.begin(), pairs.end(), [](auto const& pair){ return get<0>(pair) != get<1>(pair); })); + BOOST_TEST(all_of(pairs.begin(), pairs.end(), [](auto const& pair){ return std::get<0>(pair) != std::get<1>(pair); })); } BOOST_AUTO_TEST_CASE(materialise_should_return_number_of_pairs_thats_a_fraction_of_collection_size) @@ -132,18 +130,18 @@ BOOST_AUTO_TEST_CASE(materialise_should_return_random_values_with_equal_probabil constexpr double variance = selectionChance * (1 - selectionChance); SimulationRNG::reset(1); - vector> pairs = PairsFromRandomSubset(selectionChance).materialise(collectionSize); - vector bernoulliTrials(collectionSize, 0); + std::vector> pairs = PairsFromRandomSubset(selectionChance).materialise(collectionSize); + std::vector bernoulliTrials(collectionSize, 0); for (auto& pair: pairs) { - BOOST_REQUIRE(get<1>(pair) < collectionSize); - BOOST_REQUIRE(get<1>(pair) < collectionSize); - bernoulliTrials[get<0>(pair)] = 1.0; - bernoulliTrials[get<1>(pair)] = 1.0; + BOOST_REQUIRE(std::get<1>(pair) < collectionSize); + BOOST_REQUIRE(std::get<1>(pair) < collectionSize); + bernoulliTrials[std::get<0>(pair)] = 1.0; + bernoulliTrials[std::get<1>(pair)] = 1.0; } - BOOST_TEST(abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance); - BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance); + BOOST_TEST(std::abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(std::abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance); } BOOST_AUTO_TEST_CASE(materialise_should_return_only_values_that_can_be_used_as_collection_indices) @@ -151,10 +149,10 @@ BOOST_AUTO_TEST_CASE(materialise_should_return_only_values_that_can_be_used_as_c const size_t collectionSize = 200; constexpr double selectionChance = 0.5; - vector> pairs = PairsFromRandomSubset(selectionChance).materialise(collectionSize); + std::vector> pairs = PairsFromRandomSubset(selectionChance).materialise(collectionSize); - BOOST_TEST(all_of(pairs.begin(), pairs.end(), [&](auto const& pair){ return get<0>(pair) <= collectionSize; })); - BOOST_TEST(all_of(pairs.begin(), pairs.end(), [&](auto const& pair){ return get<1>(pair) <= collectionSize; })); + BOOST_TEST(all_of(pairs.begin(), pairs.end(), [&](auto const& pair){ return std::get<0>(pair) <= collectionSize; })); + BOOST_TEST(all_of(pairs.begin(), pairs.end(), [&](auto const& pair){ return std::get<1>(pair) <= collectionSize; })); } BOOST_AUTO_TEST_CASE(materialise_should_use_unique_indices) @@ -162,12 +160,12 @@ BOOST_AUTO_TEST_CASE(materialise_should_use_unique_indices) constexpr size_t collectionSize = 200; constexpr double selectionChance = 0.5; - vector> pairs = PairsFromRandomSubset(selectionChance).materialise(collectionSize); - set indices; + std::vector> pairs = PairsFromRandomSubset(selectionChance).materialise(collectionSize); + std::set indices; for (auto& pair: pairs) { - indices.insert(get<0>(pair)); - indices.insert(get<1>(pair)); + indices.insert(std::get<0>(pair)); + indices.insert(std::get<1>(pair)); } BOOST_TEST(indices.size() == 2 * pairs.size()); @@ -195,7 +193,7 @@ BOOST_AUTO_TEST_CASE(materialise_should_return_all_pairs_if_selection_chance_is_ BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(PairMosaicSelectionTest) -using IndexPairs = vector>; +using IndexPairs = std::vector>; BOOST_AUTO_TEST_CASE(materialise) { diff --git a/test/yulPhaser/Phaser.cpp b/test/yulPhaser/Phaser.cpp index feee2269e761..671f75546305 100644 --- a/test/yulPhaser/Phaser.cpp +++ b/test/yulPhaser/Phaser.cpp @@ -32,7 +32,6 @@ #include #include -using namespace std; using namespace solidity::util; using namespace solidity::langutil; using namespace solidity::yul; @@ -69,15 +68,15 @@ class GeneticAlgorithmFactoryFixture class FixtureWithPrograms { protected: - vector m_sourceStreams = { + std::vector m_sourceStreams = { CharStream("{}", ""), CharStream("{{}}", ""), CharStream("{{{}}}", ""), }; - vector m_programs = { - get(Program::load(m_sourceStreams[0])), - get(Program::load(m_sourceStreams[1])), - get(Program::load(m_sourceStreams[2])), + std::vector m_programs = { + std::get(Program::load(m_sourceStreams[0])), + std::get(Program::load(m_sourceStreams[1])), + std::get(Program::load(m_sourceStreams[2])), }; }; @@ -96,7 +95,7 @@ class FitnessMetricFactoryFixture: public FixtureWithPrograms class PoulationFactoryFixture { protected: - shared_ptr m_fitnessMetric = make_shared(); + std::shared_ptr m_fitnessMetric = std::make_shared(); PopulationFactory::Options m_options = { /* minChromosomeLength = */ 0, /* maxChromosomeLength = */ 0, @@ -113,7 +112,7 @@ BOOST_AUTO_TEST_SUITE(GeneticAlgorithmFactoryTest) BOOST_FIXTURE_TEST_CASE(build_should_select_the_right_algorithm_and_pass_the_options_to_it, GeneticAlgorithmFactoryFixture) { m_options.algorithm = Algorithm::Random; - unique_ptr algorithm1 = GeneticAlgorithmFactory::build(m_options, 100); + std::unique_ptr algorithm1 = GeneticAlgorithmFactory::build(m_options, 100); BOOST_REQUIRE(algorithm1 != nullptr); auto randomAlgorithm = dynamic_cast(algorithm1.get()); @@ -123,7 +122,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_select_the_right_algorithm_and_pass_the_opt BOOST_TEST(randomAlgorithm->options().maxChromosomeLength == m_options.maxChromosomeLength); m_options.algorithm = Algorithm::GEWEP; - unique_ptr algorithm2 = GeneticAlgorithmFactory::build(m_options, 100); + std::unique_ptr algorithm2 = GeneticAlgorithmFactory::build(m_options, 100); BOOST_REQUIRE(algorithm2 != nullptr); auto gewepAlgorithm = dynamic_cast(algorithm2.get()); @@ -139,7 +138,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_select_the_right_algorithm_and_pass_the_opt BOOST_TEST(gewepAlgorithm->options().percentGenesToAddOrDelete == m_options.gewepGenesToAddOrDelete.value()); m_options.algorithm = Algorithm::Classic; - unique_ptr algorithm3 = GeneticAlgorithmFactory::build(m_options, 100); + std::unique_ptr algorithm3 = GeneticAlgorithmFactory::build(m_options, 100); BOOST_REQUIRE(algorithm3 != nullptr); auto classicAlgorithm = dynamic_cast(algorithm3.get()); @@ -156,8 +155,8 @@ BOOST_FIXTURE_TEST_CASE(build_should_select_the_right_algorithm_and_pass_the_opt BOOST_FIXTURE_TEST_CASE(build_should_set_random_algorithm_elite_pool_size_based_on_population_size_if_not_specified, GeneticAlgorithmFactoryFixture) { m_options.algorithm = Algorithm::Random; - m_options.randomElitePoolSize = nullopt; - unique_ptr algorithm = GeneticAlgorithmFactory::build(m_options, 100); + m_options.randomElitePoolSize = std::nullopt; + std::unique_ptr algorithm = GeneticAlgorithmFactory::build(m_options, 100); BOOST_REQUIRE(algorithm != nullptr); auto randomAlgorithm = dynamic_cast(algorithm.get()); @@ -168,11 +167,11 @@ BOOST_FIXTURE_TEST_CASE(build_should_set_random_algorithm_elite_pool_size_based_ BOOST_FIXTURE_TEST_CASE(build_should_set_gewep_mutation_percentages_based_on_maximum_chromosome_length_if_not_specified, GeneticAlgorithmFactoryFixture) { m_options.algorithm = Algorithm::GEWEP; - m_options.gewepGenesToRandomise = nullopt; - m_options.gewepGenesToAddOrDelete = nullopt; + m_options.gewepGenesToRandomise = std::nullopt; + m_options.gewepGenesToAddOrDelete = std::nullopt; m_options.maxChromosomeLength = 125; - unique_ptr algorithm = GeneticAlgorithmFactory::build(m_options, 100); + std::unique_ptr algorithm = GeneticAlgorithmFactory::build(m_options, 100); BOOST_REQUIRE(algorithm != nullptr); auto gewepAlgorithm = dynamic_cast(algorithm.get()); @@ -188,7 +187,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_create_metric_of_the_right_type, FitnessMet { m_options.metric = MetricChoice::RelativeCodeSize; m_options.metricAggregator = MetricAggregatorChoice::Sum; - unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}, m_weights); + std::unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}, m_weights); BOOST_REQUIRE(metric != nullptr); auto sumMetric = dynamic_cast(metric.get()); @@ -206,7 +205,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_respect_chromosome_repetitions_option, Fitn m_options.metric = MetricChoice::CodeSize; m_options.metricAggregator = MetricAggregatorChoice::Average; m_options.chromosomeRepetitions = 5; - unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}, m_weights); + std::unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}, m_weights); BOOST_REQUIRE(metric != nullptr); auto averageMetric = dynamic_cast(metric.get()); @@ -224,7 +223,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_set_relative_metric_scale, FitnessMetricFac m_options.metric = MetricChoice::RelativeCodeSize; m_options.metricAggregator = MetricAggregatorChoice::Average; m_options.relativeMetricScale = 10; - unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}, m_weights); + std::unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}, m_weights); BOOST_REQUIRE(metric != nullptr); auto averageMetric = dynamic_cast(metric.get()); @@ -239,10 +238,10 @@ BOOST_FIXTURE_TEST_CASE(build_should_set_relative_metric_scale, FitnessMetricFac BOOST_FIXTURE_TEST_CASE(build_should_create_metric_for_each_input_program, FitnessMetricFactoryFixture) { - unique_ptr metric = FitnessMetricFactory::build( + std::unique_ptr metric = FitnessMetricFactory::build( m_options, m_programs, - vector>(m_programs.size(), nullptr), + std::vector>(m_programs.size(), nullptr), m_weights ); BOOST_REQUIRE(metric != nullptr); @@ -255,14 +254,14 @@ BOOST_FIXTURE_TEST_CASE(build_should_create_metric_for_each_input_program, Fitne BOOST_FIXTURE_TEST_CASE(build_should_pass_program_caches_to_metrics, FitnessMetricFactoryFixture) { assert(m_programs.size() == 3); - vector> caches = { - make_shared(m_programs[0]), - make_shared(m_programs[1]), - make_shared(m_programs[2]), + std::vector> caches = { + std::make_shared(m_programs[0]), + std::make_shared(m_programs[1]), + std::make_shared(m_programs[2]), }; m_options.metric = MetricChoice::RelativeCodeSize; - unique_ptr metric = FitnessMetricFactory::build(m_options, m_programs, caches, m_weights); + std::unique_ptr metric = FitnessMetricFactory::build(m_options, m_programs, caches, m_weights); BOOST_REQUIRE(metric != nullptr); auto combinedMetric = dynamic_cast(metric.get()); @@ -287,7 +286,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_create_an_empty_population_if_no_specific_o m_options.populationFromFile = {}; BOOST_TEST( PopulationFactory::build(m_options, m_fitnessMetric) == - Population(m_fitnessMetric, vector{}) + Population(m_fitnessMetric, std::vector{}) ); } @@ -318,7 +317,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_respect_random_population_option, Poulation BOOST_FIXTURE_TEST_CASE(build_should_respect_population_from_file_option, PoulationFactoryFixture) { - map> fileContent = { + std::map> fileContent = { {"a.txt", {"a", "fff", "", "jxccLTa"}}, {"b.txt", {}}, {"c.txt", {""}}, @@ -328,9 +327,9 @@ BOOST_FIXTURE_TEST_CASE(build_should_respect_population_from_file_option, Poulat TemporaryDirectory tempDir; for (auto const& [fileName, chromosomes]: fileContent) { - ofstream tmpFile((tempDir.path() / fileName).string()); + std::ofstream tmpFile((tempDir.path() / fileName).string()); for (auto const& chromosome: chromosomes) - tmpFile << chromosome << endl; + tmpFile << chromosome << std::endl; m_options.populationFromFile.push_back((tempDir.path() / fileName).string()); } @@ -361,8 +360,8 @@ BOOST_FIXTURE_TEST_CASE(build_should_combine_populations_from_all_sources, Poula { TemporaryDirectory tempDir; { - ofstream tmpFile((tempDir.path() / "population.txt").string()); - tmpFile << "axc" << endl << "fcL" << endl; + std::ofstream tmpFile((tempDir.path() / "population.txt").string()); + tmpFile << "axc" << std::endl << "fcL" << std::endl; } m_options.population = {"axc", "fcL"}; @@ -388,7 +387,7 @@ BOOST_AUTO_TEST_SUITE(ProgramCacheFactoryTest) BOOST_FIXTURE_TEST_CASE(build_should_create_cache_for_each_input_program_if_cache_enabled, FixtureWithPrograms) { ProgramCacheFactory::Options options{/* programCacheEnabled = */ true}; - vector> caches = ProgramCacheFactory::build(options, m_programs); + std::vector> caches = ProgramCacheFactory::build(options, m_programs); assert(m_programs.size() >= 2 && "There must be at least 2 programs for this test to be meaningful"); BOOST_TEST(caches.size() == m_programs.size()); @@ -402,7 +401,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_create_cache_for_each_input_program_if_cach BOOST_FIXTURE_TEST_CASE(build_should_return_nullptr_for_each_input_program_if_cache_disabled, FixtureWithPrograms) { ProgramCacheFactory::Options options{/* programCacheEnabled = */ false}; - vector> caches = ProgramCacheFactory::build(options, m_programs); + std::vector> caches = ProgramCacheFactory::build(options, m_programs); assert(m_programs.size() >= 2 && "There must be at least 2 programs for this test to be meaningful"); BOOST_TEST(caches.size() == m_programs.size()); @@ -416,7 +415,7 @@ BOOST_AUTO_TEST_SUITE(ProgramFactoryTest) BOOST_AUTO_TEST_CASE(build_should_load_programs_from_files) { TemporaryDirectory tempDir; - vector sources{"{}", "{{}}", "{{{}}}"}; + std::vector sources{"{}", "{{}}", "{{{}}}"}; ProgramFactory::Options options{ /* inputFiles = */ { (tempDir.path() / "program1.yul").string(), @@ -428,17 +427,17 @@ BOOST_AUTO_TEST_CASE(build_should_load_programs_from_files) for (size_t i = 0; i < sources.size(); ++i) { - ofstream tmpFile(options.inputFiles[i]); - tmpFile << sources[i] << endl; + std::ofstream tmpFile(options.inputFiles[i]); + tmpFile << sources[i] << std::endl; } - vector programs = ProgramFactory::build(options); + std::vector programs = ProgramFactory::build(options); BOOST_TEST(programs.size() == sources.size()); for (size_t i = 0; i < sources.size(); ++i) { CharStream sourceStream(sources[i], options.inputFiles[i]); - BOOST_TEST(toString(programs[i]) == toString(get(Program::load(sourceStream)))); + BOOST_TEST(toString(programs[i]) == toString(std::get(Program::load(sourceStream)))); } } @@ -451,17 +450,17 @@ BOOST_AUTO_TEST_CASE(build_should_apply_prefix) }; CharStream nestedSource("{{{let x:= 1}}}", ""); - Program nestedProgram = get(Program::load(nestedSource)); - Program flatProgram = get(Program::load(nestedSource)); + Program nestedProgram = std::get(Program::load(nestedSource)); + Program flatProgram = std::get(Program::load(nestedSource)); flatProgram.optimise(Chromosome::genesToSteps("f")); assert(toString(nestedProgram) != toString(flatProgram)); { - ofstream tmpFile(options.inputFiles[0]); - tmpFile << nestedSource.source() << endl; + std::ofstream tmpFile(options.inputFiles[0]); + tmpFile << nestedSource.source() << std::endl; } - vector programs = ProgramFactory::build(options); + std::vector programs = ProgramFactory::build(options); BOOST_TEST(programs.size() == 1); BOOST_TEST(toString(programs[0]) == toString(flatProgram)); diff --git a/test/yulPhaser/Population.cpp b/test/yulPhaser/Population.cpp index 13d06b521030..aed014592c1b 100644 --- a/test/yulPhaser/Population.cpp +++ b/test/yulPhaser/Population.cpp @@ -38,7 +38,6 @@ #include #include -using namespace std; using namespace solidity::langutil; using namespace solidity::yul; using namespace boost::unit_test::framework; @@ -52,12 +51,12 @@ class PopulationFixture static ChromosomePair twoStepSwap(Chromosome const& _chromosome1, Chromosome const& _chromosome2) { return ChromosomePair{ - Chromosome(vector{_chromosome1.optimisationSteps()[0], _chromosome2.optimisationSteps()[1]}), - Chromosome(vector{_chromosome2.optimisationSteps()[0], _chromosome1.optimisationSteps()[1]}), + Chromosome(std::vector{_chromosome1.optimisationSteps()[0], _chromosome2.optimisationSteps()[1]}), + Chromosome(std::vector{_chromosome2.optimisationSteps()[0], _chromosome1.optimisationSteps()[1]}), }; } - shared_ptr m_fitnessMetric = make_shared(); + std::shared_ptr m_fitnessMetric = std::make_shared(); }; BOOST_AUTO_TEST_SUITE(Phaser, *boost::unit_test::label("nooptions")) @@ -95,14 +94,14 @@ BOOST_AUTO_TEST_CASE(isFitter_should_return_false_for_identical_individuals) BOOST_FIXTURE_TEST_CASE(constructor_should_copy_chromosomes_compute_fitness_and_sort_chromosomes, PopulationFixture) { - vector chromosomes = { + std::vector chromosomes = { Chromosome::makeRandom(5), Chromosome::makeRandom(15), Chromosome::makeRandom(10), }; Population population(m_fitnessMetric, chromosomes); - vector const& individuals = population.individuals(); + std::vector const& individuals = population.individuals(); BOOST_TEST(individuals.size() == 3); BOOST_TEST(individuals[0].fitness == 5); @@ -115,7 +114,7 @@ BOOST_FIXTURE_TEST_CASE(constructor_should_copy_chromosomes_compute_fitness_and_ BOOST_FIXTURE_TEST_CASE(constructor_should_accept_individuals_without_recalculating_fitness, PopulationFixture) { - vector customIndividuals = { + std::vector customIndividuals = { Individual(Chromosome("aaaccc"), 20), Individual(Chromosome("aaa"), 10), Individual(Chromosome("aaaf"), 30), @@ -126,7 +125,7 @@ BOOST_FIXTURE_TEST_CASE(constructor_should_accept_individuals_without_recalculat Population population(m_fitnessMetric, customIndividuals); - vector expectedIndividuals{customIndividuals[1], customIndividuals[0], customIndividuals[2]}; + std::vector expectedIndividuals{customIndividuals[1], customIndividuals[0], customIndividuals[2]}; BOOST_TEST(population.individuals() == expectedIndividuals); } @@ -170,13 +169,13 @@ BOOST_FIXTURE_TEST_CASE(makeRandom_should_use_random_chromosome_length, Populati constexpr double relativeTolerance = 0.05; auto population = Population::makeRandom(m_fitnessMetric, populationSize, minLength, maxLength); - vector samples = chromosomeLengths(population); + std::vector samples = chromosomeLengths(population); const double expectedValue = (maxLength + minLength) / 2.0; const double variance = ((maxLength - minLength + 1) * (maxLength - minLength + 1) - 1) / 12.0; - BOOST_TEST(abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); - BOOST_TEST(abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); + BOOST_TEST(std::abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(std::abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); } BOOST_FIXTURE_TEST_CASE(makeRandom_should_return_population_with_random_chromosomes, PopulationFixture) @@ -186,10 +185,10 @@ BOOST_FIXTURE_TEST_CASE(makeRandom_should_return_population_with_random_chromoso constexpr int chromosomeLength = 30; constexpr double relativeTolerance = 0.01; - map stepIndices = enumerateOptmisationSteps(); + std::map stepIndices = enumerateOptmisationSteps(); auto population = Population::makeRandom(m_fitnessMetric, populationSize, chromosomeLength, chromosomeLength); - vector samples; + std::vector samples; for (auto& individual: population.individuals()) for (auto& step: individual.chromosome.optimisationSteps()) samples.push_back(stepIndices.at(step)); @@ -197,8 +196,8 @@ BOOST_FIXTURE_TEST_CASE(makeRandom_should_return_population_with_random_chromoso const double expectedValue = double(stepIndices.size() - 1) / 2.0; const double variance = double(stepIndices.size() * stepIndices.size() - 1) / 12.0; - BOOST_TEST(abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); - BOOST_TEST(abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); + BOOST_TEST(std::abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(std::abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); } BOOST_FIXTURE_TEST_CASE(makeRandom_should_compute_fitness, PopulationFixture) @@ -223,7 +222,7 @@ BOOST_FIXTURE_TEST_CASE(select_should_return_population_containing_individuals_i { Population population(m_fitnessMetric, {Chromosome("a"), Chromosome("c"), Chromosome("g"), Chromosome("h")}); RangeSelection selection(0.25, 0.75); - assert(selection.materialise(population.individuals().size()) == (vector{1, 2})); + assert(selection.materialise(population.individuals().size()) == (std::vector{1, 2})); BOOST_TEST( population.select(selection) == @@ -235,7 +234,7 @@ BOOST_FIXTURE_TEST_CASE(select_should_include_duplicates_if_selection_contains_d { Population population(m_fitnessMetric, {Chromosome("a"), Chromosome("c")}); MosaicSelection selection({0, 1}, 2.0); - assert(selection.materialise(population.individuals().size()) == (vector{0, 1, 0, 1})); + assert(selection.materialise(population.individuals().size()) == (std::vector{0, 1, 0, 1})); BOOST_TEST(population.select(selection) == Population(m_fitnessMetric, { population.individuals()[0].chromosome, @@ -258,7 +257,7 @@ BOOST_FIXTURE_TEST_CASE(mutate_should_return_population_containing_individuals_i { Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc"), Chromosome("gg"), Chromosome("hh")}); RangeSelection selection(0.25, 0.75); - assert(selection.materialise(population.individuals().size()) == (vector{1, 2})); + assert(selection.materialise(population.individuals().size()) == (std::vector{1, 2})); Population expectedPopulation(m_fitnessMetric, {Chromosome("fc"), Chromosome("fg")}); @@ -269,7 +268,7 @@ BOOST_FIXTURE_TEST_CASE(mutate_should_include_duplicates_if_selection_contains_d { Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("aa")}); RangeSelection selection(0.0, 1.0); - assert(selection.materialise(population.individuals().size()) == (vector{0, 1})); + assert(selection.materialise(population.individuals().size()) == (std::vector{0, 1})); BOOST_TEST( population.mutate(selection, geneSubstitution(0, BlockFlattener::name)) == @@ -290,7 +289,7 @@ BOOST_FIXTURE_TEST_CASE(crossover_should_return_population_containing_individual { Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc"), Chromosome("gg"), Chromosome("hh")}); PairMosaicSelection selection({{0, 1}, {2, 1}}, 1.0); - assert(selection.materialise(population.individuals().size()) == (vector>{{0, 1}, {2, 1}, {0, 1}, {2, 1}})); + assert(selection.materialise(population.individuals().size()) == (std::vector>{{0, 1}, {2, 1}, {0, 1}, {2, 1}})); Population expectedPopulation(m_fitnessMetric, {Chromosome("ac"), Chromosome("ac"), Chromosome("gc"), Chromosome("gc")}); @@ -301,7 +300,7 @@ BOOST_FIXTURE_TEST_CASE(crossover_should_include_duplicates_if_selection_contain { Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("aa")}); PairMosaicSelection selection({{0, 0}, {1, 1}}, 2.0); - assert(selection.materialise(population.individuals().size()) == (vector>{{0, 0}, {1, 1}, {0, 0}, {1, 1}})); + assert(selection.materialise(population.individuals().size()) == (std::vector>{{0, 0}, {1, 1}, {0, 0}, {1, 1}})); BOOST_TEST( population.crossover(selection, fixedPointCrossover(0.5)) == @@ -322,14 +321,14 @@ BOOST_FIXTURE_TEST_CASE(symmetricCrossoverWithRemainder_should_return_crossed_po { Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc"), Chromosome("gg"), Chromosome("hh")}); PairMosaicSelection selection({{2, 1}}, 0.25); - assert(selection.materialise(population.individuals().size()) == (vector>{{2, 1}})); + assert(selection.materialise(population.individuals().size()) == (std::vector>{{2, 1}})); Population expectedCrossedPopulation(m_fitnessMetric, {Chromosome("gc"), Chromosome("cg")}); Population expectedRemainder(m_fitnessMetric, {Chromosome("aa"), Chromosome("hh")}); BOOST_TEST( population.symmetricCrossoverWithRemainder(selection, twoStepSwap) == - (tuple{expectedCrossedPopulation, expectedRemainder}) + (std::tuple{expectedCrossedPopulation, expectedRemainder}) ); } @@ -337,7 +336,7 @@ BOOST_FIXTURE_TEST_CASE(symmetricCrossoverWithRemainder_should_allow_crossing_th { Population population(m_fitnessMetric, {Chromosome("aa"), Chromosome("cc"), Chromosome("gg"), Chromosome("hh")}); PairMosaicSelection selection({{0, 0}, {2, 1}}, 1.0); - assert(selection.materialise(population.individuals().size()) == (vector>{{0, 0}, {2, 1}, {0, 0}, {2, 1}})); + assert(selection.materialise(population.individuals().size()) == (std::vector>{{0, 0}, {2, 1}, {0, 0}, {2, 1}})); Population expectedCrossedPopulation(m_fitnessMetric, { Chromosome("aa"), Chromosome("aa"), @@ -349,7 +348,7 @@ BOOST_FIXTURE_TEST_CASE(symmetricCrossoverWithRemainder_should_allow_crossing_th BOOST_TEST( population.symmetricCrossoverWithRemainder(selection, twoStepSwap) == - (tuple{expectedCrossedPopulation, expectedRemainder}) + (std::tuple{expectedCrossedPopulation, expectedRemainder}) ); } @@ -361,7 +360,7 @@ BOOST_FIXTURE_TEST_CASE(symmetricCrossoverWithRemainder_should_return_empty_popu BOOST_TEST( population.symmetricCrossoverWithRemainder(selection, twoStepSwap) == - (tuple{Population(m_fitnessMetric), population}) + (std::tuple{Population(m_fitnessMetric), population}) ); } diff --git a/test/yulPhaser/Program.cpp b/test/yulPhaser/Program.cpp index 2e91926d059f..a54bf09af146 100644 --- a/test/yulPhaser/Program.cpp +++ b/test/yulPhaser/Program.cpp @@ -34,7 +34,6 @@ #include #include -using namespace std; using namespace solidity::langutil; using namespace solidity::util; using namespace solidity::yul; @@ -47,7 +46,7 @@ namespace /// If the block isn't redundant it just returns it immediately. Block const& skipRedundantBlocks(Block const& _block) { - if (_block.statements.size() == 1 && holds_alternative(_block.statements[0])) + if (_block.statements.size() == 1 && std::holds_alternative(_block.statements[0])) return skipRedundantBlocks(get(_block.statements[0])); else return _block; @@ -62,7 +61,7 @@ BOOST_AUTO_TEST_SUITE(ProgramTest) BOOST_AUTO_TEST_CASE(copy_constructor_should_make_deep_copy_of_ast) { - string sourceCode( + std::string sourceCode( "{\n" " let x := 1\n" "}\n" @@ -82,7 +81,7 @@ BOOST_AUTO_TEST_CASE(copy_constructor_should_make_deep_copy_of_ast) BOOST_AUTO_TEST_CASE(load_should_rewind_the_stream) { - string sourceCode( + std::string sourceCode( "{\n" " let x := 1\n" " let y := 2\n" @@ -98,7 +97,7 @@ BOOST_AUTO_TEST_CASE(load_should_rewind_the_stream) BOOST_AUTO_TEST_CASE(load_should_disambiguate) { - string sourceCode( + std::string sourceCode( "{\n" " {\n" " let x := 1\n" @@ -126,7 +125,7 @@ BOOST_AUTO_TEST_CASE(load_should_disambiguate) BOOST_AUTO_TEST_CASE(load_should_do_function_grouping_and_hoisting) { - string sourceCode( + std::string sourceCode( "{\n" " function foo() -> result\n" " {\n" @@ -144,14 +143,14 @@ BOOST_AUTO_TEST_CASE(load_should_do_function_grouping_and_hoisting) Program program = get(Program::load(sourceStream)); BOOST_TEST(program.ast().statements.size() == 3); - BOOST_TEST(holds_alternative(program.ast().statements[0])); - BOOST_TEST(holds_alternative(program.ast().statements[1])); - BOOST_TEST(holds_alternative(program.ast().statements[2])); + BOOST_TEST(std::holds_alternative(program.ast().statements[0])); + BOOST_TEST(std::holds_alternative(program.ast().statements[1])); + BOOST_TEST(std::holds_alternative(program.ast().statements[2])); } BOOST_AUTO_TEST_CASE(load_should_do_loop_init_rewriting) { - string sourceCode( + std::string sourceCode( "{\n" " for { let i := 0 } true {}\n" " {\n" @@ -163,35 +162,35 @@ BOOST_AUTO_TEST_CASE(load_should_do_loop_init_rewriting) // skipRedundantBlocks() makes the test independent of whether load() includes function grouping or not. Block const& parentBlock = skipRedundantBlocks(program.ast()); - BOOST_TEST(holds_alternative(parentBlock.statements[0])); - BOOST_TEST(holds_alternative(parentBlock.statements[1])); + BOOST_TEST(std::holds_alternative(parentBlock.statements[0])); + BOOST_TEST(std::holds_alternative(parentBlock.statements[1])); } BOOST_AUTO_TEST_CASE(load_should_throw_InvalidProgram_if_program_cant_be_parsed) { - string sourceCode("invalid program\n"); + std::string sourceCode("invalid program\n"); CharStream sourceStream(sourceCode, current_test_case().p_name); - BOOST_TEST(holds_alternative(Program::load(sourceStream))); + BOOST_TEST(std::holds_alternative(Program::load(sourceStream))); } BOOST_AUTO_TEST_CASE(load_should_throw_InvalidProgram_if_program_cant_be_analyzed) { // This should be parsed just fine but fail the analysis with: // Error: Variable not found or variable not lvalue. - string sourceCode( + std::string sourceCode( "{\n" " x := 1\n" "}\n" ); CharStream sourceStream(sourceCode, current_test_case().p_name); - BOOST_TEST(holds_alternative(Program::load(sourceStream))); + BOOST_TEST(std::holds_alternative(Program::load(sourceStream))); } BOOST_AUTO_TEST_CASE(load_should_accept_yul_objects_as_input) { - string sourceCode( + std::string sourceCode( "object \"C_178\" {\n" " code {\n" " mstore(64, 128)\n" @@ -203,12 +202,12 @@ BOOST_AUTO_TEST_CASE(load_should_accept_yul_objects_as_input) CharStream sourceStream(sourceCode, current_test_case().p_name); auto programOrErrors = Program::load(sourceStream); - BOOST_TEST(holds_alternative(programOrErrors)); + BOOST_TEST(std::holds_alternative(programOrErrors)); } BOOST_AUTO_TEST_CASE(load_should_return_errors_if_analysis_of_object_code_fails) { - string sourceCode( + std::string sourceCode( "object \"C_178\" {\n" " code {\n" " return(0, datasize(\"C_178_deployed\"))\n" @@ -218,12 +217,12 @@ BOOST_AUTO_TEST_CASE(load_should_return_errors_if_analysis_of_object_code_fails) CharStream sourceStream(sourceCode, current_test_case().p_name); auto programOrErrors = Program::load(sourceStream); - BOOST_TEST(holds_alternative(programOrErrors)); + BOOST_TEST(std::holds_alternative(programOrErrors)); } BOOST_AUTO_TEST_CASE(load_should_return_errors_if_parsing_of_nested_object_fails) { - string sourceCode( + std::string sourceCode( "object \"C_178\" {\n" " code {\n" " return(0, datasize(\"C_178_deployed\"))\n" @@ -243,12 +242,12 @@ BOOST_AUTO_TEST_CASE(load_should_return_errors_if_parsing_of_nested_object_fails CharStream sourceStream(sourceCode, current_test_case().p_name); auto programOrErrors = Program::load(sourceStream); - BOOST_TEST(holds_alternative(programOrErrors)); + BOOST_TEST(std::holds_alternative(programOrErrors)); } BOOST_AUTO_TEST_CASE(load_should_extract_nested_object_with_deployed_suffix_if_present) { - string sourceCode( + std::string sourceCode( "object \"C_178\" {\n" " code {\n" " return(0, datasize(\"C_178_deployed\"))\n" @@ -265,12 +264,12 @@ BOOST_AUTO_TEST_CASE(load_should_extract_nested_object_with_deployed_suffix_if_p CharStream sourceStream(sourceCode, current_test_case().p_name); auto programOrErrors = Program::load(sourceStream); - BOOST_TEST(holds_alternative(programOrErrors)); + BOOST_TEST(std::holds_alternative(programOrErrors)); } BOOST_AUTO_TEST_CASE(load_should_fall_back_to_parsing_the_whole_object_if_there_is_no_subobject_with_the_right_name) { - string sourceCode( + std::string sourceCode( "object \"C_178\" {\n" " code {\n" " mstore(64, 128)\n" @@ -292,16 +291,16 @@ BOOST_AUTO_TEST_CASE(load_should_fall_back_to_parsing_the_whole_object_if_there_ CharStream sourceStream(sourceCode, current_test_case().p_name); auto programOrErrors = Program::load(sourceStream); - BOOST_TEST(holds_alternative(programOrErrors)); + BOOST_TEST(std::holds_alternative(programOrErrors)); Block const& parentBlock = skipRedundantBlocks(get(programOrErrors).ast()); BOOST_TEST(parentBlock.statements.size() == 1); - BOOST_TEST(holds_alternative(parentBlock.statements[0])); + BOOST_TEST(std::holds_alternative(parentBlock.statements[0])); } BOOST_AUTO_TEST_CASE(load_should_ignore_data_in_objects) { - string sourceCode( + std::string sourceCode( "object \"C_178\" {\n" " code {\n" " mstore(64, 128)\n" @@ -312,12 +311,12 @@ BOOST_AUTO_TEST_CASE(load_should_ignore_data_in_objects) CharStream sourceStream(sourceCode, current_test_case().p_name); auto programOrErrors = Program::load(sourceStream); - BOOST_TEST(holds_alternative(programOrErrors)); + BOOST_TEST(std::holds_alternative(programOrErrors)); } BOOST_AUTO_TEST_CASE(optimise) { - string sourceCode( + std::string sourceCode( "{\n" " {\n" " if 1 { let x := 1 }\n" @@ -330,22 +329,22 @@ BOOST_AUTO_TEST_CASE(optimise) [[maybe_unused]] Block const& parentBlockBefore = skipRedundantBlocks(program.ast()); assert(parentBlockBefore.statements.size() == 2); - assert(holds_alternative(parentBlockBefore.statements[0])); - assert(holds_alternative(parentBlockBefore.statements[1])); + assert(std::holds_alternative(parentBlockBefore.statements[0])); + assert(std::holds_alternative(parentBlockBefore.statements[1])); program.optimise({StructuralSimplifier::name, BlockFlattener::name}); Block const& parentBlockAfter = program.ast(); BOOST_TEST(parentBlockAfter.statements.size() == 1); - BOOST_TEST(holds_alternative(parentBlockAfter.statements[0])); + BOOST_TEST(std::holds_alternative(parentBlockAfter.statements[0])); Block const& innerBlock = get(parentBlockAfter.statements[0]); BOOST_TEST(innerBlock.statements.size() == 1); - BOOST_TEST(holds_alternative(innerBlock.statements[0])); + BOOST_TEST(std::holds_alternative(innerBlock.statements[0])); } BOOST_AUTO_TEST_CASE(output_operator) { - string sourceCode( + std::string sourceCode( "{\n" " let factor := 13\n" " {\n" @@ -369,7 +368,7 @@ BOOST_AUTO_TEST_CASE(output_operator) BOOST_AUTO_TEST_CASE(toJson) { - string sourceCode( + std::string sourceCode( "{\n" " let a := 3\n" " if a\n" @@ -382,14 +381,14 @@ BOOST_AUTO_TEST_CASE(toJson) Program program = get(Program::load(sourceStream)); Json::Value parsingResult; - string errors; + std::string errors; BOOST_TEST(jsonParseStrict(program.toJson(), parsingResult, &errors)); BOOST_TEST(errors.empty()); } BOOST_AUTO_TEST_CASE(codeSize) { - string sourceCode( + std::string sourceCode( "{\n" " function foo() -> result\n" " {\n" diff --git a/test/yulPhaser/ProgramCache.cpp b/test/yulPhaser/ProgramCache.cpp index 2dab86fcda17..c4bf7f609e61 100644 --- a/test/yulPhaser/ProgramCache.cpp +++ b/test/yulPhaser/ProgramCache.cpp @@ -30,7 +30,6 @@ #include #include -using namespace std; using namespace solidity::util; using namespace solidity::langutil; using namespace solidity::yul; @@ -50,16 +49,16 @@ class ProgramCacheFixture " }\n" "}\n"; - Program optimisedProgram(Program _program, string _abbreviatedOptimisationSteps) const + Program optimisedProgram(Program _program, std::string _abbreviatedOptimisationSteps) const { Program result = std::move(_program); result.optimise(Chromosome::genesToSteps(_abbreviatedOptimisationSteps)); return result; } - static set cachedKeys(ProgramCache const& _programCache) + static std::set cachedKeys(ProgramCache const& _programCache) { - set keys; + std::set keys; for (auto pair = _programCache.entries().begin(); pair != _programCache.entries().end(); ++pair) keys.insert(pair->first); @@ -67,7 +66,7 @@ class ProgramCacheFixture } CharStream m_sourceStream = CharStream(SampleSourceCode, "program-cache-test"); - Program m_program = get(Program::load(m_sourceStream)); + Program m_program = std::get(Program::load(m_sourceStream)); ProgramCache m_programCache{m_program}; }; @@ -111,7 +110,7 @@ BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_store_programs_for_all_prefixes, BOOST_TEST(toString(cachedProgram) == toString(programIuO)); - BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "IuO"})); + BOOST_REQUIRE((cachedKeys(m_programCache) == std::set{"I", "Iu", "IuO"})); BOOST_TEST(toString(*m_programCache.find("I")) == toString(programI)); BOOST_TEST(toString(*m_programCache.find("Iu")) == toString(programIu)); BOOST_TEST(toString(*m_programCache.find("IuO")) == toString(programIuO)); @@ -119,7 +118,7 @@ BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_store_programs_for_all_prefixes, BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_repeat_the_chromosome_requested_number_of_times, ProgramCacheFixture) { - string steps = "IuOIuO"; + std::string steps = "IuOIuO"; Program cachedProgram = m_programCache.optimiseProgram("IuO", 2); @@ -148,14 +147,14 @@ BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_reuse_the_longest_prefix_and_move m_programCache.startRound(1); BOOST_TEST(m_programCache.currentRound() == 1); - BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "Ia"})); + BOOST_REQUIRE((cachedKeys(m_programCache) == std::set{"I", "Iu", "Ia"})); BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); BOOST_TEST(m_programCache.entries().find("Ia")->second.roundNumber == 0); m_programCache.optimiseProgram("IuOI"); - BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "Ia", "IuO", "IuOI"})); + BOOST_REQUIRE((cachedKeys(m_programCache) == std::set{"I", "Iu", "Ia", "IuO", "IuOI"})); BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 1); BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 1); BOOST_TEST(m_programCache.entries().find("Ia")->second.roundNumber == 0); @@ -171,14 +170,14 @@ BOOST_FIXTURE_TEST_CASE(startRound_should_remove_entries_older_than_two_rounds, m_programCache.optimiseProgram("Iu"); BOOST_TEST(m_programCache.currentRound() == 0); - BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu"})); + BOOST_REQUIRE((cachedKeys(m_programCache) == std::set{"I", "Iu"})); BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); m_programCache.optimiseProgram("a"); BOOST_TEST(m_programCache.currentRound() == 0); - BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "a"})); + BOOST_REQUIRE((cachedKeys(m_programCache) == std::set{"I", "Iu", "a"})); BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 0); @@ -186,7 +185,7 @@ BOOST_FIXTURE_TEST_CASE(startRound_should_remove_entries_older_than_two_rounds, m_programCache.startRound(1); BOOST_TEST(m_programCache.currentRound() == 1); - BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "a"})); + BOOST_REQUIRE((cachedKeys(m_programCache) == std::set{"I", "Iu", "a"})); BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 0); @@ -194,7 +193,7 @@ BOOST_FIXTURE_TEST_CASE(startRound_should_remove_entries_older_than_two_rounds, m_programCache.optimiseProgram("af"); BOOST_TEST(m_programCache.currentRound() == 1); - BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "a", "af"})); + BOOST_REQUIRE((cachedKeys(m_programCache) == std::set{"I", "Iu", "a", "af"})); BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 1); @@ -203,7 +202,7 @@ BOOST_FIXTURE_TEST_CASE(startRound_should_remove_entries_older_than_two_rounds, m_programCache.startRound(2); BOOST_TEST(m_programCache.currentRound() == 2); - BOOST_REQUIRE((cachedKeys(m_programCache) == set{"a", "af"})); + BOOST_REQUIRE((cachedKeys(m_programCache) == std::set{"a", "af"})); BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 1); BOOST_TEST(m_programCache.entries().find("af")->second.roundNumber == 1); @@ -223,31 +222,31 @@ BOOST_FIXTURE_TEST_CASE(gatherStats_should_return_cache_statistics, ProgramCache m_programCache.optimiseProgram("L"); m_programCache.optimiseProgram("Iu"); - BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "I", "Iu"})); + BOOST_REQUIRE((cachedKeys(m_programCache) == std::set{"L", "I", "Iu"})); CacheStats expectedStats1{0, 3, sizeL + sizeI + sizeIu, {{0, 3}}}; BOOST_CHECK(m_programCache.gatherStats() == expectedStats1); m_programCache.optimiseProgram("IuO"); - BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "I", "Iu", "IuO"})); + BOOST_REQUIRE((cachedKeys(m_programCache) == std::set{"L", "I", "Iu", "IuO"})); CacheStats expectedStats2{2, 4, sizeL + sizeI + sizeIu + sizeIuO, {{0, 4}}}; BOOST_CHECK(m_programCache.gatherStats() == expectedStats2); m_programCache.startRound(1); - BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "I", "Iu", "IuO"})); + BOOST_REQUIRE((cachedKeys(m_programCache) == std::set{"L", "I", "Iu", "IuO"})); BOOST_CHECK(m_programCache.gatherStats() == expectedStats2); m_programCache.optimiseProgram("IuO"); - BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "I", "Iu", "IuO"})); + BOOST_REQUIRE((cachedKeys(m_programCache) == std::set{"L", "I", "Iu", "IuO"})); CacheStats expectedStats3{5, 4, sizeL + sizeI + sizeIu + sizeIuO, {{0, 1}, {1, 3}}}; BOOST_CHECK(m_programCache.gatherStats() == expectedStats3); m_programCache.startRound(2); - BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "IuO"})); + BOOST_REQUIRE((cachedKeys(m_programCache) == std::set{"I", "Iu", "IuO"})); CacheStats expectedStats4{5, 4, sizeI + sizeIu + sizeIuO, {{1, 3}}}; BOOST_CHECK(m_programCache.gatherStats() == expectedStats4); m_programCache.optimiseProgram("LT"); - BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "LT", "I", "Iu", "IuO"})); + BOOST_REQUIRE((cachedKeys(m_programCache) == std::set{"L", "LT", "I", "Iu", "IuO"})); CacheStats expectedStats5{5, 6, sizeL + sizeLT + sizeI + sizeIu + sizeIuO, {{1, 3}, {2, 2}}}; BOOST_CHECK(m_programCache.gatherStats() == expectedStats5); } diff --git a/test/yulPhaser/Selections.cpp b/test/yulPhaser/Selections.cpp index 28c8b8a879dc..866234978590 100644 --- a/test/yulPhaser/Selections.cpp +++ b/test/yulPhaser/Selections.cpp @@ -29,7 +29,6 @@ #include #include -using namespace std; using namespace solidity::util; namespace solidity::phaser::test @@ -41,28 +40,28 @@ BOOST_AUTO_TEST_SUITE(RangeSelectionTest) BOOST_AUTO_TEST_CASE(materialise) { - BOOST_TEST(RangeSelection(0.0, 1.0).materialise(10) == vector({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); - BOOST_TEST(RangeSelection(0.0, 0.1).materialise(10) == vector({0})); - BOOST_TEST(RangeSelection(0.0, 0.2).materialise(10) == vector({0, 1})); - BOOST_TEST(RangeSelection(0.0, 0.7).materialise(10) == vector({0, 1, 2, 3, 4, 5, 6})); + BOOST_TEST(RangeSelection(0.0, 1.0).materialise(10) == std::vector({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); + BOOST_TEST(RangeSelection(0.0, 0.1).materialise(10) == std::vector({0})); + BOOST_TEST(RangeSelection(0.0, 0.2).materialise(10) == std::vector({0, 1})); + BOOST_TEST(RangeSelection(0.0, 0.7).materialise(10) == std::vector({0, 1, 2, 3, 4, 5, 6})); - BOOST_TEST(RangeSelection(0.9, 1.0).materialise(10) == vector({ 9})); - BOOST_TEST(RangeSelection(0.8, 1.0).materialise(10) == vector({ 8, 9})); - BOOST_TEST(RangeSelection(0.5, 1.0).materialise(10) == vector({ 5, 6, 7, 8, 9})); + BOOST_TEST(RangeSelection(0.9, 1.0).materialise(10) == std::vector({ 9})); + BOOST_TEST(RangeSelection(0.8, 1.0).materialise(10) == std::vector({ 8, 9})); + BOOST_TEST(RangeSelection(0.5, 1.0).materialise(10) == std::vector({ 5, 6, 7, 8, 9})); - BOOST_TEST(RangeSelection(0.3, 0.6).materialise(10) == vector({ 3, 4, 5 })); - BOOST_TEST(RangeSelection(0.2, 0.7).materialise(10) == vector({ 2, 3, 4, 5, 6 })); - BOOST_TEST(RangeSelection(0.4, 0.7).materialise(10) == vector({ 4, 5, 6 })); + BOOST_TEST(RangeSelection(0.3, 0.6).materialise(10) == std::vector({ 3, 4, 5 })); + BOOST_TEST(RangeSelection(0.2, 0.7).materialise(10) == std::vector({ 2, 3, 4, 5, 6 })); + BOOST_TEST(RangeSelection(0.4, 0.7).materialise(10) == std::vector({ 4, 5, 6 })); - BOOST_TEST(RangeSelection(0.4, 0.7).materialise(5) == vector({2, 3})); + BOOST_TEST(RangeSelection(0.4, 0.7).materialise(5) == std::vector({2, 3})); } BOOST_AUTO_TEST_CASE(materialise_should_round_indices) { - BOOST_TEST(RangeSelection(0.01, 0.99).materialise(10) == vector({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); - BOOST_TEST(RangeSelection(0.04, 0.96).materialise(10) == vector({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); - BOOST_TEST(RangeSelection(0.05, 0.95).materialise(10) == vector({ 1, 2, 3, 4, 5, 6, 7, 8, 9})); - BOOST_TEST(RangeSelection(0.06, 0.94).materialise(10) == vector({ 1, 2, 3, 4, 5, 6, 7, 8 })); + BOOST_TEST(RangeSelection(0.01, 0.99).materialise(10) == std::vector({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); + BOOST_TEST(RangeSelection(0.04, 0.96).materialise(10) == std::vector({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); + BOOST_TEST(RangeSelection(0.05, 0.95).materialise(10) == std::vector({ 1, 2, 3, 4, 5, 6, 7, 8, 9})); + BOOST_TEST(RangeSelection(0.06, 0.94).materialise(10) == std::vector({ 1, 2, 3, 4, 5, 6, 7, 8 })); } BOOST_AUTO_TEST_CASE(materialise_should_handle_empty_collections) @@ -93,27 +92,27 @@ BOOST_AUTO_TEST_SUITE(MosaicSelectionTest) BOOST_AUTO_TEST_CASE(materialise) { - BOOST_TEST(MosaicSelection({1}, 0.5).materialise(4) == vector({1, 1})); - BOOST_TEST(MosaicSelection({1}, 1.0).materialise(4) == vector({1, 1, 1, 1})); - BOOST_TEST(MosaicSelection({1}, 2.0).materialise(4) == vector({1, 1, 1, 1, 1, 1, 1, 1})); - BOOST_TEST(MosaicSelection({1}, 1.0).materialise(2) == vector({1, 1})); - - BOOST_TEST(MosaicSelection({0, 1}, 0.5).materialise(4) == vector({0, 1})); - BOOST_TEST(MosaicSelection({0, 1}, 1.0).materialise(4) == vector({0, 1, 0, 1})); - BOOST_TEST(MosaicSelection({0, 1}, 2.0).materialise(4) == vector({0, 1, 0, 1, 0, 1, 0, 1})); - BOOST_TEST(MosaicSelection({0, 1}, 1.0).materialise(2) == vector({0, 1})); - - BOOST_TEST(MosaicSelection({3, 2, 1, 0}, 0.5).materialise(4) == vector({3, 2})); - BOOST_TEST(MosaicSelection({3, 2, 1, 0}, 1.0).materialise(4) == vector({3, 2, 1, 0})); - BOOST_TEST(MosaicSelection({3, 2, 1, 0}, 2.0).materialise(4) == vector({3, 2, 1, 0, 3, 2, 1, 0})); - BOOST_TEST(MosaicSelection({1, 0, 1, 0}, 1.0).materialise(2) == vector({1, 0})); + BOOST_TEST(MosaicSelection({1}, 0.5).materialise(4) == std::vector({1, 1})); + BOOST_TEST(MosaicSelection({1}, 1.0).materialise(4) == std::vector({1, 1, 1, 1})); + BOOST_TEST(MosaicSelection({1}, 2.0).materialise(4) == std::vector({1, 1, 1, 1, 1, 1, 1, 1})); + BOOST_TEST(MosaicSelection({1}, 1.0).materialise(2) == std::vector({1, 1})); + + BOOST_TEST(MosaicSelection({0, 1}, 0.5).materialise(4) == std::vector({0, 1})); + BOOST_TEST(MosaicSelection({0, 1}, 1.0).materialise(4) == std::vector({0, 1, 0, 1})); + BOOST_TEST(MosaicSelection({0, 1}, 2.0).materialise(4) == std::vector({0, 1, 0, 1, 0, 1, 0, 1})); + BOOST_TEST(MosaicSelection({0, 1}, 1.0).materialise(2) == std::vector({0, 1})); + + BOOST_TEST(MosaicSelection({3, 2, 1, 0}, 0.5).materialise(4) == std::vector({3, 2})); + BOOST_TEST(MosaicSelection({3, 2, 1, 0}, 1.0).materialise(4) == std::vector({3, 2, 1, 0})); + BOOST_TEST(MosaicSelection({3, 2, 1, 0}, 2.0).materialise(4) == std::vector({3, 2, 1, 0, 3, 2, 1, 0})); + BOOST_TEST(MosaicSelection({1, 0, 1, 0}, 1.0).materialise(2) == std::vector({1, 0})); } BOOST_AUTO_TEST_CASE(materialise_should_round_indices) { - BOOST_TEST(MosaicSelection({4, 3, 2, 1, 0}, 0.49).materialise(5) == vector({4, 3})); - BOOST_TEST(MosaicSelection({4, 3, 2, 1, 0}, 0.50).materialise(5) == vector({4, 3, 2})); - BOOST_TEST(MosaicSelection({4, 3, 2, 1, 0}, 0.51).materialise(5) == vector({4, 3, 2})); + BOOST_TEST(MosaicSelection({4, 3, 2, 1, 0}, 0.49).materialise(5) == std::vector({4, 3})); + BOOST_TEST(MosaicSelection({4, 3, 2, 1, 0}, 0.50).materialise(5) == std::vector({4, 3, 2})); + BOOST_TEST(MosaicSelection({4, 3, 2, 1, 0}, 0.51).materialise(5) == std::vector({4, 3, 2})); } BOOST_AUTO_TEST_CASE(materialise_should_handle_empty_collections) @@ -132,10 +131,10 @@ BOOST_AUTO_TEST_CASE(materialise_should_handle_empty_selections) BOOST_AUTO_TEST_CASE(materialise_should_clamp_indices_at_collection_size) { - BOOST_TEST(MosaicSelection({4, 3, 2, 1, 0}, 1.0).materialise(4) == vector({3, 3, 2, 1})); - BOOST_TEST(MosaicSelection({4, 3, 2, 1, 0}, 2.0).materialise(3) == vector({2, 2, 2, 1, 0, 2})); - BOOST_TEST(MosaicSelection({4, 3, 2, 1, 0}, 1.0).materialise(1) == vector({0})); - BOOST_TEST(MosaicSelection({4, 3, 2, 1, 0}, 7.0).materialise(1) == vector({0, 0, 0, 0, 0, 0, 0})); + BOOST_TEST(MosaicSelection({4, 3, 2, 1, 0}, 1.0).materialise(4) == std::vector({3, 3, 2, 1})); + BOOST_TEST(MosaicSelection({4, 3, 2, 1, 0}, 2.0).materialise(3) == std::vector({2, 2, 2, 1, 0, 2})); + BOOST_TEST(MosaicSelection({4, 3, 2, 1, 0}, 1.0).materialise(1) == std::vector({0})); + BOOST_TEST(MosaicSelection({4, 3, 2, 1, 0}, 7.0).materialise(1) == std::vector({0, 0, 0, 0, 0, 0, 0})); } BOOST_AUTO_TEST_SUITE_END() @@ -150,17 +149,17 @@ BOOST_AUTO_TEST_CASE(materialise_should_return_random_values_with_equal_probabil constexpr double variance = (collectionSize * collectionSize - 1) / 12.0; SimulationRNG::reset(1); - vector samples = RandomSelection(selectionSize).materialise(collectionSize); + std::vector samples = RandomSelection(selectionSize).materialise(collectionSize); - BOOST_TEST(abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); - BOOST_TEST(abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); + BOOST_TEST(std::abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(std::abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); } BOOST_AUTO_TEST_CASE(materialise_should_return_only_values_that_can_be_used_as_collection_indices) { const size_t collectionSize = 200; - vector indices = RandomSelection(0.5).materialise(collectionSize); + std::vector indices = RandomSelection(0.5).materialise(collectionSize); BOOST_TEST(indices.size() == 100); BOOST_TEST(all_of(indices.begin(), indices.end(), [&](auto const& index){ return index <= collectionSize; })); @@ -214,20 +213,20 @@ BOOST_AUTO_TEST_CASE(materialise_should_return_random_values_with_equal_probabil constexpr double variance = selectionChance * (1 - selectionChance); SimulationRNG::reset(1); - auto indices = convertContainer>(RandomSubset(selectionChance).materialise(collectionSize)); + auto indices = convertContainer>(RandomSubset(selectionChance).materialise(collectionSize)); - vector bernoulliTrials(collectionSize); + std::vector bernoulliTrials(collectionSize); for (size_t i = 0; i < collectionSize; ++i) bernoulliTrials[i] = double(indices.count(i)); - BOOST_TEST(abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance); - BOOST_TEST(abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance); + BOOST_TEST(std::abs(mean(bernoulliTrials) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(std::abs(meanSquaredError(bernoulliTrials, expectedValue) - variance) < variance * relativeTolerance); } BOOST_AUTO_TEST_CASE(materialise_should_return_only_values_that_can_be_used_as_collection_indices) { const size_t collectionSize = 200; - vector indices = RandomSubset(0.5).materialise(collectionSize); + std::vector indices = RandomSubset(0.5).materialise(collectionSize); BOOST_TEST(all_of(indices.begin(), indices.end(), [&](auto const& index){ return index <= collectionSize; })); } @@ -235,7 +234,7 @@ BOOST_AUTO_TEST_CASE(materialise_should_return_only_values_that_can_be_used_as_c BOOST_AUTO_TEST_CASE(materialise_should_return_indices_in_the_same_order_they_are_in_the_container) { const size_t collectionSize = 200; - vector indices = RandomSubset(0.5).materialise(collectionSize); + std::vector indices = RandomSubset(0.5).materialise(collectionSize); for (size_t i = 1; i < indices.size(); ++i) BOOST_TEST(indices[i - 1] < indices[i]); diff --git a/test/yulPhaser/SimulationRNG.cpp b/test/yulPhaser/SimulationRNG.cpp index d6e8c3a81150..ff91846e30d8 100644 --- a/test/yulPhaser/SimulationRNG.cpp +++ b/test/yulPhaser/SimulationRNG.cpp @@ -24,8 +24,6 @@ #include -using namespace std; - namespace solidity::phaser::test { @@ -43,12 +41,12 @@ BOOST_AUTO_TEST_CASE(bernoulliTrial_should_produce_samples_with_right_expected_v constexpr double expectedValue = successProbability; constexpr double variance = successProbability * (1 - successProbability); - vector samples; + std::vector samples; for (uint32_t i = 0; i < numSamples; ++i) samples.push_back(static_cast(SimulationRNG::bernoulliTrial(successProbability))); - BOOST_TEST(abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); - BOOST_TEST(abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); + BOOST_TEST(std::abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(std::abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); } BOOST_AUTO_TEST_CASE(bernoulliTrial_can_be_reset) @@ -57,21 +55,21 @@ BOOST_AUTO_TEST_CASE(bernoulliTrial_can_be_reset) constexpr double successProbability = 0.4; SimulationRNG::reset(1); - vector samples1; + std::vector samples1; for (uint32_t i = 0; i < numSamples; ++i) samples1.push_back(static_cast(SimulationRNG::bernoulliTrial(successProbability))); - vector samples2; + std::vector samples2; for (uint32_t i = 0; i < numSamples; ++i) samples2.push_back(static_cast(SimulationRNG::bernoulliTrial(successProbability))); SimulationRNG::reset(1); - vector samples3; + std::vector samples3; for (uint32_t i = 0; i < numSamples; ++i) samples3.push_back(static_cast(SimulationRNG::bernoulliTrial(successProbability))); SimulationRNG::reset(2); - vector samples4; + std::vector samples4; for (uint32_t i = 0; i < numSamples; ++i) samples4.push_back(static_cast(SimulationRNG::bernoulliTrial(successProbability))); @@ -95,12 +93,12 @@ BOOST_AUTO_TEST_CASE(uniformInt_returns_different_values_when_called_multiple_ti constexpr double expectedValue = (minValue + maxValue) / 2.0; constexpr double variance = ((maxValue - minValue + 1) * (maxValue - minValue + 1) - 1) / 12.0; - vector samples; + std::vector samples; for (uint32_t i = 0; i < numSamples; ++i) samples.push_back(SimulationRNG::uniformInt(minValue, maxValue)); - BOOST_TEST(abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); - BOOST_TEST(abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); + BOOST_TEST(std::abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(std::abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); } BOOST_AUTO_TEST_CASE(uniformInt_can_be_reset) @@ -110,21 +108,21 @@ BOOST_AUTO_TEST_CASE(uniformInt_can_be_reset) constexpr uint32_t maxValue = 80; SimulationRNG::reset(1); - vector samples1; + std::vector samples1; for (uint32_t i = 0; i < numSamples; ++i) samples1.push_back(SimulationRNG::uniformInt(minValue, maxValue)); - vector samples2; + std::vector samples2; for (uint32_t i = 0; i < numSamples; ++i) samples2.push_back(SimulationRNG::uniformInt(minValue, maxValue)); SimulationRNG::reset(1); - vector samples3; + std::vector samples3; for (uint32_t i = 0; i < numSamples; ++i) samples3.push_back(SimulationRNG::uniformInt(minValue, maxValue)); SimulationRNG::reset(2); - vector samples4; + std::vector samples4; for (uint32_t i = 0; i < numSamples; ++i) samples4.push_back(SimulationRNG::uniformInt(minValue, maxValue)); @@ -148,12 +146,12 @@ BOOST_AUTO_TEST_CASE(binomialInt_should_produce_samples_with_right_expected_valu constexpr double expectedValue = numTrials * successProbability; constexpr double variance = numTrials * successProbability * (1 - successProbability); - vector samples; + std::vector samples; for (uint32_t i = 0; i < numSamples; ++i) samples.push_back(SimulationRNG::binomialInt(numTrials, successProbability)); - BOOST_TEST(abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); - BOOST_TEST(abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); + BOOST_TEST(std::abs(mean(samples) - expectedValue) < expectedValue * relativeTolerance); + BOOST_TEST(std::abs(meanSquaredError(samples, expectedValue) - variance) < variance * relativeTolerance); } BOOST_AUTO_TEST_CASE(binomialInt_can_be_reset) @@ -163,21 +161,21 @@ BOOST_AUTO_TEST_CASE(binomialInt_can_be_reset) constexpr double successProbability = 0.6; SimulationRNG::reset(1); - vector samples1; + std::vector samples1; for (uint32_t i = 0; i < numSamples; ++i) samples1.push_back(SimulationRNG::binomialInt(numTrials, successProbability)); - vector samples2; + std::vector samples2; for (uint32_t i = 0; i < numSamples; ++i) samples2.push_back(SimulationRNG::binomialInt(numTrials, successProbability)); SimulationRNG::reset(1); - vector samples3; + std::vector samples3; for (uint32_t i = 0; i < numSamples; ++i) samples3.push_back(SimulationRNG::binomialInt(numTrials, successProbability)); SimulationRNG::reset(2); - vector samples4; + std::vector samples4; for (uint32_t i = 0; i < numSamples; ++i) samples4.push_back(SimulationRNG::binomialInt(numTrials, successProbability)); diff --git a/test/yulPhaser/TestHelpers.cpp b/test/yulPhaser/TestHelpers.cpp index 50afd70a3eb3..d75b63b4ac8b 100644 --- a/test/yulPhaser/TestHelpers.cpp +++ b/test/yulPhaser/TestHelpers.cpp @@ -22,22 +22,21 @@ #include -using namespace std; using namespace solidity; using namespace solidity::yul; using namespace solidity::phaser; using namespace solidity::phaser::test; -function phaser::test::wholeChromosomeReplacement(Chromosome _newChromosome) +std::function phaser::test::wholeChromosomeReplacement(Chromosome _newChromosome) { return [_newChromosome = std::move(_newChromosome)](Chromosome const&) { return _newChromosome; }; } -function phaser::test::geneSubstitution(size_t _geneIndex, string _geneValue) +std::function phaser::test::geneSubstitution(size_t _geneIndex, std::string _geneValue) { return [=](Chromosome const& _chromosome) { - vector newGenes = _chromosome.optimisationSteps(); + std::vector newGenes = _chromosome.optimisationSteps(); assert(_geneIndex < newGenes.size()); newGenes[_geneIndex] = _geneValue; @@ -45,18 +44,18 @@ function phaser::test::geneSubstitution(size_t _geneIndex, string _gen }; } -vector phaser::test::chromosomeLengths(Population const& _population) +std::vector phaser::test::chromosomeLengths(Population const& _population) { - vector lengths; + std::vector lengths; for (auto const& individual: _population.individuals()) lengths.push_back(individual.chromosome.length()); return lengths; } -map phaser::test::enumerateOptmisationSteps() +std::map phaser::test::enumerateOptmisationSteps() { - map stepIndices; + std::map stepIndices; size_t i = 0; for (auto const& nameAndAbbreviation: OptimiserSuite::stepNameToAbbreviationMap()) stepIndices.insert({nameAndAbbreviation.first, i++}); @@ -67,29 +66,29 @@ map phaser::test::enumerateOptmisationSteps() size_t phaser::test::countDifferences(Chromosome const& _chromosome1, Chromosome const& _chromosome2) { size_t count = 0; - for (size_t i = 0; i < min(_chromosome1.length(), _chromosome2.length()); ++i) + for (size_t i = 0; i < std::min(_chromosome1.length(), _chromosome2.length()); ++i) if (_chromosome1.optimisationSteps()[i] != _chromosome2.optimisationSteps()[i]) ++count; - return count + static_cast(abs( + return count + static_cast(std::abs( static_cast(_chromosome1.length()) - static_cast(_chromosome2.length()) )); } -string phaser::test::stripWhitespace(string const& input) +std::string phaser::test::stripWhitespace(std::string const& input) { - regex whitespaceRegex("\\s+"); + std::regex whitespaceRegex("\\s+"); return regex_replace(input, whitespaceRegex, ""); } -size_t phaser::test::countSubstringOccurrences(string const& _inputString, string const& _substring) +size_t phaser::test::countSubstringOccurrences(std::string const& _inputString, std::string const& _substring) { assert(_substring.size() > 0); size_t count = 0; size_t lastOccurrence = 0; - while ((lastOccurrence = _inputString.find(_substring, lastOccurrence)) != string::npos) + while ((lastOccurrence = _inputString.find(_substring, lastOccurrence)) != std::string::npos) { ++count; lastOccurrence += _substring.size(); diff --git a/test/yulPhaser/TestHelpersTest.cpp b/test/yulPhaser/TestHelpersTest.cpp index 89b98ecad7aa..23be74230cca 100644 --- a/test/yulPhaser/TestHelpersTest.cpp +++ b/test/yulPhaser/TestHelpersTest.cpp @@ -24,7 +24,6 @@ #include -using namespace std; using namespace solidity::yul; using namespace boost::test_tools; @@ -43,7 +42,7 @@ BOOST_AUTO_TEST_CASE(ChromosomeLengthMetric_evaluate_should_return_chromosome_le BOOST_AUTO_TEST_CASE(wholeChromosomeReplacement_should_replace_whole_chromosome_with_another) { - function mutation = wholeChromosomeReplacement(Chromosome("aaa")); + std::function mutation = wholeChromosomeReplacement(Chromosome("aaa")); BOOST_TEST(mutation(Chromosome("ccc")) == Chromosome("aaa")); } @@ -51,22 +50,22 @@ BOOST_AUTO_TEST_CASE(geneSubstitution_should_change_a_single_gene_at_a_given_ind { Chromosome chromosome("aaccff"); - function mutation1 = geneSubstitution(0, chromosome.optimisationSteps()[5]); + std::function mutation1 = geneSubstitution(0, chromosome.optimisationSteps()[5]); BOOST_TEST(mutation1(chromosome) == Chromosome("faccff")); - function mutation2 = geneSubstitution(5, chromosome.optimisationSteps()[0]); + std::function mutation2 = geneSubstitution(5, chromosome.optimisationSteps()[0]); BOOST_TEST(mutation2(chromosome) == Chromosome("aaccfa")); } BOOST_AUTO_TEST_CASE(chromosomeLengths_should_return_lengths_of_all_chromosomes_in_a_population) { - shared_ptr fitnessMetric = make_shared(); + std::shared_ptr fitnessMetric = std::make_shared(); Population population1(fitnessMetric, {Chromosome(), Chromosome("a"), Chromosome("aa"), Chromosome("aaa")}); - BOOST_TEST((chromosomeLengths(population1) == vector{0, 1, 2, 3})); + BOOST_TEST((chromosomeLengths(population1) == std::vector{0, 1, 2, 3})); Population population2(fitnessMetric); - BOOST_TEST((chromosomeLengths(population2) == vector{})); + BOOST_TEST((chromosomeLengths(population2) == std::vector{})); } BOOST_AUTO_TEST_CASE(countDifferences_should_return_zero_for_identical_chromosomes) @@ -99,11 +98,11 @@ BOOST_AUTO_TEST_CASE(countDifferences_should_count_missing_characters_as_differe BOOST_AUTO_TEST_CASE(enumerateOptimisationSteps_should_assing_indices_to_all_available_optimisation_steps) { - map stepsAndAbbreviations = OptimiserSuite::stepNameToAbbreviationMap(); - map stepsAndIndices = enumerateOptmisationSteps(); + std::map stepsAndAbbreviations = OptimiserSuite::stepNameToAbbreviationMap(); + std::map stepsAndIndices = enumerateOptmisationSteps(); BOOST_TEST(stepsAndIndices.size() == stepsAndAbbreviations.size()); - set stepsSoFar; + std::set stepsSoFar; for (auto& [name, index]: stepsAndIndices) { BOOST_TEST(index >= 0); From 838658ff36ba542a63f7e9582fdae51d7ac73024 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Fri, 9 Feb 2024 14:50:23 +0100 Subject: [PATCH 094/189] Install dependencies and add symbolic link for python3 --- .circleci/config.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 02c15e691b4f..c06e06e12b81 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -977,6 +977,9 @@ jobs: steps: - run: git config --global core.autocrlf false - checkout + - run: + name: Install dependencies + command: python -m pip install --user requests - run: name: Python unit tests command: python.exe test/pyscriptTests.py @@ -1688,7 +1691,7 @@ jobs: - run: git config --global core.autocrlf false - checkout # Ensure windows has python3 alias required by prepare_bytecode_report - - run: ln -s /c/tools/miniconda3/python /c/tools/miniconda3/python3 + - run: ln -s /c/Python312/python /c/Python312/python3 - attach_workspace: at: build - prepare_bytecode_report: From 7c1bdbaa584ced642891894ab98b3bfa9055ea19 Mon Sep 17 00:00:00 2001 From: r0qs Date: Mon, 12 Feb 2024 11:50:30 +0100 Subject: [PATCH 095/189] Remove sha from foundry release tag --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c06e06e12b81..b29c46bf8b9e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -155,7 +155,7 @@ commands: ) echo "export FOUNDRY_REPO=$FOUNDRY_REPO" >> "$BASH_ENV" echo "export FOUNDRY_VERSION=$FOUNDRY_VERSION" >> "$BASH_ENV" - echo "export FOUNDRY_RELEASE_TAG='nightly-${FOUNDRY_RELEASE_SHA}'" >> "$BASH_ENV" + echo "export FOUNDRY_RELEASE_TAG='${FOUNDRY_VERSION}'" >> "$BASH_ENV" # Save commit sha for caching echo "$FOUNDRY_RELEASE_SHA" > /tmp/workspace/foundry-release-sha - restore_cache: From c7549de45bbc280055cc433990d13cac608f1d22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 11 Feb 2024 17:07:13 +0100 Subject: [PATCH 096/189] Only warn instead of failing when enforcing gas expectations with non-standard settings --- test/Common.cpp | 20 +++++++++----------- test/soltest.cpp | 3 --- test/tools/isoltest.cpp | 3 --- 3 files changed, 9 insertions(+), 17 deletions(-) diff --git a/test/Common.cpp b/test/Common.cpp index 6a3c5a2a00ad..14d90d837e8d 100644 --- a/test/Common.cpp +++ b/test/Common.cpp @@ -143,18 +143,16 @@ void CommonOptions::validate() const "Selected batch has to be less than number of batches." ); - if (enforceGasTest) + if (!enforceGasTest) + cout << endl << "WARNING :: Gas cost expectations are not being enforced" << endl << endl; + else if (evmVersion() != langutil::EVMVersion{} || useABIEncoderV1) { - assertThrow( - evmVersion() == langutil::EVMVersion{}, - ConfigException, - "Gas costs can only be enforced on latest evm version." - ); - assertThrow( - useABIEncoderV1 == false, - ConfigException, - "Gas costs can only be enforced on abi encoder v2." - ); + cout << endl << "WARNING :: Enforcing gas cost expectations with non-standard settings:" << endl; + if (evmVersion() != langutil::EVMVersion{}) + cout << "- EVM version: " << evmVersion().name() << " (default: " << langutil::EVMVersion{}.name() << ")" << endl; + if (useABIEncoderV1) + cout << "- ABI coder: v1 (default: v2)" << endl; + cout << endl << "DO NOT COMMIT THE UPDATED EXPECTATIONS." << endl << endl; } } diff --git a/test/soltest.cpp b/test/soltest.cpp index 6aa100248875..443c4c5de3e3 100644 --- a/test/soltest.cpp +++ b/test/soltest.cpp @@ -238,9 +238,6 @@ test_suite* init_unit_test_suite(int /*argc*/, char* /*argv*/[]) if (solidity::test::CommonOptions::get().disableSemanticTests) cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl; - if (!solidity::test::CommonOptions::get().enforceGasTest) - cout << endl << "WARNING :: Gas Cost Expectations are not being enforced" << endl << endl; - Batcher batcher(CommonOptions::get().selectedBatch, CommonOptions::get().batches); if (CommonOptions::get().batches > 1) cout << "Batch " << CommonOptions::get().selectedBatch << " out of " << CommonOptions::get().batches << endl; diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 2d93bdbc2b9a..9d20f1059fb3 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -448,9 +448,6 @@ int main(int argc, char const *argv[]) if (options.disableSemanticTests) cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl; - if (!options.enforceGasTest) - cout << "WARNING :: Gas Cost Expectations are not being enforced" << endl << endl; - TestStats global_stats{0, 0}; cout << "Running tests..." << endl << endl; From 44da00d3ff7be33c5dc87ad1a62b5aba320bec09 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Tue, 6 Feb 2024 12:25:25 +0100 Subject: [PATCH 097/189] Use MCOPY when copying byte arrays --- Changelog.md | 1 + libsolidity/codegen/YulUtilFunctions.cpp | 32 ++++++------ .../debug_info_in_yul_snippet_escaping/output | 2 + .../args | 1 + .../input.sol | 10 ++++ .../output | 51 +++++++++++++++++++ .../args | 1 + .../input.sol | 9 ++++ .../output | 51 +++++++++++++++++++ .../yul_string_format_ascii/output.json | 2 + .../yul_string_format_ascii_long/output.json | 2 + .../semanticTests/various/address_code.sol | 2 +- .../sizeLimits/bytecode_too_large.sol | 4 +- 13 files changed, 150 insertions(+), 18 deletions(-) create mode 100644 test/cmdlineTests/mcopy_bytes_array_returned_from_function/args create mode 100644 test/cmdlineTests/mcopy_bytes_array_returned_from_function/input.sol create mode 100644 test/cmdlineTests/mcopy_bytes_array_returned_from_function/output create mode 100644 test/cmdlineTests/mcopy_string_literal_returned_from_function/args create mode 100644 test/cmdlineTests/mcopy_string_literal_returned_from_function/input.sol create mode 100644 test/cmdlineTests/mcopy_string_literal_returned_from_function/output diff --git a/Changelog.md b/Changelog.md index d20c266d8384..3d64b1b80e6f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Language Features: Compiler Features: + * Code Generator: Use ``MCOPY`` instead of ``MLOAD``/``MSTORE`` loop when copying byte arrays. Bugfixes: diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 35fea8ac4b76..0dabc8a36928 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -88,34 +88,36 @@ std::string YulUtilFunctions::copyToMemoryFunction(bool _fromCalldata, bool _cle "_to_memory"s + (_cleanup ? "_with_cleanup"s : ""s); - return m_functionCollector.createFunction(functionName, [&]() { + return m_functionCollector.createFunction(functionName, [&](std::vector& _args, std::vector&) { + _args = {"src", "dst", "length"}; + if (_fromCalldata) - { return Whiskers(R"( - function (src, dst, length) { - calldatacopy(dst, src, length) - mstore(add(dst, length), 0) - } + calldatacopy(dst, src, length) + mstore(add(dst, length), 0) )") - ("functionName", functionName) ("cleanup", _cleanup) .render(); - } else { - return Whiskers(R"( - function (src, dst, length) { + if (m_evmVersion.hasMcopy()) + return Whiskers(R"( + mcopy(dst, src, length) + mstore(add(dst, length), 0) + )") + ("cleanup", _cleanup) + .render(); + else + return Whiskers(R"( let i := 0 for { } lt(i, length) { i := add(i, 32) } { mstore(add(dst, i), mload(add(src, i))) } mstore(add(dst, length), 0) - } - )") - ("functionName", functionName) - ("cleanup", _cleanup) - .render(); + )") + ("cleanup", _cleanup) + .render(); } }); } diff --git a/test/cmdlineTests/debug_info_in_yul_snippet_escaping/output b/test/cmdlineTests/debug_info_in_yul_snippet_escaping/output index 94dfb864262a..4c90d9054d2a 100644 --- a/test/cmdlineTests/debug_info_in_yul_snippet_escaping/output +++ b/test/cmdlineTests/debug_info_in_yul_snippet_escaping/output @@ -180,12 +180,14 @@ object "D_27" { } function copy_memory_to_memory_with_cleanup(src, dst, length) { + let i := 0 for { } lt(i, length) { i := add(i, 32) } { mstore(add(dst, i), mload(add(src, i))) } mstore(add(dst, length), 0) + } function round_up_to_mul_of_32(value) -> result { diff --git a/test/cmdlineTests/mcopy_bytes_array_returned_from_function/args b/test/cmdlineTests/mcopy_bytes_array_returned_from_function/args new file mode 100644 index 000000000000..c74711ba8aec --- /dev/null +++ b/test/cmdlineTests/mcopy_bytes_array_returned_from_function/args @@ -0,0 +1 @@ +--evm-version cancun --no-cbor-metadata --via-ir --optimize --ir-optimized --debug-info none diff --git a/test/cmdlineTests/mcopy_bytes_array_returned_from_function/input.sol b/test/cmdlineTests/mcopy_bytes_array_returned_from_function/input.sol new file mode 100644 index 000000000000..05f24d64e792 --- /dev/null +++ b/test/cmdlineTests/mcopy_bytes_array_returned_from_function/input.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0.0; + +contract C { + function foo() external pure returns (bytes memory) + { + bytes memory ret = "aaaaa"; + return ret; + } +} diff --git a/test/cmdlineTests/mcopy_bytes_array_returned_from_function/output b/test/cmdlineTests/mcopy_bytes_array_returned_from_function/output new file mode 100644 index 000000000000..2441357d97fb --- /dev/null +++ b/test/cmdlineTests/mcopy_bytes_array_returned_from_function/output @@ -0,0 +1,51 @@ +Optimized IR: +/// @use-src 0:"mcopy_bytes_array_returned_from_function/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:"mcopy_bytes_array_returned_from_function/input.sol" + object "C_14_deployed" { + code { + { + let _1 := memoryguard(0x80) + if iszero(lt(calldatasize(), 4)) + { + if eq(0xc2985578, shr(224, calldataload(0))) + { + if callvalue() { revert(0, 0) } + if slt(add(calldatasize(), not(3)), 0) { revert(0, 0) } + let _2 := 64 + let newFreePtr := add(_1, _2) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, _1)) + { + mstore(0, shl(224, 0x4e487b71)) + mstore(4, 0x41) + revert(0, 36) + } + mstore(_2, newFreePtr) + mstore(_1, 5) + let _3 := add(_1, 0x20) + mstore(_3, "aaaaa") + let memPos := mload(_2) + mstore(memPos, 0x20) + let length := mload(_1) + mstore(add(memPos, 0x20), length) + mcopy(add(memPos, _2), _3, length) + mstore(add(add(memPos, length), _2), 0) + return(memPos, add(sub(add(memPos, and(add(length, 31), not(31))), memPos), _2)) + } + } + revert(0, 0) + } + } + data ".metadata" hex"" + } +} diff --git a/test/cmdlineTests/mcopy_string_literal_returned_from_function/args b/test/cmdlineTests/mcopy_string_literal_returned_from_function/args new file mode 100644 index 000000000000..c74711ba8aec --- /dev/null +++ b/test/cmdlineTests/mcopy_string_literal_returned_from_function/args @@ -0,0 +1 @@ +--evm-version cancun --no-cbor-metadata --via-ir --optimize --ir-optimized --debug-info none diff --git a/test/cmdlineTests/mcopy_string_literal_returned_from_function/input.sol b/test/cmdlineTests/mcopy_string_literal_returned_from_function/input.sol new file mode 100644 index 000000000000..0251c7cec976 --- /dev/null +++ b/test/cmdlineTests/mcopy_string_literal_returned_from_function/input.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0.0; + +contract C { + function foo() external pure returns (string memory) + { + return "MCOPY on Cancun vacation."; + } +} diff --git a/test/cmdlineTests/mcopy_string_literal_returned_from_function/output b/test/cmdlineTests/mcopy_string_literal_returned_from_function/output new file mode 100644 index 000000000000..02740ec0d6cb --- /dev/null +++ b/test/cmdlineTests/mcopy_string_literal_returned_from_function/output @@ -0,0 +1,51 @@ +Optimized IR: +/// @use-src 0:"mcopy_string_literal_returned_from_function/input.sol" +object "C_10" { + code { + { + let _1 := memoryguard(0x80) + mstore(64, _1) + if callvalue() { revert(0, 0) } + let _2 := datasize("C_10_deployed") + codecopy(_1, dataoffset("C_10_deployed"), _2) + return(_1, _2) + } + } + /// @use-src 0:"mcopy_string_literal_returned_from_function/input.sol" + object "C_10_deployed" { + code { + { + let _1 := memoryguard(0x80) + if iszero(lt(calldatasize(), 4)) + { + if eq(0xc2985578, shr(224, calldataload(0))) + { + if callvalue() { revert(0, 0) } + if slt(add(calldatasize(), not(3)), 0) { revert(0, 0) } + let _2 := 64 + let newFreePtr := add(_1, _2) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, _1)) + { + mstore(0, shl(224, 0x4e487b71)) + mstore(4, 0x41) + revert(0, 0x24) + } + mstore(_2, newFreePtr) + mstore(_1, 25) + let _3 := add(_1, 0x20) + mstore(_3, "MCOPY on Cancun vacation.") + let memPos := mload(_2) + mstore(memPos, 0x20) + let length := mload(_1) + mstore(add(memPos, 0x20), length) + mcopy(add(memPos, _2), _3, length) + mstore(add(add(memPos, length), _2), 0) + return(memPos, add(sub(add(memPos, and(add(length, 31), not(31))), memPos), _2)) + } + } + revert(0, 0) + } + } + data ".metadata" hex"" + } +} diff --git a/test/cmdlineTests/yul_string_format_ascii/output.json b/test/cmdlineTests/yul_string_format_ascii/output.json index aa830d1abe9d..1a9612c777b8 100644 --- a/test/cmdlineTests/yul_string_format_ascii/output.json +++ b/test/cmdlineTests/yul_string_format_ascii/output.json @@ -96,12 +96,14 @@ object \"C_11\" { } function copy_memory_to_memory_with_cleanup(src, dst, length) { + let i := 0 for { } lt(i, length) { i := add(i, 32) } { mstore(add(dst, i), mload(add(src, i))) } mstore(add(dst, length), 0) + } function round_up_to_mul_of_32(value) -> result { diff --git a/test/cmdlineTests/yul_string_format_ascii_long/output.json b/test/cmdlineTests/yul_string_format_ascii_long/output.json index 348e5a0b578c..5adeabfdffcf 100644 --- a/test/cmdlineTests/yul_string_format_ascii_long/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_long/output.json @@ -96,12 +96,14 @@ object \"C_11\" { } function copy_memory_to_memory_with_cleanup(src, dst, length) { + let i := 0 for { } lt(i, length) { i := add(i, 32) } { mstore(add(dst, i), mload(add(src, i))) } mstore(add(dst, length), 0) + } function round_up_to_mul_of_32(value) -> result { diff --git a/test/libsolidity/semanticTests/various/address_code.sol b/test/libsolidity/semanticTests/various/address_code.sol index 80daf116cdbd..7e48b00ca840 100644 --- a/test/libsolidity/semanticTests/various/address_code.sol +++ b/test/libsolidity/semanticTests/various/address_code.sol @@ -8,7 +8,7 @@ contract C { } // To avoid dependency on exact length. - function f() public view returns (bool) { return address(this).code.length > 400; } + function f() public view returns (bool) { return address(this).code.length > 380; } function g() public view returns (uint) { return address(0).code.length; } function h() public view returns (uint) { return address(1).code.length; } } diff --git a/test/libsolidity/syntaxTests/sizeLimits/bytecode_too_large.sol b/test/libsolidity/syntaxTests/sizeLimits/bytecode_too_large.sol index 21c48cc43323..f22b9755d652 100644 --- a/test/libsolidity/syntaxTests/sizeLimits/bytecode_too_large.sol +++ b/test/libsolidity/syntaxTests/sizeLimits/bytecode_too_large.sol @@ -7,6 +7,6 @@ contract test { } } // ==== -// EVMVersion: >=shanghai +// EVMVersion: >=cancun // ---- -// Warning 5574: (21-27154): Contract code size is 27187 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. +// Warning 5574: (21-27154): Contract code size is 27164 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. From 3122d35a4e8b0d49e20cb71833798d15d6e9ea73 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 14 Feb 2024 14:41:26 +0100 Subject: [PATCH 098/189] ABI decode test case --- .../mcopy_bytes_array_abi_decode/args | 1 + .../mcopy_bytes_array_abi_decode/err | 5 + .../mcopy_bytes_array_abi_decode/input.sol | 9 + .../mcopy_bytes_array_abi_decode/output | 230 ++++++++++++++++++ 4 files changed, 245 insertions(+) create mode 100644 test/cmdlineTests/mcopy_bytes_array_abi_decode/args create mode 100644 test/cmdlineTests/mcopy_bytes_array_abi_decode/err create mode 100644 test/cmdlineTests/mcopy_bytes_array_abi_decode/input.sol create mode 100644 test/cmdlineTests/mcopy_bytes_array_abi_decode/output diff --git a/test/cmdlineTests/mcopy_bytes_array_abi_decode/args b/test/cmdlineTests/mcopy_bytes_array_abi_decode/args new file mode 100644 index 000000000000..4a03d8d932b9 --- /dev/null +++ b/test/cmdlineTests/mcopy_bytes_array_abi_decode/args @@ -0,0 +1 @@ +--evm-version cancun --no-cbor-metadata --via-ir --ir --debug-info none diff --git a/test/cmdlineTests/mcopy_bytes_array_abi_decode/err b/test/cmdlineTests/mcopy_bytes_array_abi_decode/err new file mode 100644 index 000000000000..de32e66295be --- /dev/null +++ b/test/cmdlineTests/mcopy_bytes_array_abi_decode/err @@ -0,0 +1,5 @@ +Warning: Statement has no effect. + --> mcopy_bytes_array_abi_decode/input.sol:7:9: + | +7 | abi.decode("abcd", (bytes)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/test/cmdlineTests/mcopy_bytes_array_abi_decode/input.sol b/test/cmdlineTests/mcopy_bytes_array_abi_decode/input.sol new file mode 100644 index 000000000000..07d66f800ec9 --- /dev/null +++ b/test/cmdlineTests/mcopy_bytes_array_abi_decode/input.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0.0; + +contract C { + function foo() external pure + { + abi.decode("abcd", (bytes)); + } +} diff --git a/test/cmdlineTests/mcopy_bytes_array_abi_decode/output b/test/cmdlineTests/mcopy_bytes_array_abi_decode/output new file mode 100644 index 000000000000..2a24ec834fa1 --- /dev/null +++ b/test/cmdlineTests/mcopy_bytes_array_abi_decode/output @@ -0,0 +1,230 @@ +IR: + +/// @use-src 0:"mcopy_bytes_array_abi_decode/input.sol" +object "C_15" { + code { + + mstore(64, memoryguard(128)) + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } + + constructor_C_15() + + let _1 := allocate_unbounded() + codecopy(_1, dataoffset("C_15_deployed"), datasize("C_15_deployed")) + + return(_1, datasize("C_15_deployed")) + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function constructor_C_15() { + + } + + } + /// @use-src 0:"mcopy_bytes_array_abi_decode/input.sol" + object "C_15_deployed" { + code { + + mstore(64, memoryguard(128)) + + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_224_unsigned(calldataload(0)) + switch selector + + case 0xc2985578 + { + // foo() + + external_fun_foo_14() + } + + default {} + } + + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + + function shift_right_224_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_tuple_(headStart, dataEnd) { + if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } + + } + + function abi_encode_tuple__to__fromStack(headStart ) -> tail { + tail := add(headStart, 0) + + } + + function external_fun_foo_14() { + + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } + abi_decode_tuple_(4, calldatasize()) + fun_foo_14() + let memPos := allocate_unbounded() + let memEnd := abi_encode_tuple__to__fromStack(memPos ) + return(memPos, sub(memEnd, memPos)) + + } + + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + function round_up_to_mul_of_32(value) -> result { + result := and(add(value, 31), not(31)) + } + + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } + + function finalize_allocation(memPtr, size) { + let newFreePtr := add(memPtr, round_up_to_mul_of_32(size)) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(64, newFreePtr) + } + + function allocate_memory(size) -> memPtr { + memPtr := allocate_unbounded() + finalize_allocation(memPtr, size) + } + + function array_allocation_size_t_string_memory_ptr(length) -> size { + // Make sure we can allocate memory without overflow + if gt(length, 0xffffffffffffffff) { panic_error_0x41() } + + size := round_up_to_mul_of_32(length) + + // add length slot + size := add(size, 0x20) + + } + + function allocate_memory_array_t_string_memory_ptr(length) -> memPtr { + let allocSize := array_allocation_size_t_string_memory_ptr(length) + memPtr := allocate_memory(allocSize) + + mstore(memPtr, length) + + } + + function store_literal_in_memory_48bed44d1bcd124a28c27f343a817e5f5243190d3c52bf347daf876de1dbbf77(memPtr) { + + mstore(add(memPtr, 0), "abcd") + + } + + function copy_literal_to_memory_48bed44d1bcd124a28c27f343a817e5f5243190d3c52bf347daf876de1dbbf77() -> memPtr { + memPtr := allocate_memory_array_t_string_memory_ptr(4) + store_literal_in_memory_48bed44d1bcd124a28c27f343a817e5f5243190d3c52bf347daf876de1dbbf77(add(memPtr, 32)) + } + + function convert_t_stringliteral_48bed44d1bcd124a28c27f343a817e5f5243190d3c52bf347daf876de1dbbf77_to_t_bytes_memory_ptr() -> converted { + converted := copy_literal_to_memory_48bed44d1bcd124a28c27f343a817e5f5243190d3c52bf347daf876de1dbbf77() + } + + function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() { + revert(0, 0) + } + + function revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() { + revert(0, 0) + } + + function revert_error_987264b3b1d58a9c7f8255e93e81c77d86d6299019c33110a076957a3e06e2ae() { + revert(0, 0) + } + + function array_allocation_size_t_bytes_memory_ptr(length) -> size { + // Make sure we can allocate memory without overflow + if gt(length, 0xffffffffffffffff) { panic_error_0x41() } + + size := round_up_to_mul_of_32(length) + + // add length slot + size := add(size, 0x20) + + } + + function copy_memory_to_memory_with_cleanup(src, dst, length) { + + mcopy(dst, src, length) + mstore(add(dst, length), 0) + + } + + function abi_decode_available_length_t_bytes_memory_ptr_fromMemory(src, length, end) -> array { + array := allocate_memory(array_allocation_size_t_bytes_memory_ptr(length)) + mstore(array, length) + let dst := add(array, 0x20) + if gt(add(src, length), end) { revert_error_987264b3b1d58a9c7f8255e93e81c77d86d6299019c33110a076957a3e06e2ae() } + copy_memory_to_memory_with_cleanup(src, dst, length) + } + + // bytes + function abi_decode_t_bytes_memory_ptr_fromMemory(offset, end) -> array { + if iszero(slt(add(offset, 0x1f), end)) { revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() } + let length := mload(offset) + array := abi_decode_available_length_t_bytes_memory_ptr_fromMemory(add(offset, 0x20), length, end) + } + + function abi_decode_tuple_t_bytes_memory_ptr_fromMemory(headStart, dataEnd) -> value0 { + if slt(sub(dataEnd, headStart), 32) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } + + { + + let offset := mload(add(headStart, 0)) + if gt(offset, 0xffffffffffffffff) { revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() } + + value0 := abi_decode_t_bytes_memory_ptr_fromMemory(add(headStart, offset), dataEnd) + } + + } + + function array_length_t_bytes_memory_ptr(value) -> length { + + length := mload(value) + + } + + function fun_foo_14() { + + let _1_mpos := convert_t_stringliteral_48bed44d1bcd124a28c27f343a817e5f5243190d3c52bf347daf876de1dbbf77_to_t_bytes_memory_ptr() + + let expr_11_mpos := abi_decode_tuple_t_bytes_memory_ptr_fromMemory(add(_1_mpos, 32), add(add(_1_mpos, 32), array_length_t_bytes_memory_ptr(_1_mpos))) + + } + + } + + data ".metadata" hex"" + } + +} From 4032b5946cc0ae48baf4a65dd35e9401a3d612eb Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Sat, 10 Feb 2024 01:47:07 +0100 Subject: [PATCH 099/189] Add DebugData to AssemblyItem. --- libevmasm/Assembly.cpp | 2 +- libevmasm/AssemblyItem.h | 45 ++++++++--- libevmasm/CommonSubexpressionEliminator.cpp | 76 ++++++++++--------- libevmasm/CommonSubexpressionEliminator.h | 4 +- libevmasm/ControlFlowGraph.cpp | 2 +- libevmasm/ExpressionClasses.cpp | 7 +- libevmasm/ExpressionClasses.h | 2 +- libevmasm/KnownState.cpp | 62 +++++++-------- libevmasm/KnownState.h | 16 ++-- libevmasm/PeepholeOptimiser.cpp | 14 ++-- libevmasm/SimplificationRules.cpp | 12 +-- libevmasm/SimplificationRules.h | 4 +- liblangutil/CMakeLists.txt | 1 + liblangutil/DebugData.h | 70 +++++++++++++++++ libyul/AST.h | 72 +++++------------- libyul/ASTForward.h | 1 - libyul/AsmParser.cpp | 26 +++---- libyul/AsmParser.h | 8 +- libyul/AsmPrinter.cpp | 2 +- libyul/AsmPrinter.h | 4 +- libyul/backends/evm/ConstantOptimiser.h | 6 +- libyul/backends/evm/ControlFlowGraph.h | 22 +++--- .../backends/evm/ControlFlowGraphBuilder.cpp | 8 +- libyul/backends/evm/ControlFlowGraphBuilder.h | 4 +- .../evm/OptimizedEVMCodeTransform.cpp | 2 +- .../backends/evm/OptimizedEVMCodeTransform.h | 2 +- libyul/optimiser/ConditionalSimplifier.cpp | 2 +- libyul/optimiser/ControlFlowSimplifier.cpp | 4 +- libyul/optimiser/ExpressionSplitter.cpp | 2 +- libyul/optimiser/ForLoopConditionIntoBody.cpp | 2 +- .../optimiser/ForLoopConditionOutOfBody.cpp | 2 +- libyul/optimiser/SSATransform.cpp | 4 +- libyul/optimiser/SimplificationRules.cpp | 2 +- libyul/optimiser/SimplificationRules.h | 6 +- libyul/optimiser/StackToMemoryMover.cpp | 4 +- test/tools/yulInterpreter/Inspector.cpp | 4 +- test/tools/yulInterpreter/Inspector.h | 6 +- 37 files changed, 289 insertions(+), 223 deletions(-) create mode 100644 liblangutil/DebugData.h diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index b6e4d0006c57..424d3229cae0 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -180,7 +180,7 @@ AssemblyItem Assembly::createAssemblyItemFromJSON(Json::Value const& _json, std: if (c_instructions.count(name)) { - AssemblyItem item{c_instructions.at(name), location}; + AssemblyItem item{c_instructions.at(name), langutil::DebugData::create(location)}; if (!jumpType.empty()) { if (item.instruction() == Instruction::JUMP || item.instruction() == Instruction::JUMPI) diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index 93be394733ca..c2b3603c6352 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -24,7 +24,8 @@ #include #include -#include +#include +#include #include #include #include @@ -64,16 +65,16 @@ class AssemblyItem public: enum class JumpType { Ordinary, IntoFunction, OutOfFunction }; - AssemblyItem(u256 _push, langutil::SourceLocation _location = langutil::SourceLocation()): - AssemblyItem(Push, std::move(_push), std::move(_location)) { } - AssemblyItem(Instruction _i, langutil::SourceLocation _location = langutil::SourceLocation()): + AssemblyItem(u256 _push, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create()): + AssemblyItem(Push, std::move(_push), std::move(_debugData)) { } + AssemblyItem(Instruction _i, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create()): m_type(Operation), m_instruction(_i), - m_location(std::move(_location)) + m_debugData(std::move(_debugData)) {} - AssemblyItem(AssemblyItemType _type, u256 _data = 0, langutil::SourceLocation _location = langutil::SourceLocation()): + AssemblyItem(AssemblyItemType _type, u256 _data = 0, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create()): m_type(_type), - m_location(std::move(_location)) + m_debugData(std::move(_debugData)) { if (m_type == Operation) m_instruction = Instruction(uint8_t(_data)); @@ -83,7 +84,8 @@ class AssemblyItem explicit AssemblyItem(bytes _verbatimData, size_t _arguments, size_t _returnVariables): m_type(VerbatimBytecode), m_instruction{}, - m_verbatimBytecode{{_arguments, _returnVariables, std::move(_verbatimData)}} + m_verbatimBytecode{{_arguments, _returnVariables, std::move(_verbatimData)}}, + m_debugData{langutil::DebugData::create()} {} AssemblyItem(AssemblyItem const&) = default; @@ -170,8 +172,29 @@ class AssemblyItem /// @returns true if the assembly item can be used in a functional context. bool canBeFunctional() const; - void setLocation(langutil::SourceLocation const& _location) { m_location = _location; } - langutil::SourceLocation const& location() const { return m_location; } + void setLocation(langutil::SourceLocation const& _location) + { + solAssert(m_debugData); + m_debugData = langutil::DebugData::create( + _location, + m_debugData->originLocation, + m_debugData->astID + ); + } + + langutil::SourceLocation const& location() const + { + solAssert(m_debugData); + return m_debugData->nativeLocation; + } + + void setDebugData(langutil::DebugData::ConstPtr _debugData) + { + solAssert(_debugData); + m_debugData = std::move(_debugData); + } + + langutil::DebugData::ConstPtr debugData() const { return m_debugData; } void setJumpType(JumpType _jumpType) { m_jumpType = _jumpType; } static std::optional parseJumpType(std::string const& _jumpType); @@ -196,7 +219,7 @@ class AssemblyItem /// If m_type == VerbatimBytecode, this holds number of arguments, number of /// return variables and verbatim bytecode. std::optional> m_verbatimBytecode; - langutil::SourceLocation m_location; + langutil::DebugData::ConstPtr m_debugData; JumpType m_jumpType = JumpType::Ordinary; /// Pushed value for operations with data to be determined during assembly stage, /// e.g. PushSubSize, PushTag, PushSub, etc. diff --git a/libevmasm/CommonSubexpressionEliminator.cpp b/libevmasm/CommonSubexpressionEliminator.cpp index de1e5a364aca..cb4eaebff3ce 100644 --- a/libevmasm/CommonSubexpressionEliminator.cpp +++ b/libevmasm/CommonSubexpressionEliminator.cpp @@ -29,6 +29,7 @@ #include #include +#include using namespace solidity; using namespace solidity::evmasm; @@ -57,9 +58,9 @@ std::vector CommonSubexpressionEliminator::getOptimizedItems() if (!m_state.stackElements().empty()) minHeight = std::min(minHeight, m_state.stackElements().begin()->first); for (int height = minHeight; height <= m_initialState.stackHeight(); ++height) - initialStackContents[height] = m_initialState.stackElement(height, SourceLocation()); + initialStackContents[height] = m_initialState.stackElement(height, langutil::DebugData::create()); for (int height = minHeight; height <= m_state.stackHeight(); ++height) - targetStackContents[height] = m_state.stackElement(height, SourceLocation()); + targetStackContents[height] = m_state.stackElement(height, langutil::DebugData::create()); AssemblyItems items = CSECodeGenerator(m_state.expressionClasses(), m_storeOperations).generateCode( m_initialState.sequenceNumber(), @@ -87,23 +88,24 @@ void CommonSubexpressionEliminator::optimizeBreakingItem() ExpressionClasses& classes = m_state.expressionClasses(); SourceLocation const& itemLocation = m_breakingItem->location(); + langutil::DebugData::ConstPtr debugData{langutil::DebugData::create(itemLocation)}; if (*m_breakingItem == AssemblyItem(Instruction::JUMPI)) { AssemblyItem::JumpType jumpType = m_breakingItem->getJumpType(); - Id condition = m_state.stackElement(m_state.stackHeight() - 1, itemLocation); + Id condition = m_state.stackElement(m_state.stackHeight() - 1, debugData); if (classes.knownNonZero(condition)) { - feedItem(AssemblyItem(Instruction::SWAP1, itemLocation), true); - feedItem(AssemblyItem(Instruction::POP, itemLocation), true); + feedItem(AssemblyItem(Instruction::SWAP1, debugData), true); + feedItem(AssemblyItem(Instruction::POP, debugData), true); - AssemblyItem item(Instruction::JUMP, itemLocation); + AssemblyItem item(Instruction::JUMP, debugData); item.setJumpType(jumpType); m_breakingItem = classes.storeItem(item); } else if (classes.knownZero(condition)) { - AssemblyItem it(Instruction::POP, itemLocation); + AssemblyItem it(Instruction::POP, debugData); feedItem(it, true); feedItem(it, true); m_breakingItem = nullptr; @@ -111,12 +113,12 @@ void CommonSubexpressionEliminator::optimizeBreakingItem() } else if (*m_breakingItem == AssemblyItem(Instruction::RETURN)) { - Id size = m_state.stackElement(m_state.stackHeight() - 1, itemLocation); + Id size = m_state.stackElement(m_state.stackHeight() - 1, debugData); if (classes.knownZero(size)) { - feedItem(AssemblyItem(Instruction::POP, itemLocation), true); - feedItem(AssemblyItem(Instruction::POP, itemLocation), true); - AssemblyItem item(Instruction::STOP, itemLocation); + feedItem(AssemblyItem(Instruction::POP, debugData), true); + feedItem(AssemblyItem(Instruction::POP, debugData), true); + AssemblyItem item(Instruction::STOP, debugData); m_breakingItem = classes.storeItem(item); } } @@ -181,16 +183,16 @@ AssemblyItems CSECodeGenerator::generateCode( assertThrow(!m_classPositions[targetItem.second].empty(), OptimizerException, ""); if (m_classPositions[targetItem.second].count(targetItem.first)) continue; - SourceLocation sourceLocation; + langutil::DebugData::ConstPtr debugData; if (m_expressionClasses.representative(targetItem.second).item) - sourceLocation = m_expressionClasses.representative(targetItem.second).item->location(); + debugData = m_expressionClasses.representative(targetItem.second).item->debugData(); int position = classElementPosition(targetItem.second); if (position < targetItem.first) // it is already at its target, we need another copy - appendDup(position, sourceLocation); + appendDup(position, debugData); else - appendOrRemoveSwap(position, sourceLocation); - appendOrRemoveSwap(targetItem.first, sourceLocation); + appendOrRemoveSwap(position, debugData); + appendOrRemoveSwap(targetItem.first, debugData); } // remove surplus elements @@ -263,7 +265,7 @@ void CSECodeGenerator::addDependencies(Id _c) case Instruction::KECCAK256: { Id length = expr.arguments.at(1); - AssemblyItem offsetInstr(Instruction::SUB, expr.item->location()); + AssemblyItem offsetInstr(Instruction::SUB, expr.item->debugData()); Id offsetToStart = m_expressionClasses.find(offsetInstr, {slot, slotToLoadFrom}); u256 const* o = m_expressionClasses.knownConstant(offsetToStart); u256 const* l = m_expressionClasses.knownConstant(length); @@ -334,7 +336,7 @@ void CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced) for (Id arg: arguments | ranges::views::reverse) generateClassElement(arg); - SourceLocation const& itemLocation = expr.item->location(); + langutil::DebugData::ConstPtr itemDebugData = expr.item->debugData(); // The arguments are somewhere on the stack now, so it remains to move them at the correct place. // This is quite difficult as sometimes, the values also have to removed in this process // (if canBeRemoved() returns true) and the two arguments can be equal. For now, this is @@ -342,42 +344,42 @@ void CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced) if (arguments.size() == 1) { if (canBeRemoved(arguments[0], _c)) - appendOrRemoveSwap(classElementPosition(arguments[0]), itemLocation); + appendOrRemoveSwap(classElementPosition(arguments[0]), itemDebugData); else - appendDup(classElementPosition(arguments[0]), itemLocation); + appendDup(classElementPosition(arguments[0]), itemDebugData); } else if (arguments.size() == 2) { if (canBeRemoved(arguments[1], _c)) { - appendOrRemoveSwap(classElementPosition(arguments[1]), itemLocation); + appendOrRemoveSwap(classElementPosition(arguments[1]), itemDebugData); if (arguments[0] == arguments[1]) - appendDup(m_stackHeight, itemLocation); + appendDup(m_stackHeight, itemDebugData); else if (canBeRemoved(arguments[0], _c)) { - appendOrRemoveSwap(m_stackHeight - 1, itemLocation); - appendOrRemoveSwap(classElementPosition(arguments[0]), itemLocation); + appendOrRemoveSwap(m_stackHeight - 1, itemDebugData); + appendOrRemoveSwap(classElementPosition(arguments[0]), itemDebugData); } else - appendDup(classElementPosition(arguments[0]), itemLocation); + appendDup(classElementPosition(arguments[0]), itemDebugData); } else { if (arguments[0] == arguments[1]) { - appendDup(classElementPosition(arguments[0]), itemLocation); - appendDup(m_stackHeight, itemLocation); + appendDup(classElementPosition(arguments[0]), itemDebugData); + appendDup(m_stackHeight, itemDebugData); } else if (canBeRemoved(arguments[0], _c)) { - appendOrRemoveSwap(classElementPosition(arguments[0]), itemLocation); - appendDup(classElementPosition(arguments[1]), itemLocation); - appendOrRemoveSwap(m_stackHeight - 1, itemLocation); + appendOrRemoveSwap(classElementPosition(arguments[0]), itemDebugData); + appendDup(classElementPosition(arguments[1]), itemDebugData); + appendOrRemoveSwap(m_stackHeight - 1, itemDebugData); } else { - appendDup(classElementPosition(arguments[1]), itemLocation); - appendDup(classElementPosition(arguments[0]), itemLocation); + appendDup(classElementPosition(arguments[1]), itemDebugData); + appendDup(classElementPosition(arguments[0]), itemDebugData); } } } @@ -398,7 +400,7 @@ void CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced) !m_generatedItems.empty() && m_generatedItems.back() == AssemblyItem(Instruction::SWAP1)) // this will not append a swap but remove the one that is already there - appendOrRemoveSwap(m_stackHeight - 1, itemLocation); + appendOrRemoveSwap(m_stackHeight - 1, itemDebugData); for (size_t i = 0; i < arguments.size(); ++i) { m_classPositions[m_stack[m_stackHeight - static_cast(i)]].erase(m_stackHeight - static_cast(i)); @@ -467,18 +469,18 @@ bool CSECodeGenerator::removeStackTopIfPossible() return true; } -void CSECodeGenerator::appendDup(int _fromPosition, SourceLocation const& _location) +void CSECodeGenerator::appendDup(int _fromPosition, langutil::DebugData::ConstPtr _debugData) { assertThrow(_fromPosition != c_invalidPosition, OptimizerException, ""); int instructionNum = 1 + m_stackHeight - _fromPosition; assertThrow(instructionNum <= 16, StackTooDeepException, util::stackTooDeepString); assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access."); - appendItem(AssemblyItem(dupInstruction(static_cast(instructionNum)), _location)); + appendItem(AssemblyItem(dupInstruction(static_cast(instructionNum)), std::move(_debugData))); m_stack[m_stackHeight] = m_stack[_fromPosition]; m_classPositions[m_stack[m_stackHeight]].insert(m_stackHeight); } -void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition, SourceLocation const& _location) +void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition, langutil::DebugData::ConstPtr _debugData) { assertThrow(_fromPosition != c_invalidPosition, OptimizerException, ""); if (_fromPosition == m_stackHeight) @@ -486,7 +488,7 @@ void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition, SourceLocation cons int instructionNum = m_stackHeight - _fromPosition; assertThrow(instructionNum <= 16, StackTooDeepException, util::stackTooDeepString); assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access."); - appendItem(AssemblyItem(swapInstruction(static_cast(instructionNum)), _location)); + appendItem(AssemblyItem(swapInstruction(static_cast(instructionNum)), std::move(_debugData))); if (m_stack[m_stackHeight] != m_stack[_fromPosition]) { diff --git a/libevmasm/CommonSubexpressionEliminator.h b/libevmasm/CommonSubexpressionEliminator.h index b0810a9a1dd0..3e963838a4cc 100644 --- a/libevmasm/CommonSubexpressionEliminator.h +++ b/libevmasm/CommonSubexpressionEliminator.h @@ -142,10 +142,10 @@ class CSECodeGenerator bool removeStackTopIfPossible(); /// Appends a dup instruction to m_generatedItems to retrieve the element at the given stack position. - void appendDup(int _fromPosition, langutil::SourceLocation const& _location); + void appendDup(int _fromPosition, langutil::DebugData::ConstPtr _debugData); /// Appends a swap instruction to m_generatedItems to retrieve the element at the given stack position. /// @note this might also remove the last item if it exactly the same swap instruction. - void appendOrRemoveSwap(int _fromPosition, langutil::SourceLocation const& _location); + void appendOrRemoveSwap(int _fromPosition, langutil::DebugData::ConstPtr _debugData); /// Appends the given assembly item. void appendItem(AssemblyItem const& _item); diff --git a/libevmasm/ControlFlowGraph.cpp b/libevmasm/ControlFlowGraph.cpp index 9d2927ed9ca4..5b73fb6d5b21 100644 --- a/libevmasm/ControlFlowGraph.cpp +++ b/libevmasm/ControlFlowGraph.cpp @@ -275,7 +275,7 @@ void ControlFlowGraph::gatherKnowledge() //@todo in the case of JUMPI, add knowledge about the condition to the state // (for both values of the condition) std::set tags = state->tagsInExpression( - state->stackElement(state->stackHeight(), langutil::SourceLocation{}) + state->stackElement(state->stackHeight(), langutil::DebugData::create()) ); state->feedItem(m_items.at(pc++)); diff --git a/libevmasm/ExpressionClasses.cpp b/libevmasm/ExpressionClasses.cpp index dd66eea32840..74de0ae3f402 100644 --- a/libevmasm/ExpressionClasses.cpp +++ b/libevmasm/ExpressionClasses.cpp @@ -33,6 +33,7 @@ #include #include #include +#include using namespace solidity; using namespace solidity::evmasm; @@ -134,11 +135,11 @@ void ExpressionClasses::forceEqual( m_expressions.insert(exp); } -ExpressionClasses::Id ExpressionClasses::newClass(SourceLocation const& _location) +ExpressionClasses::Id ExpressionClasses::newClass(langutil::DebugData::ConstPtr _debugData) { Expression exp; exp.id = static_cast(m_representatives.size()); - exp.item = storeItem(AssemblyItem(UndefinedItem, (u256(1) << 255) + exp.id, _location)); + exp.item = storeItem(AssemblyItem(UndefinedItem, (u256(1) << 255) + exp.id, std::move(_debugData))); m_representatives.push_back(exp); m_expressions.insert(exp); return exp.id; @@ -226,7 +227,7 @@ ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr) std::cout << "to " << match->action().toString() << std::endl; } - return rebuildExpression(ExpressionTemplate(match->action(), _expr.item->location())); + return rebuildExpression(ExpressionTemplate(match->action(), _expr.item->debugData())); } return std::numeric_limits::max(); diff --git a/libevmasm/ExpressionClasses.h b/libevmasm/ExpressionClasses.h index 8d4159fc7be1..510302e4c70c 100644 --- a/libevmasm/ExpressionClasses.h +++ b/libevmasm/ExpressionClasses.h @@ -92,7 +92,7 @@ class ExpressionClasses void forceEqual(Id _id, AssemblyItem const& _item, Ids const& _arguments, bool _copyItem = true); /// @returns the id of a new class which is different to all other classes. - Id newClass(langutil::SourceLocation const& _location); + Id newClass(langutil::DebugData::ConstPtr _debugData); /// @returns true if the values of the given classes are known to be different (on every input). /// @note that this function might still return false for some different inputs. diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index 91f798bf3890..3878ae0fdd75 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -27,6 +27,7 @@ #include #include +#include using namespace solidity; using namespace solidity::evmasm; @@ -114,7 +115,7 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool for (size_t i = 0; i < _item.returnValues(); ++i) setStackElement( m_stackHeight - static_cast(i), - m_expressionClasses->newClass(_item.location()) + m_expressionClasses->newClass(_item.debugData()) ); } else if (_item.type() != Operation) @@ -137,44 +138,44 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool m_stackHeight + 1, stackElement( m_stackHeight - static_cast(instruction) + static_cast(Instruction::DUP1), - _item.location() + _item.debugData() ) ); else if (SemanticInformation::isSwapInstruction(_item)) swapStackElements( m_stackHeight, m_stackHeight - 1 - static_cast(instruction) + static_cast(Instruction::SWAP1), - _item.location() + _item.debugData() ); else if (instruction != Instruction::POP) { std::vector arguments(static_cast(info.args)); for (size_t i = 0; i < static_cast(info.args); ++i) - arguments[i] = stackElement(m_stackHeight - static_cast(i), _item.location()); + arguments[i] = stackElement(m_stackHeight - static_cast(i), _item.debugData()); switch (_item.instruction()) { case Instruction::SSTORE: - op = storeInStorage(arguments[0], arguments[1], _item.location()); + op = storeInStorage(arguments[0], arguments[1], _item.debugData()); break; case Instruction::SLOAD: setStackElement( m_stackHeight + static_cast(_item.deposit()), - loadFromStorage(arguments[0], _item.location()) + loadFromStorage(arguments[0], _item.debugData()) ); break; case Instruction::MSTORE: - op = storeInMemory(arguments[0], arguments[1], _item.location()); + op = storeInMemory(arguments[0], arguments[1], _item.debugData()); break; case Instruction::MLOAD: setStackElement( m_stackHeight + static_cast(_item.deposit()), - loadFromMemory(arguments[0], _item.location()) + loadFromMemory(arguments[0], _item.debugData()) ); break; case Instruction::KECCAK256: setStackElement( m_stackHeight + static_cast(_item.deposit()), - applyKeccak256(arguments.at(0), arguments.at(1), _item.location()) + applyKeccak256(arguments.at(0), arguments.at(1), _item.debugData()) ); break; default: @@ -276,18 +277,18 @@ bool KnownState::operator==(KnownState const& _other) const return (thisIt == m_stackElements.cend() && otherIt == _other.m_stackElements.cend()); } -ExpressionClasses::Id KnownState::stackElement(int _stackHeight, SourceLocation const& _location) +ExpressionClasses::Id KnownState::stackElement(int _stackHeight, langutil::DebugData::ConstPtr _debugData) { if (m_stackElements.count(_stackHeight)) return m_stackElements.at(_stackHeight); // Stack element not found (not assigned yet), create new unknown equivalence class. return m_stackElements[_stackHeight] = - m_expressionClasses->find(AssemblyItem(UndefinedItem, _stackHeight, _location)); + m_expressionClasses->find(AssemblyItem(UndefinedItem, _stackHeight, std::move(_debugData))); } -KnownState::Id KnownState::relativeStackElement(int _stackOffset, SourceLocation const& _location) +KnownState::Id KnownState::relativeStackElement(int _stackOffset, langutil::DebugData::ConstPtr _debugData) { - return stackElement(m_stackHeight + _stackOffset, _location); + return stackElement(m_stackHeight + _stackOffset, std::move(_debugData)); } void KnownState::clearTagUnions() @@ -307,13 +308,13 @@ void KnownState::setStackElement(int _stackHeight, Id _class) void KnownState::swapStackElements( int _stackHeightA, int _stackHeightB, - SourceLocation const& _location + langutil::DebugData::ConstPtr _debugData ) { assertThrow(_stackHeightA != _stackHeightB, OptimizerException, "Swap on same stack elements."); // ensure they are created - stackElement(_stackHeightA, _location); - stackElement(_stackHeightB, _location); + stackElement(_stackHeightA, _debugData); + stackElement(_stackHeightB, _debugData); std::swap(m_stackElements[_stackHeightA], m_stackElements[_stackHeightB]); } @@ -321,7 +322,8 @@ void KnownState::swapStackElements( KnownState::StoreOperation KnownState::storeInStorage( Id _slot, Id _value, - SourceLocation const& _location) + langutil::DebugData::ConstPtr _debugData +) { if (m_storageContent.count(_slot) && m_storageContent[_slot] == _value) // do not execute the storage if we know that the value is already there @@ -336,7 +338,7 @@ KnownState::StoreOperation KnownState::storeInStorage( storageContents.insert(storageItem); m_storageContent = std::move(storageContents); - AssemblyItem item(Instruction::SSTORE, _location); + AssemblyItem item(Instruction::SSTORE, std::move(_debugData)); Id id = m_expressionClasses->find(item, {_slot, _value}, true, m_sequenceNumber); StoreOperation operation{StoreOperation::Storage, _slot, m_sequenceNumber, id}; m_storageContent[_slot] = _value; @@ -346,16 +348,16 @@ KnownState::StoreOperation KnownState::storeInStorage( return operation; } -ExpressionClasses::Id KnownState::loadFromStorage(Id _slot, SourceLocation const& _location) +ExpressionClasses::Id KnownState::loadFromStorage(Id _slot, langutil::DebugData::ConstPtr _debugData) { if (m_storageContent.count(_slot)) return m_storageContent.at(_slot); - AssemblyItem item(Instruction::SLOAD, _location); + AssemblyItem item(Instruction::SLOAD, std::move(_debugData)); return m_storageContent[_slot] = m_expressionClasses->find(item, {_slot}, true, m_sequenceNumber); } -KnownState::StoreOperation KnownState::storeInMemory(Id _slot, Id _value, SourceLocation const& _location) +KnownState::StoreOperation KnownState::storeInMemory(Id _slot, Id _value, langutil::DebugData::ConstPtr _debugData) { if (m_memoryContent.count(_slot) && m_memoryContent[_slot] == _value) // do not execute the store if we know that the value is already there @@ -368,7 +370,7 @@ KnownState::StoreOperation KnownState::storeInMemory(Id _slot, Id _value, Source memoryContents.insert(memoryItem); m_memoryContent = std::move(memoryContents); - AssemblyItem item(Instruction::MSTORE, _location); + AssemblyItem item(Instruction::MSTORE, std::move(_debugData)); Id id = m_expressionClasses->find(item, {_slot, _value}, true, m_sequenceNumber); StoreOperation operation{StoreOperation::Memory, _slot, m_sequenceNumber, id}; m_memoryContent[_slot] = _value; @@ -377,22 +379,22 @@ KnownState::StoreOperation KnownState::storeInMemory(Id _slot, Id _value, Source return operation; } -ExpressionClasses::Id KnownState::loadFromMemory(Id _slot, SourceLocation const& _location) +ExpressionClasses::Id KnownState::loadFromMemory(Id _slot, langutil::DebugData::ConstPtr _debugData) { if (m_memoryContent.count(_slot)) return m_memoryContent.at(_slot); - AssemblyItem item(Instruction::MLOAD, _location); + AssemblyItem item(Instruction::MLOAD, std::move(_debugData)); return m_memoryContent[_slot] = m_expressionClasses->find(item, {_slot}, true, m_sequenceNumber); } KnownState::Id KnownState::applyKeccak256( Id _start, Id _length, - SourceLocation const& _location + langutil::DebugData::ConstPtr _debugData ) { - AssemblyItem keccak256Item(Instruction::KECCAK256, _location); + AssemblyItem keccak256Item(Instruction::KECCAK256, _debugData); // Special logic if length is a short constant, otherwise we cannot tell. u256 const* l = m_expressionClasses->knownConstant(_length); // unknown or too large length @@ -403,10 +405,10 @@ KnownState::Id KnownState::applyKeccak256( for (unsigned i = 0; i < length; i += 32) { Id slot = m_expressionClasses->find( - AssemblyItem(Instruction::ADD, _location), + AssemblyItem(Instruction::ADD, _debugData), {_start, m_expressionClasses->find(u256(i))} ); - arguments.push_back(loadFromMemory(slot, _location)); + arguments.push_back(loadFromMemory(slot, _debugData)); } if (m_knownKeccak256Hashes.count({arguments, length})) return m_knownKeccak256Hashes.at({arguments, length}); @@ -418,7 +420,7 @@ KnownState::Id KnownState::applyKeccak256( for (Id a: arguments) data += toBigEndian(*m_expressionClasses->knownConstant(a)); data.resize(length); - v = m_expressionClasses->find(AssemblyItem(u256(util::keccak256(data)), _location)); + v = m_expressionClasses->find(AssemblyItem(u256(util::keccak256(data)), _debugData)); } else v = m_expressionClasses->find(keccak256Item, {_start, _length}, true, m_sequenceNumber); @@ -443,7 +445,7 @@ KnownState::Id KnownState::tagUnion(std::set _tags) return m_tagUnions.right.at(_tags); else { - Id id = m_expressionClasses->newClass(SourceLocation()); + Id id = m_expressionClasses->newClass(langutil::DebugData::create()); m_tagUnions.right.insert(make_pair(_tags, id)); return id; } diff --git a/libevmasm/KnownState.h b/libevmasm/KnownState.h index 9cc22710fabe..79426c36f4dd 100644 --- a/libevmasm/KnownState.h +++ b/libevmasm/KnownState.h @@ -134,9 +134,9 @@ class KnownState /// Retrieves the current equivalence class for the given stack element (or generates a new /// one if it does not exist yet). - Id stackElement(int _stackHeight, langutil::SourceLocation const& _location); + Id stackElement(int _stackHeight, langutil::DebugData::ConstPtr _debugData); /// @returns the stackElement relative to the current stack height. - Id relativeStackElement(int _stackOffset, langutil::SourceLocation const& _location = {}); + Id relativeStackElement(int _stackOffset, langutil::DebugData::ConstPtr _debugData = {}); /// @returns its set of tags if the given expression class is a known tag union; returns a set /// containing the tag if it is a PushTag expression and the empty set otherwise. @@ -155,22 +155,22 @@ class KnownState /// Assigns a new equivalence class to the next sequence number of the given stack element. void setStackElement(int _stackHeight, Id _class); /// Swaps the given stack elements in their next sequence number. - void swapStackElements(int _stackHeightA, int _stackHeightB, langutil::SourceLocation const& _location); + void swapStackElements(int _stackHeightA, int _stackHeightB, langutil::DebugData::ConstPtr _debugData); /// Increments the sequence number, deletes all storage information that might be overwritten /// and stores the new value at the given slot. /// @returns the store operation, which might be invalid if storage was not modified - StoreOperation storeInStorage(Id _slot, Id _value, langutil::SourceLocation const& _location); + StoreOperation storeInStorage(Id _slot, Id _value,langutil::DebugData::ConstPtr _debugData); /// Retrieves the current value at the given slot in storage or creates a new special sload class. - Id loadFromStorage(Id _slot, langutil::SourceLocation const& _location); + Id loadFromStorage(Id _slot, langutil::DebugData::ConstPtr _debugData); /// Increments the sequence number, deletes all memory information that might be overwritten /// and stores the new value at the given slot. /// @returns the store operation, which might be invalid if memory was not modified - StoreOperation storeInMemory(Id _slot, Id _value, langutil::SourceLocation const& _location); + StoreOperation storeInMemory(Id _slot, Id _value, langutil::DebugData::ConstPtr _debugData); /// Retrieves the current value at the given slot in memory or creates a new special mload class. - Id loadFromMemory(Id _slot, langutil::SourceLocation const& _location); + Id loadFromMemory(Id _slot, langutil::DebugData::ConstPtr _debugData); /// Finds or creates a new expression that applies the Keccak-256 hash function to the contents in memory. - Id applyKeccak256(Id _start, Id _length, langutil::SourceLocation const& _location); + Id applyKeccak256(Id _start, Id _length, langutil::DebugData::ConstPtr _debugData); /// @returns a new or already used Id representing the given set of tags. Id tagUnion(std::set _tags); diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp index e9231c0a56b0..257a3727c01d 100644 --- a/libevmasm/PeepholeOptimiser.cpp +++ b/libevmasm/PeepholeOptimiser.cpp @@ -119,7 +119,7 @@ struct OpPop: SimplePeepholeOptimizerMethod if (instructionInfo(instr, langutil::EVMVersion()).ret == 1 && !instructionInfo(instr, langutil::EVMVersion()).sideEffects) { for (int j = 0; j < instructionInfo(instr, langutil::EVMVersion()).args; j++) - *_out = {Instruction::POP, _op.location()}; + *_out = {Instruction::POP, _op.debugData()}; return true; } } @@ -142,13 +142,13 @@ struct OpStop: SimplePeepholeOptimizerMethod Instruction instr = _op.instruction(); if (!instructionInfo(instr, langutil::EVMVersion()).sideEffects) { - *_out = {Instruction::STOP, _op.location()}; + *_out = {Instruction::STOP, _op.debugData()}; return true; } } else if (_op.type() == Push) { - *_out = {Instruction::STOP, _op.location()}; + *_out = {Instruction::STOP, _op.debugData()}; return true; } } @@ -208,7 +208,7 @@ struct DoublePush: SimplePeepholeOptimizerMethod if (_push1.type() == Push && _push2.type() == Push && _push1.data() == _push2.data()) { *_out = _push1; - *_out = {Instruction::DUP1, _push2.location()}; + *_out = {Instruction::DUP1, _push2.debugData()}; return true; } else @@ -334,7 +334,7 @@ struct EqIsZeroJumpI: SimplePeepholeOptimizerMethod _jumpi == Instruction::JUMPI ) { - *_out = AssemblyItem(Instruction::SUB, _eq.location()); + *_out = AssemblyItem(Instruction::SUB, _eq.debugData()); *_out = _pushTag; *_out = _jumpi; return true; @@ -365,7 +365,7 @@ struct DoubleJump: SimplePeepholeOptimizerMethod _pushTag1.data() == _tag1.data() ) { - *_out = AssemblyItem(Instruction::ISZERO, _jumpi.location()); + *_out = AssemblyItem(Instruction::ISZERO, _jumpi.debugData()); *_out = _pushTag2; *_out = _jumpi; *_out = _tag1; @@ -393,7 +393,7 @@ struct JumpToNext: SimplePeepholeOptimizerMethod ) { if (_jump == Instruction::JUMPI) - *_out = AssemblyItem(Instruction::POP, _jump.location()); + *_out = AssemblyItem(Instruction::POP, _jump.debugData()); *_out = _tag; return true; } diff --git a/libevmasm/SimplificationRules.cpp b/libevmasm/SimplificationRules.cpp index ef2d68c7fdaa..5085b2c3c8f7 100644 --- a/libevmasm/SimplificationRules.cpp +++ b/libevmasm/SimplificationRules.cpp @@ -126,12 +126,12 @@ bool Pattern::matches(Expression const& _expr, ExpressionClasses const& _classes return true; } -AssemblyItem Pattern::toAssemblyItem(SourceLocation const& _location) const +AssemblyItem Pattern::toAssemblyItem(langutil::DebugData::ConstPtr _debugData) const { if (m_type == Operation) - return AssemblyItem(m_instruction, _location); + return AssemblyItem(m_instruction, std::move(_debugData)); else - return AssemblyItem(m_type, data(), _location); + return AssemblyItem(m_type, data(), std::move(_debugData)); } std::string Pattern::toString() const @@ -199,7 +199,7 @@ u256 const& Pattern::data() const return *m_data; } -ExpressionTemplate::ExpressionTemplate(Pattern const& _pattern, SourceLocation const& _location) +ExpressionTemplate::ExpressionTemplate(Pattern const& _pattern, langutil::DebugData::ConstPtr const& _debugData) { if (_pattern.matchGroup()) { @@ -209,10 +209,10 @@ ExpressionTemplate::ExpressionTemplate(Pattern const& _pattern, SourceLocation c else { hasId = false; - item = _pattern.toAssemblyItem(_location); + item = _pattern.toAssemblyItem(_debugData); } for (auto const& arg: _pattern.arguments()) - arguments.emplace_back(arg, _location); + arguments.emplace_back(arg, _debugData); } std::string ExpressionTemplate::toString() const diff --git a/libevmasm/SimplificationRules.h b/libevmasm/SimplificationRules.h index b3f8da3c7f7c..fadad3608492 100644 --- a/libevmasm/SimplificationRules.h +++ b/libevmasm/SimplificationRules.h @@ -111,7 +111,7 @@ class Pattern unsigned matchGroup() const { return m_matchGroup; } bool matches(Expression const& _expr, ExpressionClasses const& _classes) const; - AssemblyItem toAssemblyItem(langutil::SourceLocation const& _location) const; + AssemblyItem toAssemblyItem(langutil::DebugData::ConstPtr _debugData) const; std::vector arguments() const { return m_arguments; } /// @returns the id of the matched expression if this pattern is part of a match group. @@ -149,7 +149,7 @@ struct ExpressionTemplate { using Expression = ExpressionClasses::Expression; using Id = ExpressionClasses::Id; - explicit ExpressionTemplate(Pattern const& _pattern, langutil::SourceLocation const& _location); + explicit ExpressionTemplate(Pattern const& _pattern, langutil::DebugData::ConstPtr const& _debugData); std::string toString() const; bool hasId = false; /// Id of the matched expression, if available. diff --git a/liblangutil/CMakeLists.txt b/liblangutil/CMakeLists.txt index 53be57e79d58..054d01d91881 100644 --- a/liblangutil/CMakeLists.txt +++ b/liblangutil/CMakeLists.txt @@ -3,6 +3,7 @@ set(sources Common.h CharStream.cpp CharStream.h + DebugData.h DebugInfoSelection.cpp DebugInfoSelection.h ErrorReporter.cpp diff --git a/liblangutil/DebugData.h b/liblangutil/DebugData.h new file mode 100644 index 000000000000..70259bd039f3 --- /dev/null +++ b/liblangutil/DebugData.h @@ -0,0 +1,70 @@ +/* + 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::langutil +{ + +struct DebugData +{ + typedef typename std::shared_ptr ConstPtr; + + explicit DebugData( + langutil::SourceLocation _nativeLocation = {}, + langutil::SourceLocation _originLocation = {}, + std::optional _astID = {} + ): + nativeLocation(std::move(_nativeLocation)), + originLocation(std::move(_originLocation)), + astID(_astID) + {} + + static DebugData::ConstPtr create( + langutil::SourceLocation _nativeLocation, + langutil::SourceLocation _originLocation = {}, + std::optional _astID = {} + ) + { + return std::make_shared( + std::move(_nativeLocation), + std::move(_originLocation), + _astID + ); + } + + static DebugData::ConstPtr create() + { + static DebugData::ConstPtr emptyDebugData = create({}); + return emptyDebugData; + } + + /// Location in the Yul code. + langutil::SourceLocation nativeLocation; + /// Location in the original source that the Yul code was produced from. + /// Optional. Only present if the Yul source contains location annotations. + langutil::SourceLocation originLocation; + /// ID in the (Solidity) source AST. + std::optional astID; +}; + +} // namespace solidity::langutil diff --git a/libyul/AST.h b/libyul/AST.h index 71419d807faa..4e3e92de3dad 100644 --- a/libyul/AST.h +++ b/libyul/AST.h @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include @@ -36,77 +36,43 @@ namespace solidity::yul using Type = YulString; -struct DebugData -{ - explicit DebugData( - langutil::SourceLocation _nativeLocation, - langutil::SourceLocation _originLocation = {}, - std::optional _astID = {} - ): - nativeLocation(std::move(_nativeLocation)), - originLocation(std::move(_originLocation)), - astID(std::move(_astID)) - {} - - static std::shared_ptr create( - langutil::SourceLocation _nativeLocation = {}, - langutil::SourceLocation _originLocation = {}, - std::optional _astID = {} - ) - { - return std::make_shared( - std::move(_nativeLocation), - std::move(_originLocation), - std::move(_astID) - ); - } - - /// Location in the Yul code. - langutil::SourceLocation nativeLocation; - /// Location in the original source that the Yul code was produced from. - /// Optional. Only present if the Yul source contains location annotations. - langutil::SourceLocation originLocation; - /// ID in the (Solidity) source AST. - std::optional astID; -}; - -struct TypedName { std::shared_ptr debugData; YulString name; Type type; }; +struct TypedName { langutil::DebugData::ConstPtr debugData; YulString name; Type type; }; using TypedNameList = std::vector; /// Literal number or string (up to 32 bytes) enum class LiteralKind { Number, Boolean, String }; -struct Literal { std::shared_ptr debugData; LiteralKind kind; YulString value; Type type; }; +struct Literal { langutil::DebugData::ConstPtr debugData; LiteralKind kind; YulString value; Type type; }; /// External / internal identifier or label reference -struct Identifier { std::shared_ptr debugData; YulString name; }; +struct Identifier { langutil::DebugData::ConstPtr debugData; YulString name; }; /// Assignment ("x := mload(20:u256)", expects push-1-expression on the right hand /// side and requires x to occupy exactly one stack slot. /// /// Multiple assignment ("x, y := f()"), where the left hand side variables each occupy /// a single stack slot and expects a single expression on the right hand returning /// the same amount of items as the number of variables. -struct Assignment { std::shared_ptr debugData; std::vector variableNames; std::unique_ptr value; }; -struct FunctionCall { std::shared_ptr debugData; Identifier functionName; std::vector arguments; }; +struct Assignment { langutil::DebugData::ConstPtr debugData; std::vector variableNames; std::unique_ptr value; }; +struct FunctionCall { langutil::DebugData::ConstPtr debugData; Identifier functionName; std::vector arguments; }; /// Statement that contains only a single expression -struct ExpressionStatement { std::shared_ptr debugData; Expression expression; }; +struct ExpressionStatement { langutil::DebugData::ConstPtr debugData; Expression expression; }; /// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted -struct VariableDeclaration { std::shared_ptr debugData; TypedNameList variables; std::unique_ptr value; }; +struct VariableDeclaration { langutil::DebugData::ConstPtr debugData; TypedNameList variables; std::unique_ptr value; }; /// Block that creates a scope (frees declared stack variables) -struct Block { std::shared_ptr debugData; std::vector statements; }; +struct Block { langutil::DebugData::ConstPtr debugData; std::vector statements; }; /// Function definition ("function f(a, b) -> (d, e) { ... }") -struct FunctionDefinition { std::shared_ptr debugData; YulString name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; +struct FunctionDefinition { langutil::DebugData::ConstPtr debugData; YulString name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; /// Conditional execution without "else" part. -struct If { std::shared_ptr debugData; std::unique_ptr condition; Block body; }; +struct If { langutil::DebugData::ConstPtr debugData; std::unique_ptr condition; Block body; }; /// Switch case or default case -struct Case { std::shared_ptr debugData; std::unique_ptr value; Block body; }; +struct Case { langutil::DebugData::ConstPtr debugData; std::unique_ptr value; Block body; }; /// Switch statement -struct Switch { std::shared_ptr debugData; std::unique_ptr expression; std::vector cases; }; -struct ForLoop { std::shared_ptr debugData; Block pre; std::unique_ptr condition; Block post; Block body; }; +struct Switch { langutil::DebugData::ConstPtr debugData; std::unique_ptr expression; std::vector cases; }; +struct ForLoop { langutil::DebugData::ConstPtr debugData; Block pre; std::unique_ptr condition; Block post; Block body; }; /// Break statement (valid within for loop) -struct Break { std::shared_ptr debugData; }; +struct Break { langutil::DebugData::ConstPtr debugData; }; /// Continue statement (valid within for loop) -struct Continue { std::shared_ptr debugData; }; +struct Continue { langutil::DebugData::ConstPtr debugData; }; /// Leave statement (valid within function) -struct Leave { std::shared_ptr debugData; }; +struct Leave { langutil::DebugData::ConstPtr debugData; }; /// Extracts the IR source location from a Yul node. template inline langutil::SourceLocation nativeLocationOf(T const& _node) @@ -133,13 +99,13 @@ template inline langutil::SourceLocation originLocationOf(std::v } /// Extracts the debug data from a Yul node. -template inline std::shared_ptr debugDataOf(T const& _node) +template inline langutil::DebugData::ConstPtr debugDataOf(T const& _node) { return _node.debugData; } /// Extracts the debug data from a Yul node. -template inline std::shared_ptr debugDataOf(std::variant const& _node) +template inline langutil::DebugData::ConstPtr debugDataOf(std::variant const& _node) { return std::visit([](auto const& _arg) { return debugDataOf(_arg); }, _node); } diff --git a/libyul/ASTForward.h b/libyul/ASTForward.h index 0e61c6ca1263..a4dc5be3f8eb 100644 --- a/libyul/ASTForward.h +++ b/libyul/ASTForward.h @@ -28,7 +28,6 @@ namespace solidity::yul { -struct DebugData; enum class LiteralKind; struct Literal; struct Label; diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 5d3ec84a4b97..3635b096af99 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -59,7 +59,7 @@ std::optional toInt(std::string const& _value) } -std::shared_ptr Parser::createDebugData() const +langutil::DebugData::ConstPtr Parser::createDebugData() const { switch (m_useSourceLocationFrom) { @@ -74,7 +74,7 @@ std::shared_ptr Parser::createDebugData() const } void Parser::updateLocationEndFrom( - std::shared_ptr& _debugData, + langutil::DebugData::ConstPtr& _debugData, SourceLocation const& _location ) const { @@ -286,7 +286,7 @@ std::optional>> Parser::parseASTI Block Parser::parseBlock() { RecursionGuard recursionGuard(*this); - Block block = createWithLocation(); + Block block = createWithDebugData(); expectToken(Token::LBrace); while (currentToken() != Token::RBrace) block.statements.emplace_back(parseStatement()); @@ -308,7 +308,7 @@ Statement Parser::parseStatement() return parseBlock(); case Token::If: { - If _if = createWithLocation(); + If _if = createWithDebugData(); advance(); _if.condition = std::make_unique(parseExpression()); _if.body = parseBlock(); @@ -317,7 +317,7 @@ Statement Parser::parseStatement() } case Token::Switch: { - Switch _switch = createWithLocation(); + Switch _switch = createWithDebugData(); advance(); _switch.expression = std::make_unique(parseExpression()); while (currentToken() == Token::Case) @@ -337,21 +337,21 @@ Statement Parser::parseStatement() return parseForLoop(); case Token::Break: { - Statement stmt{createWithLocation()}; + Statement stmt{createWithDebugData()}; checkBreakContinuePosition("break"); advance(); return stmt; } case Token::Continue: { - Statement stmt{createWithLocation()}; + Statement stmt{createWithDebugData()}; checkBreakContinuePosition("continue"); advance(); return stmt; } case Token::Leave: { - Statement stmt{createWithLocation()}; + Statement stmt{createWithDebugData()}; if (!m_insideFunction) m_errorReporter.syntaxError(8149_error, currentLocation(), "Keyword \"leave\" can only be used inside a function."); advance(); @@ -428,7 +428,7 @@ Statement Parser::parseStatement() Case Parser::parseCase() { RecursionGuard recursionGuard(*this); - Case _case = createWithLocation(); + Case _case = createWithDebugData(); if (currentToken() == Token::Default) advance(); else if (currentToken() == Token::Case) @@ -452,7 +452,7 @@ ForLoop Parser::parseForLoop() ForLoopComponent outerForLoopComponent = m_currentForLoopComponent; - ForLoop forLoop = createWithLocation(); + ForLoop forLoop = createWithDebugData(); expectToken(Token::For); m_currentForLoopComponent = ForLoopComponent::ForLoopPre; forLoop.pre = parseBlock(); @@ -559,7 +559,7 @@ std::variant Parser::parseLiteralOrIdentifier() VariableDeclaration Parser::parseVariableDeclaration() { RecursionGuard recursionGuard(*this); - VariableDeclaration varDecl = createWithLocation(); + VariableDeclaration varDecl = createWithDebugData(); expectToken(Token::Let); while (true) { @@ -595,7 +595,7 @@ FunctionDefinition Parser::parseFunctionDefinition() ForLoopComponent outerForLoopComponent = m_currentForLoopComponent; m_currentForLoopComponent = ForLoopComponent::None; - FunctionDefinition funDef = createWithLocation(); + FunctionDefinition funDef = createWithDebugData(); expectToken(Token::Function); funDef.name = expectAsmIdentifier(); expectToken(Token::LParen); @@ -657,7 +657,7 @@ FunctionCall Parser::parseCall(std::variant&& _initialOp) TypedName Parser::parseTypedName() { RecursionGuard recursionGuard(*this); - TypedName typedName = createWithLocation(); + TypedName typedName = createWithDebugData(); typedName.name = expectAsmIdentifier(); if (currentToken() == Token::Colon) { diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index da4f09ed2212..3e0af66acc10 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -118,15 +118,15 @@ class Parser: public langutil::ParserBase ); /// Creates a DebugData object with the correct source location set. - std::shared_ptr createDebugData() const; + langutil::DebugData::ConstPtr createDebugData() const; void updateLocationEndFrom( - std::shared_ptr& _debugData, + langutil::DebugData::ConstPtr& _debugData, langutil::SourceLocation const& _location ) const; - /// Creates an inline assembly node with the current source location. - template T createWithLocation() const + /// Creates an inline assembly node with the current debug data. + template T createWithDebugData() const { T r; r.debugData = createDebugData(); diff --git a/libyul/AsmPrinter.cpp b/libyul/AsmPrinter.cpp index bee213f8cafb..b27af037131b 100644 --- a/libyul/AsmPrinter.cpp +++ b/libyul/AsmPrinter.cpp @@ -305,7 +305,7 @@ std::string AsmPrinter::formatSourceLocation( return sourceLocation + (solidityCodeSnippet.empty() ? "" : " ") + solidityCodeSnippet; } -std::string AsmPrinter::formatDebugData(std::shared_ptr const& _debugData, bool _statement) +std::string AsmPrinter::formatDebugData(langutil::DebugData::ConstPtr const& _debugData, bool _statement) { if (!_debugData || m_debugInfoSelection.none()) return ""; diff --git a/libyul/AsmPrinter.h b/libyul/AsmPrinter.h index 7c67c14acfdc..d3f80b9a09d5 100644 --- a/libyul/AsmPrinter.h +++ b/libyul/AsmPrinter.h @@ -30,7 +30,7 @@ #include #include -#include +#include #include @@ -93,7 +93,7 @@ class AsmPrinter private: std::string formatTypedName(TypedName _variable); std::string appendTypeName(YulString _type, bool _isBoolLiteral = false) const; - std::string formatDebugData(std::shared_ptr const& _debugData, bool _statement); + std::string formatDebugData(langutil::DebugData::ConstPtr const& _debugData, bool _statement); template std::string formatDebugData(T const& _node) { diff --git a/libyul/backends/evm/ConstantOptimiser.h b/libyul/backends/evm/ConstantOptimiser.h index a60d2b2f5601..280f50bf9b97 100644 --- a/libyul/backends/evm/ConstantOptimiser.h +++ b/libyul/backends/evm/ConstantOptimiser.h @@ -27,7 +27,7 @@ #include #include -#include +#include #include @@ -74,7 +74,7 @@ class RepresentationFinder RepresentationFinder( EVMDialect const& _dialect, GasMeter const& _meter, - std::shared_ptr _debugData, + langutil::DebugData::ConstPtr _debugData, std::map& _cache ): m_dialect(_dialect), @@ -100,7 +100,7 @@ class RepresentationFinder EVMDialect const& m_dialect; GasMeter const& m_meter; - std::shared_ptr m_debugData; + langutil::DebugData::ConstPtr m_debugData; /// Counter for the complexity of optimization, will stop when it reaches zero. size_t m_maxSteps = 10000; std::map& m_cache; diff --git a/libyul/backends/evm/ControlFlowGraph.h b/libyul/backends/evm/ControlFlowGraph.h index 853a9e705dc2..980787c52297 100644 --- a/libyul/backends/evm/ControlFlowGraph.h +++ b/libyul/backends/evm/ControlFlowGraph.h @@ -76,7 +76,7 @@ struct FunctionReturnLabelSlot struct VariableSlot { std::reference_wrapper variable; - std::shared_ptr debugData{}; + langutil::DebugData::ConstPtr debugData{}; bool operator==(VariableSlot const& _rhs) const { return &variable.get() == &_rhs.variable.get(); } bool operator<(VariableSlot const& _rhs) const { return &variable.get() < &_rhs.variable.get(); } static constexpr bool canBeFreelyGenerated = false; @@ -85,7 +85,7 @@ struct VariableSlot struct LiteralSlot { u256 value; - std::shared_ptr debugData{}; + langutil::DebugData::ConstPtr debugData{}; bool operator==(LiteralSlot const& _rhs) const { return value == _rhs.value; } bool operator<(LiteralSlot const& _rhs) const { return value < _rhs.value; } static constexpr bool canBeFreelyGenerated = true; @@ -132,7 +132,7 @@ struct CFG struct BuiltinCall { - std::shared_ptr debugData; + langutil::DebugData::ConstPtr debugData; std::reference_wrapper builtin; std::reference_wrapper functionCall; /// Number of proper arguments with a position on the stack, excluding literal arguments. @@ -142,7 +142,7 @@ struct CFG }; struct FunctionCall { - std::shared_ptr debugData; + langutil::DebugData::ConstPtr debugData; std::reference_wrapper function; std::reference_wrapper functionCall; /// True, if the call is recursive, i.e. entering the function involves a control flow path (potentially involving @@ -153,7 +153,7 @@ struct CFG }; struct Assignment { - std::shared_ptr debugData; + langutil::DebugData::ConstPtr debugData; /// The variables being assigned to also occur as ``output`` in the ``Operation`` containing /// the assignment, but are also stored here for convenience. std::vector variables; @@ -176,25 +176,25 @@ struct CFG struct MainExit {}; struct ConditionalJump { - std::shared_ptr debugData; + langutil::DebugData::ConstPtr debugData; StackSlot condition; BasicBlock* nonZero = nullptr; BasicBlock* zero = nullptr; }; struct Jump { - std::shared_ptr debugData; + langutil::DebugData::ConstPtr debugData; BasicBlock* target = nullptr; /// The only backwards jumps are jumps from loop post to loop condition. bool backwards = false; }; struct FunctionReturn { - std::shared_ptr debugData; + langutil::DebugData::ConstPtr debugData; CFG::FunctionInfo* info = nullptr; }; struct Terminated {}; - std::shared_ptr debugData; + langutil::DebugData::ConstPtr debugData; std::vector entries; std::vector operations; /// True, if the block is the beginning of a disconnected subgraph. That is, if no block that is reachable @@ -210,7 +210,7 @@ struct CFG struct FunctionInfo { - std::shared_ptr debugData; + langutil::DebugData::ConstPtr debugData; Scope::Function const& function; FunctionDefinition const& functionDefinition; BasicBlock* entry = nullptr; @@ -238,7 +238,7 @@ struct CFG /// the switch case literals when transforming the control flow of a switch to a sequence of conditional jumps. std::list ghostCalls; - BasicBlock& makeBlock(std::shared_ptr _debugData) + BasicBlock& makeBlock(langutil::DebugData::ConstPtr _debugData) { return blocks.emplace_back(BasicBlock{std::move(_debugData), {}, {}}); } diff --git a/libyul/backends/evm/ControlFlowGraphBuilder.cpp b/libyul/backends/evm/ControlFlowGraphBuilder.cpp index 9a0a73b35fbf..4b0da1e80d91 100644 --- a/libyul/backends/evm/ControlFlowGraphBuilder.cpp +++ b/libyul/backends/evm/ControlFlowGraphBuilder.cpp @@ -333,7 +333,7 @@ void ControlFlowGraphBuilder::operator()(If const& _if) void ControlFlowGraphBuilder::operator()(Switch const& _switch) { yulAssert(m_currentBlock, ""); - std::shared_ptr preSwitchDebugData = debugDataOf(_switch); + langutil::DebugData::ConstPtr preSwitchDebugData = debugDataOf(_switch); auto ghostVariableId = m_graph.ghostVariables.size(); YulString ghostVariableName("GHOST[" + std::to_string(ghostVariableId) + "]"); @@ -393,7 +393,7 @@ void ControlFlowGraphBuilder::operator()(Switch const& _switch) void ControlFlowGraphBuilder::operator()(ForLoop const& _loop) { - std::shared_ptr preLoopDebugData = debugDataOf(_loop); + langutil::DebugData::ConstPtr preLoopDebugData = debugDataOf(_loop); ScopedSaveAndRestore scopeRestore(m_scope, m_info.scopes.at(&_loop.pre).get()); (*this)(_loop.pre); @@ -608,7 +608,7 @@ Scope::Variable const& ControlFlowGraphBuilder::lookupVariable(YulString _name) } void ControlFlowGraphBuilder::makeConditionalJump( - std::shared_ptr _debugData, + langutil::DebugData::ConstPtr _debugData, StackSlot _condition, CFG::BasicBlock& _nonZero, CFG::BasicBlock& _zero @@ -627,7 +627,7 @@ void ControlFlowGraphBuilder::makeConditionalJump( } void ControlFlowGraphBuilder::jump( - std::shared_ptr _debugData, + langutil::DebugData::ConstPtr _debugData, CFG::BasicBlock& _target, bool backwards ) diff --git a/libyul/backends/evm/ControlFlowGraphBuilder.h b/libyul/backends/evm/ControlFlowGraphBuilder.h index ffe935b0dd72..5007d361089b 100644 --- a/libyul/backends/evm/ControlFlowGraphBuilder.h +++ b/libyul/backends/evm/ControlFlowGraphBuilder.h @@ -67,13 +67,13 @@ class ControlFlowGraphBuilder Scope::Variable const& lookupVariable(YulString _name) const; /// Resets m_currentBlock to enforce a subsequent explicit reassignment. void makeConditionalJump( - std::shared_ptr _debugData, + langutil::DebugData::ConstPtr _debugData, StackSlot _condition, CFG::BasicBlock& _nonZero, CFG::BasicBlock& _zero ); void jump( - std::shared_ptr _debugData, + langutil::DebugData::ConstPtr _debugData, CFG::BasicBlock& _target, bool _backwards = false ); diff --git a/libyul/backends/evm/OptimizedEVMCodeTransform.cpp b/libyul/backends/evm/OptimizedEVMCodeTransform.cpp index 3df75bb88f50..a3052a542f64 100644 --- a/libyul/backends/evm/OptimizedEVMCodeTransform.cpp +++ b/libyul/backends/evm/OptimizedEVMCodeTransform.cpp @@ -237,7 +237,7 @@ void OptimizedEVMCodeTransform::validateSlot(StackSlot const& _slot, Expression }, _expression); } -void OptimizedEVMCodeTransform::createStackLayout(std::shared_ptr _debugData, Stack _targetStack) +void OptimizedEVMCodeTransform::createStackLayout(langutil::DebugData::ConstPtr _debugData, Stack _targetStack) { static constexpr auto slotVariableName = [](StackSlot const& _slot) { return std::visit(util::GenericVisitor{ diff --git a/libyul/backends/evm/OptimizedEVMCodeTransform.h b/libyul/backends/evm/OptimizedEVMCodeTransform.h index ed03c1453015..7e648ca964bb 100644 --- a/libyul/backends/evm/OptimizedEVMCodeTransform.h +++ b/libyul/backends/evm/OptimizedEVMCodeTransform.h @@ -83,7 +83,7 @@ class OptimizedEVMCodeTransform /// Shuffles m_stack to the desired @a _targetStack while emitting the shuffling code to m_assembly. /// Sets the source locations to the one in @a _debugData. - void createStackLayout(std::shared_ptr _debugData, Stack _targetStack); + void createStackLayout(langutil::DebugData::ConstPtr _debugData, Stack _targetStack); /// Generate code for the given block @a _block. /// Expects the current stack layout m_stack to be a stack layout that is compatible with the diff --git a/libyul/optimiser/ConditionalSimplifier.cpp b/libyul/optimiser/ConditionalSimplifier.cpp index b08b113de0ca..7119091257d3 100644 --- a/libyul/optimiser/ConditionalSimplifier.cpp +++ b/libyul/optimiser/ConditionalSimplifier.cpp @@ -78,7 +78,7 @@ void ConditionalSimplifier::operator()(Block& _block) ) { YulString condition = std::get(*_if.condition).name; - std::shared_ptr debugData = _if.debugData; + langutil::DebugData::ConstPtr debugData = _if.debugData; return make_vector( std::move(_s), Assignment{ diff --git a/libyul/optimiser/ControlFlowSimplifier.cpp b/libyul/optimiser/ControlFlowSimplifier.cpp index 771b4e86bfc7..10f479a421b4 100644 --- a/libyul/optimiser/ControlFlowSimplifier.cpp +++ b/libyul/optimiser/ControlFlowSimplifier.cpp @@ -37,7 +37,7 @@ namespace { ExpressionStatement makeDiscardCall( - std::shared_ptr const& _debugData, + langutil::DebugData::ConstPtr const& _debugData, BuiltinFunction const& _discardFunction, Expression&& _expression ) @@ -196,7 +196,7 @@ OptionalStatements ControlFlowSimplifier::reduceSingleCaseSwitch(Switch& _switch yulAssert(_switchStmt.cases.size() == 1, "Expected only one case!"); auto& switchCase = _switchStmt.cases.front(); - std::shared_ptr debugData = debugDataOf(*_switchStmt.expression); + langutil::DebugData::ConstPtr debugData = debugDataOf(*_switchStmt.expression); YulString type = m_typeInfo.typeOf(*_switchStmt.expression); if (switchCase.value) { diff --git a/libyul/optimiser/ExpressionSplitter.cpp b/libyul/optimiser/ExpressionSplitter.cpp index 986bcb097f33..6f0834d7760e 100644 --- a/libyul/optimiser/ExpressionSplitter.cpp +++ b/libyul/optimiser/ExpressionSplitter.cpp @@ -98,7 +98,7 @@ void ExpressionSplitter::outlineExpression(Expression& _expr) visit(_expr); - std::shared_ptr debugData = debugDataOf(_expr); + langutil::DebugData::ConstPtr debugData = debugDataOf(_expr); YulString var = m_nameDispenser.newName({}); YulString type = m_typeInfo.typeOf(_expr); m_statementsToPrefix.emplace_back(VariableDeclaration{ diff --git a/libyul/optimiser/ForLoopConditionIntoBody.cpp b/libyul/optimiser/ForLoopConditionIntoBody.cpp index 2dd0832c4288..33d8496f08b5 100644 --- a/libyul/optimiser/ForLoopConditionIntoBody.cpp +++ b/libyul/optimiser/ForLoopConditionIntoBody.cpp @@ -38,7 +38,7 @@ void ForLoopConditionIntoBody::operator()(ForLoop& _forLoop) !std::holds_alternative(*_forLoop.condition) ) { - std::shared_ptr debugData = debugDataOf(*_forLoop.condition); + langutil::DebugData::ConstPtr debugData = debugDataOf(*_forLoop.condition); _forLoop.body.statements.emplace( begin(_forLoop.body.statements), diff --git a/libyul/optimiser/ForLoopConditionOutOfBody.cpp b/libyul/optimiser/ForLoopConditionOutOfBody.cpp index 363ecdf0c02d..e2451bc6c6e9 100644 --- a/libyul/optimiser/ForLoopConditionOutOfBody.cpp +++ b/libyul/optimiser/ForLoopConditionOutOfBody.cpp @@ -54,7 +54,7 @@ void ForLoopConditionOutOfBody::operator()(ForLoop& _forLoop) return; YulString iszero = m_dialect.booleanNegationFunction()->name; - std::shared_ptr debugData = debugDataOf(*firstStatement.condition); + langutil::DebugData::ConstPtr debugData = debugDataOf(*firstStatement.condition); if ( std::holds_alternative(*firstStatement.condition) && diff --git a/libyul/optimiser/SSATransform.cpp b/libyul/optimiser/SSATransform.cpp index 74a7701494d4..675d21acc343 100644 --- a/libyul/optimiser/SSATransform.cpp +++ b/libyul/optimiser/SSATransform.cpp @@ -84,7 +84,7 @@ void IntroduceSSA::operator()(Block& _block) // Replace "let a := v" by "let a_1 := v let a := a_1" // Replace "let a, b := v" by "let a_1, b_1 := v let a := a_1 let b := b_2" - std::shared_ptr debugData = varDecl.debugData; + langutil::DebugData::ConstPtr debugData = varDecl.debugData; std::vector statements; statements.emplace_back(VariableDeclaration{debugData, {}, std::move(varDecl.value)}); TypedNameList newVariables; @@ -111,7 +111,7 @@ void IntroduceSSA::operator()(Block& _block) // Replace "a := v" by "let a_1 := v a := v" // Replace "a, b := v" by "let a_1, b_1 := v a := a_1 b := b_2" - std::shared_ptr debugData = assignment.debugData; + langutil::DebugData::ConstPtr debugData = assignment.debugData; std::vector statements; statements.emplace_back(VariableDeclaration{debugData, {}, std::move(assignment.value)}); TypedNameList newVariables; diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index f8bbcf678a1c..7fe56026ac95 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -234,7 +234,7 @@ evmasm::Instruction Pattern::instruction() const return m_instruction; } -Expression Pattern::toExpression(std::shared_ptr const& _debugData, langutil::EVMVersion _evmVersion) const +Expression Pattern::toExpression(langutil::DebugData::ConstPtr const& _debugData, langutil::EVMVersion _evmVersion) const { if (matchGroup()) return ASTCopier().translate(matchGroupValue()); diff --git a/libyul/optimiser/SimplificationRules.h b/libyul/optimiser/SimplificationRules.h index e36730f05425..5e498bd5c9a4 100644 --- a/libyul/optimiser/SimplificationRules.h +++ b/libyul/optimiser/SimplificationRules.h @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include @@ -42,6 +42,8 @@ struct Dialect; struct AssignedValue; class Pattern; +using DebugData = langutil::DebugData; + /** * Container for all simplification rules. */ @@ -131,7 +133,7 @@ class Pattern /// Turns this pattern into an actual expression. Should only be called /// for patterns resulting from an action, i.e. with match groups assigned. - Expression toExpression(std::shared_ptr const& _debugData, langutil::EVMVersion _evmVersion) const; + Expression toExpression(langutil::DebugData::ConstPtr const& _debugData, langutil::EVMVersion _evmVersion) const; private: Expression const& matchGroupValue() const; diff --git a/libyul/optimiser/StackToMemoryMover.cpp b/libyul/optimiser/StackToMemoryMover.cpp index ab39669b2d9a..dedb5cd382ce 100644 --- a/libyul/optimiser/StackToMemoryMover.cpp +++ b/libyul/optimiser/StackToMemoryMover.cpp @@ -36,7 +36,7 @@ namespace { std::vector generateMemoryStore( Dialect const& _dialect, - std::shared_ptr const& _debugData, + langutil::DebugData::ConstPtr const& _debugData, YulString _mpos, Expression _value ) @@ -55,7 +55,7 @@ std::vector generateMemoryStore( return result; } -FunctionCall generateMemoryLoad(Dialect const& _dialect, std::shared_ptr const& _debugData, YulString _mpos) +FunctionCall generateMemoryLoad(Dialect const& _dialect, langutil::DebugData::ConstPtr const& _debugData, YulString _mpos) { BuiltinFunction const* memoryLoadFunction = _dialect.memoryLoadFunction(_dialect.defaultType); yulAssert(memoryLoadFunction, ""); diff --git a/test/tools/yulInterpreter/Inspector.cpp b/test/tools/yulInterpreter/Inspector.cpp index 9c4701d00a12..7746483a367a 100644 --- a/test/tools/yulInterpreter/Inspector.cpp +++ b/test/tools/yulInterpreter/Inspector.cpp @@ -56,7 +56,7 @@ void InspectedInterpreter::run( InspectedInterpreter{_inspector, _state, _dialect, scope, _disableExternalCalls, _disableMemoryTrace}(_ast); } -Inspector::NodeAction Inspector::queryUser(DebugData const& _data, std::map const& _variables) +Inspector::NodeAction Inspector::queryUser(langutil::DebugData const& _data, std::map const& _variables) { if (m_stepMode == NodeAction::RunNode) { @@ -131,7 +131,7 @@ Inspector::NodeAction Inspector::queryUser(DebugData const& _data, std::map(_data.nativeLocation.start), diff --git a/test/tools/yulInterpreter/Inspector.h b/test/tools/yulInterpreter/Inspector.h index 611279c6fe1c..f4518c7087d3 100644 --- a/test/tools/yulInterpreter/Inspector.h +++ b/test/tools/yulInterpreter/Inspector.h @@ -53,13 +53,13 @@ class Inspector * @returns NodeAction::RunNode if the current AST node (and all children nodes!) should be * processed without stopping, else NodeAction::StepThroughNode. */ - NodeAction queryUser(DebugData const& _data, std::map const& _variables); + NodeAction queryUser(langutil::DebugData const& _data, std::map const& _variables); void stepMode(NodeAction _action) { m_stepMode = _action; } std::string const& source() const { return m_source; } - void interactiveVisit(DebugData const& _debugData, std::map const& _variables, std::function _visitNode) + void interactiveVisit(langutil::DebugData const& _debugData, std::map const& _variables, std::function _visitNode) { Inspector::NodeAction action = queryUser(_debugData, _variables); @@ -78,7 +78,7 @@ class Inspector } private: - std::string currentSource(DebugData const& _data) const; + std::string currentSource(langutil::DebugData const& _data) const; /// Source of the file std::string const& m_source; From c7b8dcc52f28d68862ef298a94c7ce44f8080346 Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Fri, 16 Feb 2024 20:40:05 +0100 Subject: [PATCH 100/189] [test] Add /usr/local/lib to search paths for macOS. --- test/Common.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/Common.cpp b/test/Common.cpp index 14d90d837e8d..bb4d2db7ebce 100644 --- a/test/Common.cpp +++ b/test/Common.cpp @@ -78,7 +78,10 @@ std::optional findInDefaultPath(std::string const& lib_name) fs::current_path() / ".." / "deps" / "lib", fs::current_path() / ".." / ".." / "deps", fs::current_path() / ".." / ".." / "deps" / "lib", - fs::current_path() + fs::current_path(), +#ifdef __APPLE__ + fs::current_path().root_path() / fs::path("usr") / "local" / "lib", +#endif }; for (auto const& basePath: searchPath) { From 9b8ed03823569a48005f0826175a08d5e1281f80 Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Sun, 18 Feb 2024 20:53:03 +0100 Subject: [PATCH 101/189] Fix typo in Assembly::fromJSON(..). --- libevmasm/Assembly.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index b6e4d0006c57..ab6ac2c63f24 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -555,7 +555,7 @@ std::pair, std::vector> Assembly::fromJSO result->importAssemblyItemsFromJSON(_json[".code"], _level == 0 ? parsedSourceList : _sourceList); - if (_json[".auxdata"]) + if (_json.isMember(".auxdata")) { solRequire(_json[".auxdata"].isString(), AssemblyImportException, "Optional member '.auxdata' is not a string."); result->m_auxiliaryData = fromHex(_json[".auxdata"].asString()); From 5aed7515efd427b8f13f0abf3313539166292a3c Mon Sep 17 00:00:00 2001 From: r0qs Date: Mon, 19 Feb 2024 10:22:12 +0100 Subject: [PATCH 102/189] Use ethers version 6.11.0 on openzeppelin external tests --- test/externalTests/zeppelin.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/externalTests/zeppelin.sh b/test/externalTests/zeppelin.sh index 64e6f5f0ec90..9f72aa4ec958 100755 --- a/test/externalTests/zeppelin.sh +++ b/test/externalTests/zeppelin.sh @@ -110,6 +110,13 @@ function zeppelin_test # Maybe related to the use of dynamic imports here: https://github.com/NomicFoundation/hardhat/commit/16ae15642951ac324ef7093a3342f7cf3a2a49a4 npm install @nomicfoundation/hardhat-chai-matchers@2.0.3 + # TODO: Remove when OpenZeppelin update to ethers 6.11.1+ + # Prior versions of ethers accepts an object instead of a string as an argument to the toUtf8Bytes function. + # However, starting from Ethers version 6.11.1, string arguments are enforced, + # which causes the introduced assertion to fail in some OpenZeppelin tests. + # See: https://github.com/ethers-io/ethers.js/issues/4583 + npm install ethers@6.11.0 + replace_version_pragmas for preset in $SELECTED_PRESETS; do From c21490f3c2e5c66e224029fc7f9cb482168bd7fe Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Mon, 19 Feb 2024 13:34:15 +0100 Subject: [PATCH 103/189] [ci] Remove usage of ETH_EVMONE environment variable. --- .circleci/config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b29c46bf8b9e..f3843a689846 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -498,7 +498,6 @@ defaults: TERM: xterm MAKEFLAGS: -j5 CPUs: 5 - ETH_EVMONE: /usr/local/lib/libevmone.dylib - base_python_small: &base_python_small docker: From 18cabee1e4f60aae045ee9a0a0f2aba6d0d2c279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 9 Jan 2024 14:08:38 +0100 Subject: [PATCH 104/189] CompilerStack: Don't swallow details include in OptimizerException - By catching and asserting it we hide the line number and message. - OptimizerException inherits from util::Exception so just letting it through will have the same effect as asserting. --- libsolidity/interface/CompilerStack.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 9b5c87818bf2..69394417586e 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -1480,15 +1480,8 @@ void CompilerStack::compileContract( solAssert(!m_viaIR, ""); bytes cborEncodedMetadata = createCBORMetadata(compiledContract, /* _forIR */ false); - try - { - // Run optimiser and compile the contract. - compiler->compileContract(_contract, _otherCompilers, cborEncodedMetadata); - } - catch(evmasm::OptimizerException const&) - { - solAssert(false, "Optimizer exception during compilation"); - } + // Run optimiser and compile the contract. + compiler->compileContract(_contract, _otherCompilers, cborEncodedMetadata); _otherCompilers[compiledContract.contract] = compiler; From f5188158f7edbc672cfc2b5b9125730bbf1886b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 9 Jan 2024 16:44:27 +0100 Subject: [PATCH 105/189] Remove obsolete gas cost tiers - They're no longer used because the cost depends on the EVM version. --- libevmasm/Assembly.cpp | 1 - libevmasm/GasMeter.cpp | 24 +++++++++++++----------- libevmasm/Instruction.cpp | 12 ++++++------ libevmasm/Instruction.h | 2 -- solc/CommandLineInterface.cpp | 2 -- 5 files changed, 19 insertions(+), 22 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index ab6ac2c63f24..3f067ff6bcac 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index 5982b8a08092..e237e32a26ba 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -274,18 +274,20 @@ unsigned GasMeter::runGas(Instruction _instruction, langutil::EVMVersion _evmVer switch (instructionInfo(_instruction, _evmVersion).gasPriceTier) { - case Tier::Zero: return GasCosts::tier0Gas; - case Tier::Base: return GasCosts::tier1Gas; - case Tier::VeryLow: return GasCosts::tier2Gas; - case Tier::Low: return GasCosts::tier3Gas; - case Tier::Mid: return GasCosts::tier4Gas; - case Tier::High: return GasCosts::tier5Gas; - case Tier::Ext: return GasCosts::tier6Gas; - case Tier::WarmAccess: return GasCosts::warmStorageReadCost; - default: break; + case Tier::Zero: return GasCosts::tier0Gas; + case Tier::Base: return GasCosts::tier1Gas; + case Tier::VeryLow: return GasCosts::tier2Gas; + case Tier::Low: return GasCosts::tier3Gas; + case Tier::Mid: return GasCosts::tier4Gas; + case Tier::High: return GasCosts::tier5Gas; + case Tier::Ext: return GasCosts::tier6Gas; + case Tier::WarmAccess: return GasCosts::warmStorageReadCost; + + case Tier::Special: + case Tier::Invalid: + assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction, _evmVersion).name); } - assertThrow(false, OptimizerException, "Invalid gas tier for instruction " + instructionInfo(_instruction, _evmVersion).name); - return 0; + util::unreachable(); } u256 GasMeter::dataGas(bytes const& _data, bool _inCreation, langutil::EVMVersion _evmVersion) diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index a21f33e1838f..4e0684b6b4eb 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -211,7 +211,7 @@ static std::map const c_instructionInfo = { Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, false, Tier::Low } }, { Instruction::KECCAK256, { "KECCAK256", 0, 2, 1, true, Tier::Special } }, { Instruction::ADDRESS, { "ADDRESS", 0, 0, 1, false, Tier::Base } }, - { Instruction::BALANCE, { "BALANCE", 0, 1, 1, false, Tier::Balance } }, + { Instruction::BALANCE, { "BALANCE", 0, 1, 1, false, Tier::Special } }, { Instruction::ORIGIN, { "ORIGIN", 0, 0, 1, false, Tier::Base } }, { Instruction::CALLER, { "CALLER", 0, 0, 1, false, Tier::Base } }, { Instruction::CALLVALUE, { "CALLVALUE", 0, 0, 1, false, Tier::Base } }, @@ -221,12 +221,12 @@ static std::map const c_instructionInfo = { Instruction::CODESIZE, { "CODESIZE", 0, 0, 1, false, Tier::Base } }, { Instruction::CODECOPY, { "CODECOPY", 0, 3, 0, true, Tier::VeryLow } }, { Instruction::GASPRICE, { "GASPRICE", 0, 0, 1, false, Tier::Base } }, - { Instruction::EXTCODESIZE, { "EXTCODESIZE", 0, 1, 1, false, Tier::ExtCode } }, - { Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, true, Tier::ExtCode } }, - { Instruction::RETURNDATASIZE, {"RETURNDATASIZE", 0, 0, 1, false, Tier::Base } }, - { Instruction::RETURNDATACOPY, {"RETURNDATACOPY", 0, 3, 0, true, Tier::VeryLow } }, + { Instruction::EXTCODESIZE, { "EXTCODESIZE", 0, 1, 1, false, Tier::Special } }, + { Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, true, Tier::Special } }, + { Instruction::RETURNDATASIZE, {"RETURNDATASIZE", 0, 0, 1, false, Tier::Base } }, + { Instruction::RETURNDATACOPY, {"RETURNDATACOPY", 0, 3, 0, true, Tier::VeryLow } }, { Instruction::MCOPY, { "MCOPY", 0, 3, 0, true, Tier::VeryLow } }, - { Instruction::EXTCODEHASH, { "EXTCODEHASH", 0, 1, 1, false, Tier::Balance } }, + { Instruction::EXTCODEHASH, { "EXTCODEHASH", 0, 1, 1, false, Tier::Special } }, { Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, Tier::Ext } }, { Instruction::BLOBHASH, { "BLOBHASH", 0, 1, 1, false, Tier::VeryLow } }, { Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::Base } }, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index 1733863aa5a2..d29a6feca73c 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -297,8 +297,6 @@ enum class Tier High, // 10, Slow Ext, // 20, Ext WarmAccess, // 100, Warm Access - ExtCode, // 700, Extcode - Balance, // 400, Balance Special, // multiparam or otherwise special Invalid // Invalid. }; diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 7181bfd4cc7e..4c08efb51a00 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -43,9 +43,7 @@ #include -#include #include -#include #include #include From 686f5cd455c837d6a54f819c8cff66733e2ecc8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 9 Jan 2024 15:54:03 +0100 Subject: [PATCH 106/189] Document the Tier enum and constants that come from Execution Specs --- libevmasm/GasMeter.h | 56 +++++++++++++++++++++-------------------- libevmasm/Instruction.h | 25 ++++++++++-------- 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/libevmasm/GasMeter.h b/libevmasm/GasMeter.h index ec1946644787..e4bb19925796 100644 --- a/libevmasm/GasMeter.h +++ b/libevmasm/GasMeter.h @@ -43,32 +43,34 @@ class KnownState; namespace GasCosts { + /// NOTE: The GAS_... constants referenced by comments are defined for each EVM version in the Execution Specs: + /// https://ethereum.github.io/execution-specs/autoapi/ethereum//vm/gas/index.html + static unsigned const stackLimit = 1024; - static unsigned const tier0Gas = 0; - static unsigned const tier1Gas = 2; - static unsigned const tier2Gas = 3; - static unsigned const tier3Gas = 5; - static unsigned const tier4Gas = 8; - static unsigned const tier5Gas = 10; - static unsigned const tier6Gas = 20; - static unsigned const tier7Gas = 0; - static unsigned const expGas = 10; + static unsigned const tier0Gas = 0; // GAS_ZERO (in Execution Specs) + static unsigned const tier1Gas = 2; // GAS_BASE + static unsigned const tier2Gas = 3; // GAS_VERY_LOW + static unsigned const tier3Gas = 5; // GAS_LOW / GAS_FAST_STEP + static unsigned const tier4Gas = 8; // GAS_MID + static unsigned const tier5Gas = 10; // GAS_HIGH + static unsigned const tier6Gas = 20; // GAS_BLOCK_HASH + static unsigned const expGas = 10; // GAS_EXPONENTIATION inline unsigned expByteGas(langutil::EVMVersion _evmVersion) { - return _evmVersion >= langutil::EVMVersion::spuriousDragon() ? 50 : 10; + return _evmVersion >= langutil::EVMVersion::spuriousDragon() ? 50 : 10; // GAS_EXPONENTIATION_PER_BYTE } - static unsigned const keccak256Gas = 30; - static unsigned const keccak256WordGas = 6; + static unsigned const keccak256Gas = 30; // GAS_KECCAK256 + static unsigned const keccak256WordGas = 6; // GAS_KECCAK256_WORD /// Corresponds to ACCESS_LIST_ADDRESS_COST from EIP-2930 static unsigned const accessListAddressCost = 2400; /// Corresponds to ACCESS_LIST_STORAGE_COST from EIP-2930 static unsigned const accessListStorageKeyCost = 1900; /// Corresponds to COLD_SLOAD_COST from EIP-2929 - static unsigned const coldSloadCost = 2100; + static unsigned const coldSloadCost = 2100; // GAS_COLD_SLOAD /// Corresponds to COLD_ACCOUNT_ACCESS_COST from EIP-2929 - static unsigned const coldAccountAccessCost = 2600; + static unsigned const coldAccountAccessCost = 2600; // GAS_COLD_ACCOUNT_ACCESS /// Corresponds to WARM_STORAGE_READ_COST from EIP-2929 - static unsigned const warmStorageReadCost = 100; + static unsigned const warmStorageReadCost = 100; // GAS_WARM_ACCESS inline unsigned sloadGas(langutil::EVMVersion _evmVersion) { if (_evmVersion >= langutil::EVMVersion::berlin()) @@ -81,7 +83,7 @@ namespace GasCosts return 50; } /// Corresponds to SSTORE_SET_GAS - static unsigned const sstoreSetGas = 20000; + static unsigned const sstoreSetGas = 20000; // GAS_STORAGE_SET /// Corresponds to SSTORE_RESET_GAS from EIP-2929 static unsigned const sstoreResetGas = 5000 - coldSloadCost; /// Corresponds to SSTORE_CLEARS_SCHEDULE from EIP-2200 @@ -130,11 +132,11 @@ namespace GasCosts else return 20; } - static unsigned const jumpdestGas = 1; - static unsigned const logGas = 375; - static unsigned const logDataGas = 8; - static unsigned const logTopicGas = 375; - static unsigned const createGas = 32000; + static unsigned const jumpdestGas = 1; // GAS_JUMPDEST + static unsigned const logGas = 375; // GAS_LOG + static unsigned const logDataGas = 8; // GAS_LOG_DATA + static unsigned const logTopicGas = 375; // GAS_LOG_TOPIC + static unsigned const createGas = 32000; // GAS_CREATE inline unsigned callGas(langutil::EVMVersion _evmVersion) { if (_evmVersion >= langutil::EVMVersion::berlin()) @@ -144,10 +146,10 @@ namespace GasCosts else return 40; } - static unsigned const callStipend = 2300; - static unsigned const callValueTransferGas = 9000; - static unsigned const callNewAccountGas = 25000; - inline unsigned selfdestructGas(langutil::EVMVersion _evmVersion) + static unsigned const callStipend = 2300; // GAS_CALL_STIPEND + static unsigned const callValueTransferGas = 9000; // GAS_CALL_VALUE + static unsigned const callNewAccountGas = 25000; // GAS_NEW_ACCOUNT / GAS_SELF_DESTRUCT_NEW_ACCOUNT + inline unsigned selfdestructGas(langutil::EVMVersion _evmVersion) // GAS_SELF_DESTRUCT { if (_evmVersion >= langutil::EVMVersion::berlin()) return coldAccountAccessCost; @@ -164,9 +166,9 @@ namespace GasCosts else return 24000; } - static unsigned const memoryGas = 3; + static unsigned const memoryGas = 3; // GAS_MEMORY / GAS_COPY / GAS_RETURN_DATA_COPY static unsigned const quadCoeffDiv = 512; - static unsigned const createDataGas = 200; + static unsigned const createDataGas = 200; // GAS_CODE_DEPOSIT static unsigned const txGas = 21000; static unsigned const txCreateGas = 53000; static unsigned const txDataZeroGas = 4; diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index d29a6feca73c..7d551f71d72f 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -287,18 +287,23 @@ inline Instruction logInstruction(unsigned _number) return Instruction(unsigned(Instruction::LOG0) + _number); } +/// Gas price tiers representing static cost of an instruction. +/// Opcodes whose cost is dynamic or depends on EVM version should use the `Special` tier and need +/// dedicated logic in GasMeter (especially in estimateMax()). +/// The tiers loosely follow opcode groups originally defined in the Yellow Paper. enum class Tier { - Zero = 0, // 0, Zero - Base, // 2, Quick - VeryLow, // 3, Fastest - Low, // 5, Fast - Mid, // 8, Mid - High, // 10, Slow - Ext, // 20, Ext - WarmAccess, // 100, Warm Access - Special, // multiparam or otherwise special - Invalid // Invalid. + // NOTE: Tiers should be ordered by cost, since we sometimes perform comparisons between them. + Zero = 0, // 0, Zero + Base, // 2, Quick + VeryLow, // 3, Fastest + Low, // 5, Fast + Mid, // 8, Mid + High, // 10, Slow + Ext, // 20, Ext + WarmAccess, // 100, Warm Access + Special, // multiparam or otherwise special + Invalid, // Invalid. }; /// Information structure for a particular instruction. From a973b4c6bffbd25497fb6b06fa1935ce4b63cbcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 9 Jan 2024 17:13:42 +0100 Subject: [PATCH 107/189] Rename Ext gas cost tier to BlockHash --- libevmasm/GasMeter.cpp | 2 +- libevmasm/Instruction.cpp | 2 +- libevmasm/Instruction.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index e237e32a26ba..b3d160585e37 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -280,7 +280,7 @@ unsigned GasMeter::runGas(Instruction _instruction, langutil::EVMVersion _evmVer case Tier::Low: return GasCosts::tier3Gas; case Tier::Mid: return GasCosts::tier4Gas; case Tier::High: return GasCosts::tier5Gas; - case Tier::Ext: return GasCosts::tier6Gas; + case Tier::BlockHash: return GasCosts::tier6Gas; case Tier::WarmAccess: return GasCosts::warmStorageReadCost; case Tier::Special: diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index 4e0684b6b4eb..ca0dff185ad4 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -227,7 +227,7 @@ static std::map const c_instructionInfo = { Instruction::RETURNDATACOPY, {"RETURNDATACOPY", 0, 3, 0, true, Tier::VeryLow } }, { Instruction::MCOPY, { "MCOPY", 0, 3, 0, true, Tier::VeryLow } }, { Instruction::EXTCODEHASH, { "EXTCODEHASH", 0, 1, 1, false, Tier::Special } }, - { Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, Tier::Ext } }, + { Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, Tier::BlockHash } }, { Instruction::BLOBHASH, { "BLOBHASH", 0, 1, 1, false, Tier::VeryLow } }, { Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::Base } }, { Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, false, Tier::Base } }, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index 7d551f71d72f..289469b5ef7b 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -300,7 +300,7 @@ enum class Tier Low, // 5, Fast Mid, // 8, Mid High, // 10, Slow - Ext, // 20, Ext + BlockHash, // 20 WarmAccess, // 100, Warm Access Special, // multiparam or otherwise special Invalid, // Invalid. From 88ee7d8bb57075c94df3d70fce115357f1290b0c Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Mon, 19 Feb 2024 16:28:08 +0100 Subject: [PATCH 108/189] [ci] Remove code signature from universal binary. --- .circleci/config.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index b29c46bf8b9e..672b77c96d4b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -242,6 +242,13 @@ commands: # -------------------------------------------------------------------------- # Artifact Commands + remove_signature_from_universal_binary: + description: Remove signature from universal binary + steps: + - run: + name: Remove signature from universal binary + command: codesign --remove-signature build/solc/solc + store_artifacts_solc: description: Store compiled solc executable as artifact steps: @@ -1156,6 +1163,7 @@ jobs: - checkout - install_dependencies_osx - run_build + - remove_signature_from_universal_binary - store_artifacts_solc - store_artifacts_yul_phaser - persist_executables_to_workspace_osx From ac54fe1972f25227f9932c8b224ef119360b0e2d Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 11 Jan 2024 13:58:23 +0100 Subject: [PATCH 109/189] Purge using namespace std from test --- scripts/check_style.sh | 1 + test/Common.cpp | 34 +++++++------- test/CommonSyntaxTest.cpp | 69 ++++++++++++++-------------- test/EVMHost.cpp | 55 +++++++++++------------ test/ExecutionFramework.cpp | 45 +++++++++---------- test/FilesystemUtils.cpp | 17 ++++--- test/Metadata.cpp | 24 +++++----- test/TestCase.cpp | 35 +++++++-------- test/TestCaseReader.cpp | 89 ++++++++++++++++++------------------- test/soltest.cpp | 21 +++++---- 10 files changed, 192 insertions(+), 198 deletions(-) diff --git a/scripts/check_style.sh b/scripts/check_style.sh index fe4d19734fc1..3c9acf86ca74 100755 --- a/scripts/check_style.sh +++ b/scripts/check_style.sh @@ -39,6 +39,7 @@ NAMESPACE_STD_FREE_FILES=( libyul/backends/evm/* libyul/optimiser/* solc/* + test/* test/contracts/* test/libevmasm/* test/liblangutil/* diff --git a/test/Common.cpp b/test/Common.cpp index bb4d2db7ebce..ae725774c48a 100644 --- a/test/Common.cpp +++ b/test/Common.cpp @@ -32,8 +32,6 @@ namespace fs = boost::filesystem; namespace po = boost::program_options; -using namespace std; - namespace solidity::test { @@ -147,15 +145,15 @@ void CommonOptions::validate() const ); if (!enforceGasTest) - cout << endl << "WARNING :: Gas cost expectations are not being enforced" << endl << endl; + std::cout << std::endl << "WARNING :: Gas cost expectations are not being enforced" << std::endl << std::endl; else if (evmVersion() != langutil::EVMVersion{} || useABIEncoderV1) { - cout << endl << "WARNING :: Enforcing gas cost expectations with non-standard settings:" << endl; + std::cout << std::endl << "WARNING :: Enforcing gas cost expectations with non-standard settings:" << std::endl; if (evmVersion() != langutil::EVMVersion{}) - cout << "- EVM version: " << evmVersion().name() << " (default: " << langutil::EVMVersion{}.name() << ")" << endl; + std::cout << "- EVM version: " << evmVersion().name() << " (default: " << langutil::EVMVersion{}.name() << ")" << std::endl; if (useABIEncoderV1) - cout << "- ABI coder: v1 (default: v2)" << endl; - cout << endl << "DO NOT COMMIT THE UPDATED EXPECTATIONS." << endl << endl; + std::cout << "- ABI coder: v1 (default: v2)" << std::endl; + std::cout << std::endl << "DO NOT COMMIT THE UPDATED EXPECTATIONS." << std::endl << std::endl; } } @@ -176,7 +174,7 @@ bool CommonOptions::parse(int argc, char const* const* argv) // Request as uint64_t, since uint8_t will be parsed as character by boost. uint64_t eofVersion = arguments["eof-version"].as(); if (eofVersion != 1) - BOOST_THROW_EXCEPTION(std::runtime_error("Invalid EOF version: " + to_string(eofVersion))); + BOOST_THROW_EXCEPTION(std::runtime_error("Invalid EOF version: " + std::to_string(eofVersion))); m_eofVersion = 1; } @@ -213,18 +211,18 @@ bool CommonOptions::parse(int argc, char const* const* argv) return true; } -string CommonOptions::toString(vector const& _selectedOptions) const +std::string CommonOptions::toString(std::vector const& _selectedOptions) const { if (_selectedOptions.empty()) return ""; - auto boolToString = [](bool _value) -> string { return _value ? "true" : "false"; }; + auto boolToString = [](bool _value) -> std::string { return _value ? "true" : "false"; }; // Using std::map to avoid if-else/switch-case block - map optionValueMap = { + std::map optionValueMap = { {"evmVersion", evmVersion().name()}, {"optimize", boolToString(optimize)}, {"useABIEncoderV1", boolToString(useABIEncoderV1)}, - {"batch", to_string(selectedBatch + 1) + "/" + to_string(batches)}, + {"batch", std::to_string(selectedBatch + 1) + "/" + std::to_string(batches)}, {"enforceGasTest", boolToString(enforceGasTest)}, {"enforceGasTestMinValue", enforceGasTestMinValue.str()}, {"disableSemanticTests", boolToString(disableSemanticTests)}, @@ -233,18 +231,18 @@ string CommonOptions::toString(vector const& _selectedOptions) const {"showMetadata", boolToString(showMetadata)} }; - soltestAssert(ranges::all_of(_selectedOptions, [&optionValueMap](string const& _option) { return optionValueMap.count(_option) > 0; })); + soltestAssert(ranges::all_of(_selectedOptions, [&optionValueMap](std::string const& _option) { return optionValueMap.count(_option) > 0; })); - vector optionsWithValues = _selectedOptions | - ranges::views::transform([&optionValueMap](string const& _option) { return _option + "=" + optionValueMap.at(_option); }) | - ranges::to(); + std::vector optionsWithValues = _selectedOptions | + ranges::views::transform([&optionValueMap](std::string const& _option) { return _option + "=" + optionValueMap.at(_option); }) | + ranges::to(); return solidity::util::joinHumanReadable(optionsWithValues); } -void CommonOptions::printSelectedOptions(ostream& _stream, string const& _linePrefix, vector const& _selectedOptions) const +void CommonOptions::printSelectedOptions(std::ostream& _stream, std::string const& _linePrefix, std::vector const& _selectedOptions) const { - _stream << _linePrefix << "Run Settings: " << toString(_selectedOptions) << endl; + _stream << _linePrefix << "Run Settings: " << toString(_selectedOptions) << std::endl; } langutil::EVMVersion CommonOptions::evmVersion() const diff --git a/test/CommonSyntaxTest.cpp b/test/CommonSyntaxTest.cpp index 0dedd1fb043b..3cb908118ea2 100644 --- a/test/CommonSyntaxTest.cpp +++ b/test/CommonSyntaxTest.cpp @@ -34,7 +34,6 @@ #include #include -using namespace std; using namespace solidity; using namespace solidity::util; using namespace solidity::util::formatting; @@ -48,10 +47,10 @@ namespace fs = boost::filesystem; namespace { -int parseUnsignedInteger(string::iterator& _it, string::iterator _end) +int parseUnsignedInteger(std::string::iterator& _it, std::string::iterator _end) { if (_it == _end || !util::isDigit(*_it)) - BOOST_THROW_EXCEPTION(runtime_error("Invalid test expectation. Source location expected.")); + BOOST_THROW_EXCEPTION(std::runtime_error("Invalid test expectation. Source location expected.")); int result = 0; while (_it != _end && util::isDigit(*_it)) { @@ -64,7 +63,7 @@ int parseUnsignedInteger(string::iterator& _it, string::iterator _end) } -CommonSyntaxTest::CommonSyntaxTest(string const& _filename, langutil::EVMVersion _evmVersion): +CommonSyntaxTest::CommonSyntaxTest(std::string const& _filename, langutil::EVMVersion _evmVersion): EVMVersionRestrictedTestCase(_filename), m_sources(m_reader.sources()), m_expectations(parseExpectations(m_reader.stream())), @@ -72,7 +71,7 @@ CommonSyntaxTest::CommonSyntaxTest(string const& _filename, langutil::EVMVersion { } -TestCase::TestResult CommonSyntaxTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) +TestCase::TestResult CommonSyntaxTest::run(std::ostream& _stream, std::string const& _linePrefix, bool _formatted) { parseCustomExpectations(m_reader.stream()); parseAndAnalyze(); @@ -80,7 +79,7 @@ TestCase::TestResult CommonSyntaxTest::run(ostream& _stream, string const& _line return conclude(_stream, _linePrefix, _formatted); } -TestCase::TestResult CommonSyntaxTest::conclude(ostream& _stream, string const& _linePrefix, bool _formatted) +TestCase::TestResult CommonSyntaxTest::conclude(std::ostream& _stream, std::string const& _linePrefix, bool _formatted) { if (expectationsMatch()) return TestResult::Success; @@ -89,16 +88,16 @@ TestCase::TestResult CommonSyntaxTest::conclude(ostream& _stream, string const& return TestResult::Failure; } -void CommonSyntaxTest::printExpectationAndError(ostream& _stream, string const& _linePrefix, bool _formatted) +void CommonSyntaxTest::printExpectationAndError(std::ostream& _stream, std::string const& _linePrefix, bool _formatted) { - string nextIndentLevel = _linePrefix + " "; - util::AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; + std::string nextIndentLevel = _linePrefix + " "; + util::AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << std::endl; printExpectedResult(_stream, nextIndentLevel, _formatted); - util::AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; + util::AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << std::endl; printObtainedResult(_stream, nextIndentLevel, _formatted); } -void CommonSyntaxTest::printSource(ostream& _stream, string const& _linePrefix, bool _formatted) const +void CommonSyntaxTest::printSource(std::ostream& _stream, std::string const& _linePrefix, bool _formatted) const { if (m_sources.sources.empty()) return; @@ -113,8 +112,8 @@ void CommonSyntaxTest::printSource(ostream& _stream, string const& _linePrefix, continue; if (outputSourceNames) - _stream << _linePrefix << util::formatting::CYAN << "==== Source: " << name << " ====" << util::formatting::RESET << endl; - vector sourceFormatting(source.length(), util::formatting::RESET); + _stream << _linePrefix << util::formatting::CYAN << "==== Source: " << name << " ====" << util::formatting::RESET << std::endl; + std::vector sourceFormatting(source.length(), util::formatting::RESET); for (auto const& error: m_errorList) if (error.sourceName == name && error.locationStart >= 0 && error.locationEnd >= 0) { @@ -143,7 +142,7 @@ void CommonSyntaxTest::printSource(ostream& _stream, string const& _linePrefix, _stream << source[i]; else { - _stream << util::formatting::RESET << endl; + _stream << util::formatting::RESET << std::endl; if (i + 1 < source.length()) _stream << _linePrefix << sourceFormatting[i]; } @@ -158,9 +157,9 @@ void CommonSyntaxTest::printSource(ostream& _stream, string const& _linePrefix, } } -void CommonSyntaxTest::parseCustomExpectations(istream& _stream) +void CommonSyntaxTest::parseCustomExpectations(std::istream& _stream) { - string remainingExpectations = boost::trim_copy(readUntilEnd(_stream)); + std::string remainingExpectations = boost::trim_copy(readUntilEnd(_stream)); soltestAssert( remainingExpectations.empty(), "Found custom expectations not supported by the test case:\n" + remainingExpectations @@ -172,27 +171,27 @@ bool CommonSyntaxTest::expectationsMatch() return m_expectations == m_errorList; } -void CommonSyntaxTest::printExpectedResult(ostream& _stream, string const& _linePrefix, bool _formatted) const +void CommonSyntaxTest::printExpectedResult(std::ostream& _stream, std::string const& _linePrefix, bool _formatted) const { printErrorList(_stream, m_expectations, _linePrefix, _formatted); } -void CommonSyntaxTest::printObtainedResult(ostream& _stream, string const& _linePrefix, bool _formatted) const +void CommonSyntaxTest::printObtainedResult(std::ostream& _stream, std::string const& _linePrefix, bool _formatted) const { printErrorList(_stream, m_errorList, _linePrefix, _formatted); } void CommonSyntaxTest::printErrorList( - ostream& _stream, - vector const& _errorList, - string const& _linePrefix, + std::ostream& _stream, + std::vector const& _errorList, + std::string const& _linePrefix, bool _formatted ) { if (_errorList.empty()) { if (_formatted) - util::AnsiColorized(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl; + util::AnsiColorized(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << std::endl; } else for (auto const& error: _errorList) @@ -220,11 +219,11 @@ void CommonSyntaxTest::printErrorList( _stream << error.locationEnd; _stream << "): "; } - _stream << error.message << endl; + _stream << error.message << std::endl; } } -string CommonSyntaxTest::errorMessage(util::Exception const& _e) +std::string CommonSyntaxTest::errorMessage(util::Exception const& _e) { if (_e.comment() && !_e.comment()->empty()) return boost::replace_all_copy(*_e.comment(), "\n", "\\n"); @@ -232,13 +231,13 @@ string CommonSyntaxTest::errorMessage(util::Exception const& _e) return "NONE"; } -vector CommonSyntaxTest::parseExpectations(istream& _stream) +std::vector CommonSyntaxTest::parseExpectations(std::istream& _stream) { - static string const customExpectationsDelimiter("// ----"); + static std::string const customExpectationsDelimiter("// ----"); - vector expectations; - string line; - while (getline(_stream, line)) + std::vector expectations; + std::string line; + while (std::getline(_stream, line)) { auto it = line.begin(); @@ -254,17 +253,17 @@ vector CommonSyntaxTest::parseExpectations(istream& _stream) if (it == line.end()) continue; auto typeBegin = it; - while (it != line.end() && isalpha(*it, locale::classic())) + while (it != line.end() && isalpha(*it, std::locale::classic())) ++it; - string errorTypeStr(typeBegin, it); - optional errorType = Error::parseErrorType(errorTypeStr); + std::string errorTypeStr(typeBegin, it); + std::optional errorType = Error::parseErrorType(errorTypeStr); if (!errorType.has_value()) - BOOST_THROW_EXCEPTION(runtime_error("Invalid error type: " + errorTypeStr)); + BOOST_THROW_EXCEPTION(std::runtime_error("Invalid error type: " + errorTypeStr)); skipWhitespace(it, line.end()); - optional errorId; + std::optional errorId; if (it != line.end() && util::isDigit(*it)) errorId = ErrorId{static_cast(parseUnsignedInteger(it, line.end()))}; @@ -295,7 +294,7 @@ vector CommonSyntaxTest::parseExpectations(istream& _stream) skipWhitespace(it, line.end()); - string errorMessage(it, line.end()); + std::string errorMessage(it, line.end()); expectations.emplace_back(SyntaxTestError{ errorType.value(), std::move(errorId), diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 7e05ceec3ecb..9af4d7099dd2 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -31,16 +31,15 @@ #include #include -using namespace std; using namespace solidity; using namespace solidity::util; using namespace solidity::test; using namespace evmc::literals; -evmc::VM& EVMHost::getVM(string const& _path) +evmc::VM& EVMHost::getVM(std::string const& _path) { static evmc::VM NullVM{nullptr}; - static map> vms; + static std::map> vms; if (vms.count(_path) == 0) { evmc_loader_error_code errorCode = {}; @@ -48,16 +47,16 @@ evmc::VM& EVMHost::getVM(string const& _path) if (vm && errorCode == EVMC_LOADER_SUCCESS) { if (vm.get_capabilities() & (EVMC_CAPABILITY_EVM1)) - vms[_path] = make_unique(evmc::VM(std::move(vm))); + vms[_path] = std::make_unique(evmc::VM(std::move(vm))); else - cerr << "VM loaded does not support EVM1" << endl; + std::cerr << "VM loaded does not support EVM1" << std::endl; } else { - cerr << "Error loading VM from " << _path; + std::cerr << "Error loading VM from " << _path; if (char const* errorMsg = evmc_last_error_msg()) - cerr << ":" << endl << errorMsg; - cerr << endl; + std::cerr << ":" << std::endl << errorMsg; + std::cerr << std::endl; } } @@ -67,7 +66,7 @@ evmc::VM& EVMHost::getVM(string const& _path) return NullVM; } -bool EVMHost::checkVmPaths(vector const& _vmPaths) +bool EVMHost::checkVmPaths(std::vector const& _vmPaths) { bool evmVmFound = false; for (auto const& path: _vmPaths) @@ -79,7 +78,7 @@ bool EVMHost::checkVmPaths(vector const& _vmPaths) if (vm.has_capability(EVMC_CAPABILITY_EVM1)) { if (evmVmFound) - BOOST_THROW_EXCEPTION(runtime_error("Multiple evm1 evmc vms defined. Please only define one evm1 evmc vm.")); + BOOST_THROW_EXCEPTION(std::runtime_error("Multiple evm1 evmc vms defined. Please only define one evm1 evmc vm.")); evmVmFound = true; } } @@ -92,7 +91,7 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm): { if (!m_vm) { - cerr << "Unable to find evmone library" << endl; + std::cerr << "Unable to find evmone library" << std::endl; assertThrow(false, Exception, ""); } @@ -319,7 +318,7 @@ evmc::Result EVMHost::call(evmc_message const& _message) noexcept h160 createAddress(keccak256( bytes{static_cast(0xc0 + 21 + encodedNonce.size())} + bytes{0x94} + - bytes(begin(message.sender.bytes), end(message.sender.bytes)) + + bytes(std::begin(message.sender.bytes), std::end(message.sender.bytes)) + encodedNonce ), h160::AlignRight); @@ -332,8 +331,8 @@ evmc::Result EVMHost::call(evmc_message const& _message) noexcept { h160 createAddress(keccak256( bytes{0xff} + - bytes(begin(message.sender.bytes), end(message.sender.bytes)) + - bytes(begin(message.create2_salt.bytes), end(message.create2_salt.bytes)) + + bytes(std::begin(message.sender.bytes), std::end(message.sender.bytes)) + + bytes(std::begin(message.create2_salt.bytes), std::end(message.create2_salt.bytes)) + keccak256(bytes(message.input_data, message.input_data + message.input_size)).asBytes() ), h160::AlignRight); @@ -421,7 +420,7 @@ evmc::bytes32 EVMHost::get_block_hash(int64_t _number) const noexcept h160 EVMHost::convertFromEVMC(evmc::address const& _addr) { - return h160(bytes(begin(_addr.bytes), end(_addr.bytes))); + return h160(bytes(std::begin(_addr.bytes), std::end(_addr.bytes))); } evmc::address EVMHost::convertToEVMC(h160 const& _addr) @@ -434,7 +433,7 @@ evmc::address EVMHost::convertToEVMC(h160 const& _addr) h256 EVMHost::convertFromEVMC(evmc::bytes32 const& _data) { - return h256(bytes(begin(_data.bytes), end(_data.bytes))); + return h256(bytes(std::begin(_data.bytes), std::end(_data.bytes))); } evmc::bytes32 EVMHost::convertToEVMC(h256 const& _data) @@ -452,7 +451,7 @@ evmc::Result EVMHost::precompileECRecover(evmc_message const& _message) noexcept // Fixed cost of 3000 gas. constexpr int64_t gas_cost = 3000; - static map const inputOutput{ + static std::map const inputOutput{ { fromHex( "18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c" @@ -509,7 +508,7 @@ evmc::Result EVMHost::precompileRipeMD160(evmc_message const& _message) noexcept return 600 + 120 * ((size + 31) / 32); }; - static map const inputOutput{ + static std::map const inputOutput{ { bytes{}, { @@ -628,7 +627,7 @@ evmc::Result EVMHost::precompileALTBN128G1Add(evmc_message const& _message) noex // Fixed 500 or 150 gas. int64_t gas_cost = (Revision < EVMC_ISTANBUL) ? 500 : 150; - static map const inputOutput{ + static std::map const inputOutput{ { fromHex( "0000000000000000000000000000000000000000000000000000000000000000" @@ -896,7 +895,7 @@ evmc::Result EVMHost::precompileALTBN128G1Mul(evmc_message const& _message) noex // Fixed 40000 or 6000 gas. int64_t gas_cost = (Revision < EVMC_ISTANBUL) ? 40000 : 6000; - static map const inputOutput{ + static std::map const inputOutput{ { fromHex("0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000"), { @@ -990,7 +989,7 @@ evmc::Result EVMHost::precompileALTBN128PairingProduct(evmc_message const& _mess }; // NOTE this is a partial implementation for some inputs. - static map const inputOutput{ + static std::map const inputOutput{ { fromHex( "17c139df0efee0f766bc0204762b774362e4ded88953a39ce849a8a7fa163fa9" @@ -1155,7 +1154,7 @@ evmc::Result EVMHost::precompileBlake2f(evmc_message const&) noexcept evmc::Result EVMHost::precompileGeneric( evmc_message const& _message, - map const& _inOut) noexcept + std::map const& _inOut) noexcept { bytes input(_message.input_data, _message.input_data + _message.input_size); if (_inOut.count(input)) @@ -1202,7 +1201,7 @@ StorageMap const& EVMHost::get_address_storage(evmc::address const& _addr) return accounts[_addr].storage; } -string EVMHostPrinter::state() +std::string EVMHostPrinter::state() { // Print state and execution trace. if (m_host.account_exists(m_account)) @@ -1225,14 +1224,14 @@ void EVMHostPrinter::storage() << m_host.convertFromEVMC(slot) << ": " << m_host.convertFromEVMC(value.current) - << endl; + << std::endl; } void EVMHostPrinter::balance() { m_stateStream << "BALANCE " << m_host.convertFromEVMC(m_host.get_balance(m_account)) - << endl; + << std::endl; } void EVMHostPrinter::selfdestructRecords() @@ -1242,12 +1241,12 @@ void EVMHostPrinter::selfdestructRecords() m_stateStream << "SELFDESTRUCT" << " BENEFICIARY " << m_host.convertFromEVMC(beneficiary) - << endl; + << std::endl; } void EVMHostPrinter::callRecords() { - static auto constexpr callKind = [](evmc_call_kind _kind) -> string + static auto constexpr callKind = [](evmc_call_kind _kind) -> std::string { switch (_kind) { @@ -1270,5 +1269,5 @@ void EVMHostPrinter::callRecords() m_stateStream << callKind(record.kind) << " VALUE " << m_host.convertFromEVMC(record.value) - << endl; + << std::endl; } diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index a18a73de06b8..9fc36440b336 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -41,7 +41,6 @@ #include #include -using namespace std; using namespace solidity; using namespace solidity::util; using namespace solidity::test; @@ -52,7 +51,7 @@ ExecutionFramework::ExecutionFramework(): { } -ExecutionFramework::ExecutionFramework(langutil::EVMVersion _evmVersion, vector const& _vmPaths): +ExecutionFramework::ExecutionFramework(langutil::EVMVersion _evmVersion, std::vector const& _vmPaths): m_evmVersion(_evmVersion), m_optimiserSettings(solidity::frontend::OptimiserSettings::minimal()), m_showMessages(solidity::test::CommonOptions::get().showMessages), @@ -71,7 +70,7 @@ void ExecutionFramework::selectVM(evmc_capabilities _cap) evmc::VM& vm = EVMHost::getVM(path.string()); if (vm.has_capability(_cap)) { - m_evmcHost = make_unique(m_evmVersion, vm); + m_evmcHost = std::make_unique(m_evmVersion, vm); break; } } @@ -87,7 +86,7 @@ void ExecutionFramework::reset() EVMHost::convertToEVMC(u256(1) << 100); } -std::pair ExecutionFramework::compareAndCreateMessage( +std::pair ExecutionFramework::compareAndCreateMessage( bytes const& _result, bytes const& _expectation ) @@ -101,8 +100,8 @@ std::pair ExecutionFramework::compareAndCreateMessage( auto expectedHex = boost::replace_all_copy(util::toHex(_expectation), "0", "."); for (size_t i = 0; i < std::max(resultHex.size(), expectedHex.size()); i += 0x40) { - std::string result{i >= resultHex.size() ? string{} : resultHex.substr(i, 0x40)}; - std::string expected{i > expectedHex.size() ? string{} : expectedHex.substr(i, 0x40)}; + std::string result{i >= resultHex.size() ? std::string{} : resultHex.substr(i, 0x40)}; + std::string expected{i > expectedHex.size() ? std::string{} : expectedHex.substr(i, 0x40)}; message += (result == expected ? " " : " X ") + result + @@ -137,7 +136,7 @@ u256 ExecutionFramework::gasPrice() const u256 ExecutionFramework::blockHash(u256 const& _number) const { return u256{EVMHost::convertFromEVMC( - m_evmcHost->get_block_hash(static_cast(_number & numeric_limits::max())) + m_evmcHost->get_block_hash(static_cast(_number & std::numeric_limits::max())) )}; } @@ -153,12 +152,12 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 if (m_showMessages) { if (_isCreation) - cout << "CREATE " << m_sender.hex() << ":" << endl; + std::cout << "CREATE " << m_sender.hex() << ":" << std::endl; else - cout << "CALL " << m_sender.hex() << " -> " << m_contractAddress.hex() << ":" << endl; + std::cout << "CALL " << m_sender.hex() << " -> " << m_contractAddress.hex() << ":" << std::endl; if (_value > 0) - cout << " value: " << _value << endl; - cout << " in: " << util::toHex(_data) << endl; + std::cout << " value: " << _value << std::endl; + std::cout << " in: " << util::toHex(_data) << std::endl; } evmc_message message{}; message.input_data = _data.data(); @@ -189,19 +188,19 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 unsigned const refundRatio = (m_evmVersion >= langutil::EVMVersion::london() ? 5 : 2); auto const totalGasUsed = InitialGas - result.gas_left; - auto const gasRefund = min(u256(result.gas_refund), totalGasUsed / refundRatio); + auto const gasRefund = std::min(u256(result.gas_refund), totalGasUsed / refundRatio); m_gasUsed = totalGasUsed - gasRefund; m_transactionSuccessful = (result.status_code == EVMC_SUCCESS); if (m_showMessages) { - cout << " out: " << util::toHex(m_output) << endl; - cout << " result: " << static_cast(result.status_code) << endl; - cout << " gas used: " << m_gasUsed.str() << endl; - cout << " gas used (without refund): " << totalGasUsed.str() << endl; - cout << " gas refund (total): " << result.gas_refund << endl; - cout << " gas refund (bound): " << gasRefund.str() << endl; + std::cout << " out: " << util::toHex(m_output) << std::endl; + std::cout << " result: " << static_cast(result.status_code) << std::endl; + std::cout << " gas used: " << m_gasUsed.str() << std::endl; + std::cout << " gas used (without refund): " << totalGasUsed.str() << std::endl; + std::cout << " gas refund (total): " << result.gas_refund << std::endl; + std::cout << " gas refund (bound): " << gasRefund.str() << std::endl; } } @@ -211,9 +210,9 @@ void ExecutionFramework::sendEther(h160 const& _addr, u256 const& _amount) if (m_showMessages) { - cout << "SEND_ETHER " << m_sender.hex() << " -> " << _addr.hex() << ":" << endl; + std::cout << "SEND_ETHER " << m_sender.hex() << " -> " << _addr.hex() << ":" << std::endl; if (_amount > 0) - cout << " value: " << _amount << endl; + std::cout << " value: " << _amount << std::endl; } evmc_message message{}; message.sender = EVMHost::convertToEVMC(m_sender); @@ -294,14 +293,14 @@ bool ExecutionFramework::storageEmpty(h160 const& _addr) const return true; } -vector ExecutionFramework::recordedLogs() const +std::vector ExecutionFramework::recordedLogs() const { - vector logs; + std::vector logs; for (evmc::MockedHost::log_record const& logRecord: m_evmcHost->recorded_logs) logs.emplace_back( EVMHost::convertFromEVMC(logRecord.creator), bytes{logRecord.data.begin(), logRecord.data.end()}, - logRecord.topics | ranges::views::transform([](evmc::bytes32 _bytes) { return EVMHost::convertFromEVMC(_bytes); }) | ranges::to + logRecord.topics | ranges::views::transform([](evmc::bytes32 _bytes) { return EVMHost::convertFromEVMC(_bytes); }) | ranges::to ); return logs; } diff --git a/test/FilesystemUtils.cpp b/test/FilesystemUtils.cpp index 7506d677ecfb..2d917485d631 100644 --- a/test/FilesystemUtils.cpp +++ b/test/FilesystemUtils.cpp @@ -22,11 +22,10 @@ #include -using namespace std; using namespace solidity; using namespace solidity::test; -void solidity::test::createFilesWithParentDirs(set const& _paths, string const& _content) +void solidity::test::createFilesWithParentDirs(std::set const& _paths, std::string const& _content) { for (boost::filesystem::path const& path: _paths) { @@ -34,23 +33,23 @@ void solidity::test::createFilesWithParentDirs(set cons boost::filesystem::create_directories(path.parent_path()); // Use binary mode to avoid line ending conversion on Windows. - ofstream newFile(path.string(), std::ofstream::binary); + std::ofstream newFile(path.string(), std::ofstream::binary); newFile << _content; if (newFile.fail() || !boost::filesystem::exists(path)) - BOOST_THROW_EXCEPTION(runtime_error("Failed to create an empty file: \"" + path.string() + "\".")); + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create an empty file: \"" + path.string() + "\".")); } } -void solidity::test::createFileWithContent(boost::filesystem::path const& _path, string const& _content) +void solidity::test::createFileWithContent(boost::filesystem::path const& _path, std::string const& _content) { if (boost::filesystem::is_regular_file(_path)) - BOOST_THROW_EXCEPTION(runtime_error("File already exists: \"" + _path.string() + "\".")); \ + BOOST_THROW_EXCEPTION(std::runtime_error("File already exists: \"" + _path.string() + "\".")); \ // Use binary mode to avoid line ending conversion on Windows. - ofstream newFile(_path.string(), std::ofstream::binary); + std::ofstream newFile(_path.string(), std::ofstream::binary); if (newFile.fail() || !boost::filesystem::is_regular_file(_path)) - BOOST_THROW_EXCEPTION(runtime_error("Failed to create a file: \"" + _path.string() + "\".")); \ + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create a file: \"" + _path.string() + "\".")); \ newFile << _content; } @@ -80,7 +79,7 @@ bool solidity::test::createSymlinkIfSupportedByFilesystem( ) return false; else - BOOST_THROW_EXCEPTION(runtime_error( + BOOST_THROW_EXCEPTION(std::runtime_error( "Failed to create a symbolic link: \"" + _linkName.string() + "\"" " -> " + _targetPath.string() + "\"." " " + symlinkCreationError.message() + "." diff --git a/test/Metadata.cpp b/test/Metadata.cpp index 88a1f7b42f08..2fea65c65be0 100644 --- a/test/Metadata.cpp +++ b/test/Metadata.cpp @@ -26,8 +26,6 @@ #include #include -using namespace std; - namespace solidity::test { @@ -54,7 +52,7 @@ bytes bytecodeSansMetadata(bytes const& _bytecode) return bytes(_bytecode.begin(), _bytecode.end() - static_cast(metadataSize) - 2); } -string bytecodeSansMetadata(string const& _bytecode) +std::string bytecodeSansMetadata(std::string const& _bytecode) { return util::toHex(bytecodeSansMetadata(fromHex(_bytecode, util::WhenError::Throw))); } @@ -73,11 +71,11 @@ class TinyCBORParser assertThrow(nextType() == MajorType::Map, CBORException, "Fixed-length map expected."); return readLength(); } - string readKey() + std::string readKey() { return readString(); } - string readValue() + std::string readValue() { switch(nextType()) { @@ -132,7 +130,7 @@ class TinyCBORParser if (length == 24) return m_metadata.at(m_pos++); // Unsupported length kind. (Only by this parser.) - assertThrow(false, CBORException, string("Unsupported length ") + to_string(length)); + assertThrow(false, CBORException, std::string("Unsupported length ") + std::to_string(length)); } bytes readBytes(unsigned length) { @@ -140,28 +138,28 @@ class TinyCBORParser m_pos += length; return ret; } - string readString() + std::string readString() { // Expect a text string. assertThrow(nextType() == MajorType::TextString, CBORException, "String expected."); bytes tmp{readBytes(readLength())}; - return string{tmp.begin(), tmp.end()}; + return std::string{tmp.begin(), tmp.end()}; } unsigned m_pos; bytes const& m_metadata; }; -std::optional> parseCBORMetadata(bytes const& _metadata) +std::optional> parseCBORMetadata(bytes const& _metadata) { try { TinyCBORParser parser(_metadata); - map ret; + std::map ret; unsigned count = parser.mapItemCount(); for (unsigned i = 0; i < count; i++) { - string key = parser.readKey(); - string value = parser.readValue(); + std::string key = parser.readKey(); + std::string value = parser.readValue(); ret[std::move(key)] = std::move(value); } return ret; @@ -172,7 +170,7 @@ std::optional> parseCBORMetadata(bytes const& _metadata) } } -bool isValidMetadata(string const& _serialisedMetadata) +bool isValidMetadata(std::string const& _serialisedMetadata) { Json::Value metadata; if (!util::jsonParseStrict(_serialisedMetadata, metadata)) diff --git a/test/TestCase.cpp b/test/TestCase.cpp index 51608bd4a374..ae89bb56f31a 100644 --- a/test/TestCase.cpp +++ b/test/TestCase.cpp @@ -27,21 +27,20 @@ #include #include -using namespace std; using namespace solidity; using namespace solidity::frontend; using namespace solidity::frontend::test; using namespace solidity::util; -void TestCase::printSettings(ostream& _stream, const string& _linePrefix, const bool) +void TestCase::printSettings(std::ostream& _stream, const std::string& _linePrefix, const bool) { auto& settings = m_reader.settings(); if (settings.empty()) return; - _stream << _linePrefix << "// ====" << endl; + _stream << _linePrefix << "// ====" << std::endl; for (auto const& setting: settings) - _stream << _linePrefix << "// " << setting.first << ": " << setting.second << endl; + _stream << _linePrefix << "// " << setting.first << ": " << setting.second << std::endl; } void TestCase::printUpdatedSettings(std::ostream& _stream, std::string const& _linePrefix) @@ -51,7 +50,7 @@ void TestCase::printUpdatedSettings(std::ostream& _stream, std::string const& _l bool TestCase::isTestFilename(boost::filesystem::path const& _filename) { - string extension = _filename.extension().string(); + std::string extension = _filename.extension().string(); return (extension == ".sol" || extension == ".yul" || extension == ".stack") && !boost::starts_with(_filename.string(), "~") && !boost::starts_with(_filename.string(), "."); @@ -63,19 +62,19 @@ bool TestCase::shouldRun() return m_shouldRun; } -void TestCase::expect(string::iterator& _it, string::iterator _end, string::value_type _c) +void TestCase::expect(std::string::iterator& _it, std::string::iterator _end, std::string::value_type _c) { if (_it == _end || *_it != _c) - BOOST_THROW_EXCEPTION(runtime_error(string("Invalid test expectation. Expected: \"") + _c + "\".")); + BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Invalid test expectation. Expected: \"") + _c + "\".")); ++_it; } -void TestCase::printSource(ostream& _stream, string const& _linePrefix, bool const) const +void TestCase::printSource(std::ostream& _stream, std::string const& _linePrefix, bool const) const { printPrefixed(_stream, m_source, _linePrefix); } -void TestCase::printUpdatedExpectations(ostream& _stream, string const& _linePrefix) const +void TestCase::printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const { printPrefixed(_stream, m_obtainedResult, _linePrefix); } @@ -84,30 +83,30 @@ TestCase::TestResult TestCase::checkResult(std::ostream& _stream, const std::str { if (m_expectation != m_obtainedResult) { - string nextIndentLevel = _linePrefix + " "; + std::string nextIndentLevel = _linePrefix + " "; util::AnsiColorized(_stream, _formatted, {util::formatting::BOLD, util::formatting::CYAN}) - << _linePrefix << "Expected result:" << endl; + << _linePrefix << "Expected result:" << std::endl; // TODO could compute a simple diff with highlighted lines printPrefixed(_stream, m_expectation, nextIndentLevel); util::AnsiColorized(_stream, _formatted, {util::formatting::BOLD, util::formatting::CYAN}) - << _linePrefix << "Obtained result:" << endl; + << _linePrefix << "Obtained result:" << std::endl; printPrefixed(_stream, m_obtainedResult, nextIndentLevel); return TestResult::Failure; } return TestResult::Success; } -EVMVersionRestrictedTestCase::EVMVersionRestrictedTestCase(string const& _filename): +EVMVersionRestrictedTestCase::EVMVersionRestrictedTestCase(std::string const& _filename): TestCase(_filename) { - string versionString = m_reader.stringSetting("EVMVersion", "any"); + std::string versionString = m_reader.stringSetting("EVMVersion", "any"); if (versionString == "any") return; - string comparator; + std::string comparator; size_t versionBegin = 0; for (auto character: versionString) - if (!isalpha(character, locale::classic())) + if (!isalpha(character, std::locale::classic())) { comparator += character; versionBegin++; @@ -118,7 +117,7 @@ EVMVersionRestrictedTestCase::EVMVersionRestrictedTestCase(string const& _filena versionString = versionString.substr(versionBegin); std::optional version = langutil::EVMVersion::fromString(versionString); if (!version) - BOOST_THROW_EXCEPTION(runtime_error{"Invalid EVM version: \"" + versionString + "\""}); + BOOST_THROW_EXCEPTION(std::runtime_error{"Invalid EVM version: \"" + versionString + "\""}); langutil::EVMVersion evmVersion = solidity::test::CommonOptions::get().evmVersion(); bool comparisonResult; @@ -135,7 +134,7 @@ EVMVersionRestrictedTestCase::EVMVersionRestrictedTestCase(string const& _filena else if (comparator == "!") comparisonResult = !(evmVersion == version); else - BOOST_THROW_EXCEPTION(runtime_error{"Invalid EVM comparator: \"" + comparator + "\""}); + BOOST_THROW_EXCEPTION(std::runtime_error{"Invalid EVM comparator: \"" + comparator + "\""}); if (!comparisonResult) m_shouldRun = false; diff --git a/test/TestCaseReader.cpp b/test/TestCaseReader.cpp index 7ad752ef816e..da6174cbe9bd 100644 --- a/test/TestCaseReader.cpp +++ b/test/TestCaseReader.cpp @@ -23,36 +23,35 @@ #include #include -using namespace std; using namespace solidity::frontend::test; namespace fs = boost::filesystem; -TestCaseReader::TestCaseReader(string const& _filename): m_fileStream(_filename), m_fileName(_filename) +TestCaseReader::TestCaseReader(std::string const& _filename): m_fileStream(_filename), m_fileName(_filename) { if (!m_fileStream) - BOOST_THROW_EXCEPTION(runtime_error("Cannot open file: \"" + _filename + "\".")); - m_fileStream.exceptions(ios::badbit); + BOOST_THROW_EXCEPTION(std::runtime_error("Cannot open file: \"" + _filename + "\".")); + m_fileStream.exceptions(std::ios::badbit); - tie(m_sources, m_lineNumber) = parseSourcesAndSettingsWithLineNumber(m_fileStream); + std::tie(m_sources, m_lineNumber) = parseSourcesAndSettingsWithLineNumber(m_fileStream); m_unreadSettings = m_settings; } -TestCaseReader::TestCaseReader(istringstream const& _str) +TestCaseReader::TestCaseReader(std::istringstream const& _str) { - tie(m_sources, m_lineNumber) = parseSourcesAndSettingsWithLineNumber( - static_cast(const_cast(_str)) + std::tie(m_sources, m_lineNumber) = parseSourcesAndSettingsWithLineNumber( + static_cast(const_cast(_str)) ); } -string const& TestCaseReader::source() const +std::string const& TestCaseReader::source() const { if (m_sources.sources.size() != 1) - BOOST_THROW_EXCEPTION(runtime_error("Expected single source definition, but got multiple sources.")); + BOOST_THROW_EXCEPTION(std::runtime_error("Expected single source definition, but got multiple sources.")); return m_sources.sources.at(m_sources.mainSourceFile); } -string TestCaseReader::simpleExpectations() +std::string TestCaseReader::simpleExpectations() { return parseSimpleExpectations(m_fileStream); } @@ -63,13 +62,13 @@ bool TestCaseReader::boolSetting(std::string const& _name, bool _defaultValue) return _defaultValue; m_unreadSettings.erase(_name); - string value = m_settings.at(_name); + std::string value = m_settings.at(_name); if (value == "false") return false; if (value == "true") return true; - BOOST_THROW_EXCEPTION(runtime_error("Invalid Boolean value: " + value + ".")); + BOOST_THROW_EXCEPTION(std::runtime_error("Invalid Boolean value: " + value + ".")); } size_t TestCaseReader::sizetSetting(std::string const& _name, size_t _defaultValue) @@ -83,7 +82,7 @@ size_t TestCaseReader::sizetSetting(std::string const& _name, size_t _defaultVal return stoul(m_settings.at(_name)); } -string TestCaseReader::stringSetting(string const& _name, string const& _defaultValue) +std::string TestCaseReader::stringSetting(std::string const& _name, std::string const& _defaultValue) { if (m_settings.count(_name) == 0) return _defaultValue; @@ -95,26 +94,26 @@ string TestCaseReader::stringSetting(string const& _name, string const& _default void TestCaseReader::ensureAllSettingsRead() const { if (!m_unreadSettings.empty()) - BOOST_THROW_EXCEPTION(runtime_error( + BOOST_THROW_EXCEPTION(std::runtime_error( "Unknown setting(s): " + util::joinHumanReadable(m_unreadSettings | ranges::views::keys) )); } -pair TestCaseReader::parseSourcesAndSettingsWithLineNumber(istream& _stream) +std::pair TestCaseReader::parseSourcesAndSettingsWithLineNumber(std::istream& _stream) { - map sources; - map externalSources; - string currentSourceName; - string currentSource; - string line; + std::map sources; + std::map externalSources; + std::string currentSourceName; + std::string currentSource; + std::string line; size_t lineNumber = 1; - static string const externalSourceDelimiterStart("==== ExternalSource:"); - static string const sourceDelimiterStart("==== Source:"); - static string const sourceDelimiterEnd("===="); - static string const comment("// "); - static string const settingsDelimiter("// ===="); - static string const expectationsDelimiter("// ----"); + static std::string const externalSourceDelimiterStart("==== ExternalSource:"); + static std::string const sourceDelimiterStart("==== Source:"); + static std::string const sourceDelimiterEnd("===="); + static std::string const comment("// "); + static std::string const settingsDelimiter("// ===="); + static std::string const expectationsDelimiter("// ----"); bool sourcePart = true; while (getline(_stream, line)) { @@ -138,19 +137,19 @@ pair TestCaseReader::parseSourcesAndSettingsWithLineNumber(is line.size() - sourceDelimiterEnd.size() - sourceDelimiterStart.size() )); if (sources.count(currentSourceName)) - BOOST_THROW_EXCEPTION(runtime_error("Multiple definitions of test source \"" + currentSourceName + "\".")); + BOOST_THROW_EXCEPTION(std::runtime_error("Multiple definitions of test source \"" + currentSourceName + "\".")); } else if (boost::algorithm::starts_with(line, externalSourceDelimiterStart) && boost::algorithm::ends_with(line, sourceDelimiterEnd)) { - string externalSourceString = boost::trim_copy(line.substr( + std::string externalSourceString = boost::trim_copy(line.substr( externalSourceDelimiterStart.size(), line.size() - sourceDelimiterEnd.size() - externalSourceDelimiterStart.size() )); - string externalSourceName; + std::string externalSourceName; size_t remappingPos = externalSourceString.find('='); // Does the external source define a remapping? - if (remappingPos != string::npos) + if (remappingPos != std::string::npos) { externalSourceName = boost::trim_copy(externalSourceString.substr(0, remappingPos)); externalSourceString = boost::trim_copy(externalSourceString.substr(remappingPos + 1)); @@ -164,16 +163,16 @@ pair TestCaseReader::parseSourcesAndSettingsWithLineNumber(is if (!externalSourceTarget.is_relative() || !externalSourceTarget.root_path().empty()) // NOTE: UNC paths (ones starting with // or \\) are considered relative by Boost // since they have an empty root directory (but non-empty root name). - BOOST_THROW_EXCEPTION(runtime_error("External Source paths need to be relative to the location of the test case.")); + BOOST_THROW_EXCEPTION(std::runtime_error("External Source paths need to be relative to the location of the test case.")); fs::path externalSourceFullPath = testCaseParentDir / externalSourceTarget; - string externalSourceContent; + std::string externalSourceContent; if (!fs::exists(externalSourceFullPath)) - BOOST_THROW_EXCEPTION(runtime_error("External Source '" + externalSourceTarget.string() + "' not found.")); + BOOST_THROW_EXCEPTION(std::runtime_error("External Source '" + externalSourceTarget.string() + "' not found.")); else externalSourceContent = util::readFileAsString(externalSourceFullPath); if (sources.count(externalSourceName)) - BOOST_THROW_EXCEPTION(runtime_error("Multiple definitions of test source \"" + externalSourceName + "\".")); + BOOST_THROW_EXCEPTION(std::runtime_error("Multiple definitions of test source \"" + externalSourceName + "\".")); sources[externalSourceName] = externalSourceContent; externalSources[externalSourceName] = externalSourceTarget; } @@ -183,32 +182,32 @@ pair TestCaseReader::parseSourcesAndSettingsWithLineNumber(is else if (boost::algorithm::starts_with(line, comment)) { size_t colon = line.find(':'); - if (colon == string::npos) - BOOST_THROW_EXCEPTION(runtime_error(string("Expected \":\" inside setting."))); - string key = line.substr(comment.size(), colon - comment.size()); - string value = line.substr(colon + 1); + if (colon == std::string::npos) + BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Expected \":\" inside setting."))); + std::string key = line.substr(comment.size(), colon - comment.size()); + std::string value = line.substr(colon + 1); boost::algorithm::trim(key); boost::algorithm::trim(value); m_settings[key] = value; } else - BOOST_THROW_EXCEPTION(runtime_error(string("Expected \"//\" or \"// ---\" to terminate settings and source."))); + BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Expected \"//\" or \"// ---\" to terminate settings and source."))); } // Register the last source as the main one sources[currentSourceName] = currentSource; return {{std::move(sources), std::move(externalSources), std::move(currentSourceName)}, lineNumber}; } -string TestCaseReader::parseSimpleExpectations(istream& _file) +std::string TestCaseReader::parseSimpleExpectations(std::istream& _file) { - string result; - string line; - while (getline(_file, line)) + std::string result; + std::string line; + while (std::getline(_file, line)) if (boost::algorithm::starts_with(line, "// ")) result += line.substr(3) + "\n"; else if (line == "//") result += "\n"; else - BOOST_THROW_EXCEPTION(runtime_error("Test expectations must start with \"// \".")); + BOOST_THROW_EXCEPTION(std::runtime_error("Test expectations must start with \"// \".")); return result; } diff --git a/test/soltest.cpp b/test/soltest.cpp index 443c4c5de3e3..5fde4bda5679 100644 --- a/test/soltest.cpp +++ b/test/soltest.cpp @@ -35,6 +35,7 @@ #pragma warning(pop) #endif +#pragma GCC diagnostic pop #pragma GCC diagnostic pop #include @@ -49,7 +50,6 @@ using namespace boost::unit_test; using namespace solidity::frontend::test; namespace fs = boost::filesystem; -using namespace std; namespace { @@ -100,7 +100,7 @@ void runTestCase(TestCase::Config const& _config, TestCase::TestCaseCreator cons { try { - stringstream errorStream; + std::stringstream errorStream; auto testCase = _testCaseCreator(_config); if (testCase->shouldRun()) switch (testCase->run(errorStream)) @@ -133,7 +133,7 @@ int registerTests( boost::unit_test::test_suite& _suite, boost::filesystem::path const& _basepath, boost::filesystem::path const& _path, - vector const& _labels, + std::vector const& _labels, TestCase::TestCaseCreator _testCaseCreator, solidity::test::Batcher& _batcher ) @@ -176,9 +176,9 @@ int registerTests( // This must be a vector of unique_ptrs because Boost.Test keeps the equivalent of a string_view to the filename // that is passed in. If the strings were stored directly in the vector, pointers/references to them would be // invalidated on reallocation. - static vector> filenames; + static std::vector> filenames; - filenames.emplace_back(make_unique(_path.string())); + filenames.emplace_back(std::make_unique(_path.string())); auto test_case = make_test_case( [config, _testCaseCreator] { @@ -236,11 +236,14 @@ test_suite* init_unit_test_suite(int /*argc*/, char* /*argv*/[]) exit(EXIT_FAILURE); if (solidity::test::CommonOptions::get().disableSemanticTests) - cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl; + std::cout << std::endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << std::endl << std::endl; + + if (!solidity::test::CommonOptions::get().enforceGasTest) + std::cout << std::endl << "WARNING :: Gas Cost Expectations are not being enforced" << std::endl << std::endl; Batcher batcher(CommonOptions::get().selectedBatch, CommonOptions::get().batches); if (CommonOptions::get().batches > 1) - cout << "Batch " << CommonOptions::get().selectedBatch << " out of " << CommonOptions::get().batches << endl; + std::cout << "Batch " << CommonOptions::get().selectedBatch << " out of " << CommonOptions::get().batches << std::endl; // Batch the boost tests BoostBatcher boostBatcher(batcher); @@ -287,12 +290,12 @@ test_suite* init_unit_test_suite(int /*argc*/, char* /*argv*/[]) } catch (solidity::test::ConfigException const& exception) { - cerr << exception.what() << endl; + std::cerr << exception.what() << std::endl; exit(EXIT_FAILURE); } catch (std::runtime_error const& exception) { - cerr << exception.what() << endl; + std::cerr << exception.what() << std::endl; exit(EXIT_FAILURE); } From ac398869ad8ab1191f5c52fc58703877e3766ebc Mon Sep 17 00:00:00 2001 From: Martin Blicha Date: Wed, 7 Feb 2024 12:38:41 +0100 Subject: [PATCH 110/189] SMTChecker: Respect signedness of integer type When creating zero-value expression, we need to respect the signedness of the passed type. --- libsolidity/formal/SymbolicTypes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp index 45b872b3d0d9..350e18bdcf77 100644 --- a/libsolidity/formal/SymbolicTypes.cpp +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -476,7 +476,7 @@ smtutil::Expression zeroValue(frontend::Type const* _type) if (isSupportedType(*_type)) { if (isNumber(*_type)) - return 0; + return isSigned(_type) ? smtutil::Expression(s256(0)) : smtutil::Expression(static_cast(0)); if (isBool(*_type)) return smtutil::Expression(false); if (isArray(*_type) || isMapping(*_type)) From 8770952466d9c7c16cabe534c843e29ef27f7a78 Mon Sep 17 00:00:00 2001 From: Martin Blicha Date: Wed, 7 Feb 2024 12:39:04 +0100 Subject: [PATCH 111/189] SMTChecker: Unsigned number expressions should have unsigned type --- libsmtutil/SolverInterface.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsmtutil/SolverInterface.h b/libsmtutil/SolverInterface.h index 4a21f18410b8..82f81113358a 100644 --- a/libsmtutil/SolverInterface.h +++ b/libsmtutil/SolverInterface.h @@ -124,7 +124,7 @@ class Expression explicit Expression(std::string _name, std::vector _arguments, SortPointer _sort): name(std::move(_name)), arguments(std::move(_arguments)), sort(std::move(_sort)) {} Expression(size_t _number): Expression(std::to_string(_number), {}, SortProvider::uintSort) {} - Expression(u256 const& _number): Expression(_number.str(), {}, SortProvider::sintSort) {} + Expression(u256 const& _number): Expression(_number.str(), {}, SortProvider::uintSort) {} Expression(s256 const& _number): Expression( _number >= 0 ? _number.str() : "-", _number >= 0 ? From a1ad970aa10cb9dcf55af859a0de57ed41893722 Mon Sep 17 00:00:00 2001 From: Martin Blicha Date: Fri, 9 Feb 2024 16:07:52 +0100 Subject: [PATCH 112/189] SMTChecker: Add test case --- test/libsolidity/smtCheckerTests/types/mapping_6.sol | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 test/libsolidity/smtCheckerTests/types/mapping_6.sol diff --git a/test/libsolidity/smtCheckerTests/types/mapping_6.sol b/test/libsolidity/smtCheckerTests/types/mapping_6.sol new file mode 100644 index 000000000000..f2b6d74d7a43 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/mapping_6.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 + + +// Regression for handling signedness, see issues #14791 and #14792 +contract C { + mapping(bool => int240) internal v1; + mapping(bytes14 => bytes15) internal v; + + function f() public payable { + delete v["A"]; + } +} From 22da46c91ceaa4e1a8061b16b69123b5b4ca2457 Mon Sep 17 00:00:00 2001 From: Martin Blicha Date: Fri, 9 Feb 2024 16:25:56 +0100 Subject: [PATCH 113/189] Update changelog --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 3d64b1b80e6f..a33421f1cc56 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Compiler Features: Bugfixes: + * SMTChecker: Fix internal error caused by not respecting the sign of an integer type when constructing zero-value SMT expressions. ### 0.8.24 (2024-01-25) From 4d163ff4bb6b84afff0dfd5817ad52b03e5aa521 Mon Sep 17 00:00:00 2001 From: Simon Perriard Date: Sun, 18 Feb 2024 20:13:37 +0100 Subject: [PATCH 114/189] Convert tagPos to a size for address size computation --- libevmasm/Assembly.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 203db8613d24..acf6067697df 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -861,8 +861,8 @@ LinkerObject const& Assembly::assemble() const immutableReferencesBySub = linkerObject.immutableReferences; } for (size_t tagPos: sub->m_tagPositionsInBytecode) - if (tagPos != std::numeric_limits::max() && tagPos > subTagSize) - subTagSize = tagPos; + if (tagPos != std::numeric_limits::max() && numberEncodingSize(tagPos) > subTagSize) + subTagSize = numberEncodingSize(tagPos); } bool setsImmutables = false; From 8e212553d6f96031d000914cd19550cfbc06eca6 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 19 Feb 2024 21:10:34 +0100 Subject: [PATCH 115/189] Account for subassemblies in tag size calculation, test adjustments and changelog entry. --- Changelog.md | 1 + libevmasm/Assembly.cpp | 12 ++++++++++++ test/cmdlineTests/function_debug_info/output | 2 +- .../output.json | 4 ++-- test/libsolidity/GasCosts.cpp | 16 ++++++++-------- .../semanticTests/arithmetics/check_var_init.sol | 2 +- .../array/constant_var_as_array_length.sol | 2 +- ...leanup_during_multi_element_per_slot_copy.sol | 6 +++--- .../array/fixed_arrays_in_constructors.sol | 2 +- .../semanticTests/array/reusing_memory.sol | 2 +- .../byte_array_to_storage_cleanup.sol | 6 +++--- .../constructor/arrays_in_constructors.sol | 2 +- .../constructor/bytes_in_constructors_packer.sol | 2 +- .../bytes_in_constructors_unpacker.sol | 2 +- .../constructor_arguments_external.sol | 4 ++-- .../constructor_static_array_argument.sol | 2 +- .../constructor_inheritance_init_order.sol | 2 +- .../constructor_inheritance_init_order_2.sol | 6 +++--- .../semanticTests/constructor_with_params.sol | 4 ++-- ...nstructor_with_params_diamond_inheritance.sol | 4 ++-- .../constructor_with_params_inheritance.sol | 4 ++-- .../events/event_emit_from_other_contract.sol | 6 +++--- .../externalContracts/FixedFeeRegistrar.sol | 6 +++--- .../semanticTests/externalContracts/base64.sol | 6 +++--- .../externalContracts/deposit_contract.sol | 4 ++-- .../externalContracts/prbmath_signed.sol | 6 +++--- .../externalContracts/prbmath_unsigned.sol | 6 +++--- .../externalContracts/ramanujan_pi.sol | 6 +++--- .../semanticTests/externalContracts/strings.sol | 6 +++--- .../creation_function_call_with_args.sol | 6 +++--- .../creation_function_call_with_salt.sol | 6 +++--- .../functionCall/gas_and_value_basic.sol | 6 +++--- .../functionCall/gas_and_value_brace_syntax.sol | 6 +++--- .../functionCall/send_zero_ether.sol | 2 +- .../semanticTests/immutable/use_scratch.sol | 4 ++-- .../inheritance/member_notation_ctor.sol | 4 ++-- .../inheritance/value_for_constructor.sol | 4 ++-- .../isoltestTesting/balance_other_contract.sol | 6 +++--- .../salted_create/prediction_example.sol | 2 +- .../salted_create/salted_create_with_value.sol | 2 +- .../semanticTests/smoke/constructor.sol | 6 +++--- .../semanticTests/state/blockhash_basic.sol | 6 +++--- .../semanticTests/userDefinedValueType/erc20.sol | 4 ++-- .../semanticTests/various/code_length.sol | 2 +- test/libsolidity/semanticTests/various/erc20.sol | 4 ++-- .../various/negative_stack_height.sol | 4 ++-- ...estruct_pre_cancun_multiple_beneficiaries.sol | 2 +- .../various/selfdestruct_pre_cancun_redeploy.sol | 6 +++--- .../semanticTests/various/senders_balance.sol | 6 +++--- .../semanticTests/various/value_complex.sol | 6 +++--- .../semanticTests/various/value_insane.sol | 6 +++--- .../sizeLimits/combined_too_large_shanghai.sol | 2 +- .../sizeLimits/initcode_too_large_shanghai.sol | 2 +- 53 files changed, 125 insertions(+), 112 deletions(-) diff --git a/Changelog.md b/Changelog.md index a33421f1cc56..b2ac59c65af8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Compiler Features: Bugfixes: + * Assembler: Prevent incorrect calculation of tag sizes. * SMTChecker: Fix internal error caused by not respecting the sign of an integer type when constructing zero-value SMT expressions. diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index acf6067697df..569ecda26a84 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -890,6 +890,18 @@ LinkerObject const& Assembly::assemble() const std::multimap subRef; std::vector sizeRef; ///< Pointers to code locations where the size of the program is inserted unsigned bytesPerTag = numberEncodingSize(bytesRequiredForCode); + // Adjust bytesPerTag for references to sub assemblies. + for (AssemblyItem const& i: m_items) + if (i.type() == PushTag) + { + auto [subId, tagId] = i.splitForeignPushTag(); + if (subId == std::numeric_limits::max()) + continue; + assertThrow(subId < m_subs.size(), AssemblyException, "Invalid sub id"); + auto subTagPosition = m_subs[subId]->m_tagPositionsInBytecode.at(tagId); + assertThrow(subTagPosition != std::numeric_limits::max(), AssemblyException, "Reference to tag without position."); + bytesPerTag = std::max(bytesPerTag, numberEncodingSize(subTagPosition)); + } uint8_t tagPush = static_cast(pushInstruction(bytesPerTag)); unsigned bytesRequiredIncludingData = bytesRequiredForCode + 1 + static_cast(m_auxiliaryData.size()); diff --git a/test/cmdlineTests/function_debug_info/output b/test/cmdlineTests/function_debug_info/output index b9ca3f3c40ec..c359d4e1c155 100644 --- a/test/cmdlineTests/function_debug_info/output +++ b/test/cmdlineTests/function_debug_info/output @@ -13,7 +13,7 @@ }, "abi_decode_tuple_t_uint256_fromMemory": { - "entryPoint": 92, + "entryPoint": 88, "parameterSlots": 2, "returnSlots": 1 } diff --git a/test/cmdlineTests/linking_standard_solidity_unresolved_references/output.json b/test/cmdlineTests/linking_standard_solidity_unresolved_references/output.json index 169e99961e93..3a69e3e2d782 100644 --- a/test/cmdlineTests/linking_standard_solidity_unresolved_references/output.json +++ b/test/cmdlineTests/linking_standard_solidity_unresolved_references/output.json @@ -17,11 +17,11 @@ [ { "length": 20, - "start": 174 + "start": 173 }, { "length": 20, - "start": 350 + "start": 349 } ] } diff --git a/test/libsolidity/GasCosts.cpp b/test/libsolidity/GasCosts.cpp index 520732810195..d8dad9e02705 100644 --- a/test/libsolidity/GasCosts.cpp +++ b/test/libsolidity/GasCosts.cpp @@ -101,7 +101,7 @@ BOOST_AUTO_TEST_CASE(string_storage) if (CommonOptions::get().useABIEncoderV1) CHECK_DEPLOY_GAS(133045, 129731, evmVersion); else - CHECK_DEPLOY_GAS(144999, 121229, evmVersion); + CHECK_DEPLOY_GAS(144995, 121229, evmVersion); } // This is only correct on >=Constantinople. else if (!CommonOptions::get().useABIEncoderV1) @@ -110,22 +110,22 @@ BOOST_AUTO_TEST_CASE(string_storage) { // Costs with 0 are cases which cannot be triggered in tests. if (evmVersion < EVMVersion::istanbul()) - CHECK_DEPLOY_GAS(0, 109241, evmVersion); + CHECK_DEPLOY_GAS(0, 109237, evmVersion); else if (evmVersion < EVMVersion::shanghai()) - CHECK_DEPLOY_GAS(0, 97697, evmVersion); + CHECK_DEPLOY_GAS(0, 97693, evmVersion); // Shanghai is cheaper due to `push0` else - CHECK_DEPLOY_GAS(0, 97071, evmVersion); + CHECK_DEPLOY_GAS(0, 97067, evmVersion); } else { if (evmVersion < EVMVersion::istanbul()) - CHECK_DEPLOY_GAS(139013, 123969, evmVersion); + CHECK_DEPLOY_GAS(139009, 123969, evmVersion); else if (evmVersion < EVMVersion::shanghai()) - CHECK_DEPLOY_GAS(123361, 110969, evmVersion); + CHECK_DEPLOY_GAS(123357, 110969, evmVersion); // Shanghai is cheaper due to `push0` else - CHECK_DEPLOY_GAS(121493, 110969, evmVersion); + CHECK_DEPLOY_GAS(121489, 110969, evmVersion); } } else if (evmVersion < EVMVersion::istanbul()) @@ -133,7 +133,7 @@ BOOST_AUTO_TEST_CASE(string_storage) else if (evmVersion < EVMVersion::shanghai()) CHECK_DEPLOY_GAS(114077, 96461, evmVersion); else - CHECK_DEPLOY_GAS(114077, 95835, evmVersion); + CHECK_DEPLOY_GAS(114077, 95831, evmVersion); if (evmVersion >= EVMVersion::byzantium()) { diff --git a/test/libsolidity/semanticTests/arithmetics/check_var_init.sol b/test/libsolidity/semanticTests/arithmetics/check_var_init.sol index 6cc02bd97580..fc5418cb0ed7 100644 --- a/test/libsolidity/semanticTests/arithmetics/check_var_init.sol +++ b/test/libsolidity/semanticTests/arithmetics/check_var_init.sol @@ -16,4 +16,4 @@ contract D { // ---- // f() -> FAILURE, hex"4e487b71", 0x11 // g(), 100 wei -> 1 -// gas legacy: 100388 +// gas legacy: 100380 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 ca65f070657c..45861babaaf3 100644 --- a/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol +++ b/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol @@ -8,7 +8,7 @@ contract C { } // ---- // constructor(): 1, 2, 3 -> -// gas irOptimized: 139656 +// gas irOptimized: 139616 // gas legacy: 180517 // gas legacyOptimized: 150462 // a(uint256): 0 -> 1 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 d2361dac5462..8be83289266e 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,7 +16,7 @@ contract C { } // ---- // constructor() -// gas irOptimized: 226349 -// gas legacy: 215757 -// gas legacyOptimized: 181760 +// gas irOptimized: 226321 +// gas legacy: 215753 +// gas legacyOptimized: 181756 // f() -> 0 diff --git a/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol b/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol index ac11d779a563..c7b2dc77a34b 100644 --- a/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol +++ b/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol @@ -9,7 +9,7 @@ contract Creator { } // ---- // constructor(): 1, 2, 3, 4 -> -// gas irOptimized: 126363 +// gas irOptimized: 126327 // gas legacy: 174186 // gas legacyOptimized: 128709 // r() -> 4 diff --git a/test/libsolidity/semanticTests/array/reusing_memory.sol b/test/libsolidity/semanticTests/array/reusing_memory.sol index 92b0c06bfb26..8f6afe30ba2c 100644 --- a/test/libsolidity/semanticTests/array/reusing_memory.sol +++ b/test/libsolidity/semanticTests/array/reusing_memory.sol @@ -25,5 +25,5 @@ contract Main { // ---- // f(uint256): 0x34 -> 0x46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c1 // gas irOptimized: 111924 -// gas legacy: 125162 +// gas legacy: 125154 // gas legacyOptimized: 113012 diff --git a/test/libsolidity/semanticTests/byte_array_to_storage_cleanup.sol b/test/libsolidity/semanticTests/byte_array_to_storage_cleanup.sol index dc77f0dff83a..877e2a1bce03 100644 --- a/test/libsolidity/semanticTests/byte_array_to_storage_cleanup.sol +++ b/test/libsolidity/semanticTests/byte_array_to_storage_cleanup.sol @@ -28,9 +28,9 @@ contract C { // compileViaYul: also // ---- // constructor() -> -// gas irOptimized: 443406 -// gas legacy: 711299 -// gas legacyOptimized: 482382 +// gas irOptimized: 443402 +// gas legacy: 711295 +// gas legacyOptimized: 482378 // h() -> 0x20, 0x40, 0x00, 0 // ~ emit ev(uint256[],uint256): 0x40, 0x21, 0x02, 0x00, 0x00 // g() -> 0x20, 0x40, 0, 0x00 diff --git a/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol index 3d9c1ffecefe..19d79b6c4fcf 100644 --- a/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol +++ b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol @@ -25,5 +25,5 @@ contract Creator { // ---- // f(uint256,address[]): 7, 0x40, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 7, 8 // gas irOptimized: 424526 -// gas legacy: 581443 +// gas legacy: 581426 // gas legacyOptimized: 444599 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol index 067a64e0e5f6..cfa74f016f6d 100644 --- a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol @@ -25,5 +25,5 @@ contract Creator { // ---- // f(uint256,bytes): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> 7, "h" // gas irOptimized: 275102 -// gas legacy: 418462 +// gas legacy: 418433 // gas legacyOptimized: 291960 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol index 0665cb807a7d..39da45e8a57e 100644 --- a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol @@ -9,7 +9,7 @@ contract Test { // ---- // constructor(): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> // gas irOptimized: 269225 -// gas legacy: 311324 +// gas legacy: 310820 // gas legacyOptimized: 258604 // m_x() -> 7 // m_s() -> 0x20, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" diff --git a/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol b/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol index 3037aaa31753..714ae3e5548c 100644 --- a/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol +++ b/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol @@ -17,8 +17,8 @@ contract Main { } // ---- // constructor(): "abc", true -// gas irOptimized: 104394 +// gas irOptimized: 104374 // gas legacy: 143300 -// gas legacyOptimized: 102961 +// gas legacyOptimized: 102933 // getFlag() -> true // getName() -> "abc" diff --git a/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol b/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol index 22acd1cc3fab..a61192742d04 100644 --- a/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol +++ b/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol @@ -9,7 +9,7 @@ contract C { } // ---- // constructor(): 1, 2, 3, 4 -> -// gas irOptimized: 171015 +// gas irOptimized: 170975 // gas legacy: 218378 // gas legacyOptimized: 176211 // a() -> 1 diff --git a/test/libsolidity/semanticTests/constructor_inheritance_init_order.sol b/test/libsolidity/semanticTests/constructor_inheritance_init_order.sol index 20cd8760b9b9..ece0cc31a651 100644 --- a/test/libsolidity/semanticTests/constructor_inheritance_init_order.sol +++ b/test/libsolidity/semanticTests/constructor_inheritance_init_order.sol @@ -14,5 +14,5 @@ contract B is A { // compileViaYul: true // ---- // constructor() -> -// gas irOptimized: 119640 +// gas irOptimized: 119636 // y() -> 42 diff --git a/test/libsolidity/semanticTests/constructor_inheritance_init_order_2.sol b/test/libsolidity/semanticTests/constructor_inheritance_init_order_2.sol index 820c56a4eea2..654c9af56df1 100644 --- a/test/libsolidity/semanticTests/constructor_inheritance_init_order_2.sol +++ b/test/libsolidity/semanticTests/constructor_inheritance_init_order_2.sol @@ -9,7 +9,7 @@ contract B is A { } // ---- // constructor() -> -// gas irOptimized: 119640 -// gas legacy: 133594 -// gas legacyOptimized: 115341 +// gas irOptimized: 119636 +// gas legacy: 133574 +// gas legacyOptimized: 115337 // y() -> 42 diff --git a/test/libsolidity/semanticTests/constructor_with_params.sol b/test/libsolidity/semanticTests/constructor_with_params.sol index 1916f692fd1a..e427f6d9ab7b 100644 --- a/test/libsolidity/semanticTests/constructor_with_params.sol +++ b/test/libsolidity/semanticTests/constructor_with_params.sol @@ -9,7 +9,7 @@ contract C { } // ---- // constructor(): 2, 0 -> -// gas irOptimized: 101390 -// gas legacy: 115678 +// gas irOptimized: 101370 +// gas legacy: 115614 // i() -> 2 // k() -> 0 diff --git a/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol b/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol index b2f66e4500d8..65070814476d 100644 --- a/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol +++ b/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol @@ -21,9 +21,9 @@ contract D is B, C { } // ---- // constructor(): 2, 0 -> -// gas irOptimized: 151966 +// gas irOptimized: 151950 // gas legacy: 168623 -// gas legacyOptimized: 144577 +// gas legacyOptimized: 144521 // i() -> 2 // j() -> 2 // k() -> 1 diff --git a/test/libsolidity/semanticTests/constructor_with_params_inheritance.sol b/test/libsolidity/semanticTests/constructor_with_params_inheritance.sol index f5b20d9816a0..8cc7c69f689b 100644 --- a/test/libsolidity/semanticTests/constructor_with_params_inheritance.sol +++ b/test/libsolidity/semanticTests/constructor_with_params_inheritance.sol @@ -12,8 +12,8 @@ contract D is C { } // ---- // constructor(): 2, 0 -> -// gas irOptimized: 121805 +// gas irOptimized: 121781 // gas legacy: 137193 -// gas legacyOptimized: 118548 +// gas legacyOptimized: 118504 // i() -> 2 // k() -> 1 diff --git a/test/libsolidity/semanticTests/events/event_emit_from_other_contract.sol b/test/libsolidity/semanticTests/events/event_emit_from_other_contract.sol index f8b5141b97fc..0c56cbb671fc 100644 --- a/test/libsolidity/semanticTests/events/event_emit_from_other_contract.sol +++ b/test/libsolidity/semanticTests/events/event_emit_from_other_contract.sol @@ -15,8 +15,8 @@ contract C { } // ---- // constructor() -> -// gas irOptimized: 165386 -// gas legacy: 244800 -// gas legacyOptimized: 171615 +// gas irOptimized: 165370 +// gas legacy: 244776 +// gas legacyOptimized: 171587 // deposit(bytes32), 18 wei: 0x1234 -> // ~ emit Deposit(address,bytes32,uint256) from 0x137aa4dfc0911524504fcd4d98501f179bc13b4a: #0xc06afe3a8444fc0004668591e8306bfb9968e79e, #0x1234, 0x00 diff --git a/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol b/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol index f9f53275bba9..011fecbd3422 100644 --- a/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol +++ b/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol @@ -74,9 +74,9 @@ contract FixedFeeRegistrar is Registrar { } // ---- // constructor() -// gas irOptimized: 384610 -// gas legacy: 913421 -// gas legacyOptimized: 476928 +// gas irOptimized: 384606 +// gas legacy: 913417 +// gas legacyOptimized: 476924 // reserve(string), 69 ether: 0x20, 3, "abc" -> // ~ emit Changed(string): #0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 // gas irOptimized: 45967 diff --git a/test/libsolidity/semanticTests/externalContracts/base64.sol b/test/libsolidity/semanticTests/externalContracts/base64.sol index 646601e6fb8b..ee2745d1acb9 100644 --- a/test/libsolidity/semanticTests/externalContracts/base64.sol +++ b/test/libsolidity/semanticTests/externalContracts/base64.sol @@ -33,9 +33,9 @@ contract test { // EVMVersion: >=constantinople // ---- // constructor() -// gas irOptimized: 405832 -// gas legacy: 735054 -// gas legacyOptimized: 522722 +// gas irOptimized: 405828 +// gas legacy: 735050 +// gas legacyOptimized: 522718 // encode_inline_asm(bytes): 0x20, 0 -> 0x20, 0 // encode_inline_asm(bytes): 0x20, 1, "f" -> 0x20, 4, "Zg==" // encode_inline_asm(bytes): 0x20, 2, "fo" -> 0x20, 4, "Zm8=" diff --git a/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol b/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol index 4f3342dea72f..caa7fc39c867 100644 --- a/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol +++ b/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol @@ -177,8 +177,8 @@ contract DepositContract is IDepositContract, ERC165 { // ---- // constructor() // gas irOptimized: 1386886 -// gas legacy: 2369061 -// gas legacyOptimized: 1740144 +// gas legacy: 2368733 +// gas legacyOptimized: 1740004 // supportsInterface(bytes4): 0x0 -> 0 // supportsInterface(bytes4): 0xffffffff00000000000000000000000000000000000000000000000000000000 -> false # defined to be false by ERC-165 # // supportsInterface(bytes4): 0x01ffc9a700000000000000000000000000000000000000000000000000000000 -> true # ERC-165 id # diff --git a/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol b/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol index 3311a8dcf444..ba7a208fd71b 100644 --- a/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol +++ b/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol @@ -48,9 +48,9 @@ contract test { } // ---- // constructor() -// gas irOptimized: 1841740 -// gas legacy: 2414091 -// gas legacyOptimized: 1847616 +// gas irOptimized: 1841736 +// gas legacy: 2414087 +// gas legacyOptimized: 1847612 // div(int256,int256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 // gas irOptimized: 22137 // gas legacy: 22767 diff --git a/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol index fb1d1a43524a..90b283db4613 100644 --- a/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol +++ b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol @@ -48,9 +48,9 @@ contract test { } // ---- // constructor() -// gas irOptimized: 1716327 -// gas legacy: 2193550 -// gas legacyOptimized: 1725061 +// gas irOptimized: 1716323 +// gas legacy: 2193546 +// gas legacyOptimized: 1725057 // div(uint256,uint256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 // gas irOptimized: 22004 // gas legacy: 22497 diff --git a/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol b/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol index 3f555405ddbb..4442b6d479d3 100644 --- a/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol +++ b/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol @@ -33,9 +33,9 @@ contract test { } // ---- // constructor() -// gas irOptimized: 407507 -// gas legacy: 615090 -// gas legacyOptimized: 451871 +// gas irOptimized: 407503 +// gas legacy: 615086 +// gas legacyOptimized: 451867 // prb_pi() -> 3141592656369545286 // gas irOptimized: 57478 // gas legacy: 100947 diff --git a/test/libsolidity/semanticTests/externalContracts/strings.sol b/test/libsolidity/semanticTests/externalContracts/strings.sol index 18d6db37df1e..3f88fc39c65e 100644 --- a/test/libsolidity/semanticTests/externalContracts/strings.sol +++ b/test/libsolidity/semanticTests/externalContracts/strings.sol @@ -49,9 +49,9 @@ contract test { } // ---- // constructor() -// gas irOptimized: 630224 -// gas legacy: 1061957 -// gas legacyOptimized: 718937 +// gas irOptimized: 630220 +// gas legacy: 1061953 +// gas legacyOptimized: 718933 // toSlice(string): 0x20, 11, "hello world" -> 11, 0xa0 // gas irOptimized: 22660 // gas legacy: 23190 diff --git a/test/libsolidity/semanticTests/functionCall/creation_function_call_with_args.sol b/test/libsolidity/semanticTests/functionCall/creation_function_call_with_args.sol index 39592f4a4882..34aac74efa04 100644 --- a/test/libsolidity/semanticTests/functionCall/creation_function_call_with_args.sol +++ b/test/libsolidity/semanticTests/functionCall/creation_function_call_with_args.sol @@ -15,7 +15,7 @@ contract D { } // ---- // constructor(): 2 -> -// gas irOptimized: 192703 -// gas legacy: 241234 -// gas legacyOptimized: 192961 +// gas irOptimized: 192663 +// gas legacy: 241170 +// gas legacyOptimized: 192897 // f() -> 2 diff --git a/test/libsolidity/semanticTests/functionCall/creation_function_call_with_salt.sol b/test/libsolidity/semanticTests/functionCall/creation_function_call_with_salt.sol index 3850a0be7fd6..cb26a6e26adf 100644 --- a/test/libsolidity/semanticTests/functionCall/creation_function_call_with_salt.sol +++ b/test/libsolidity/semanticTests/functionCall/creation_function_call_with_salt.sol @@ -17,7 +17,7 @@ contract D { // EVMVersion: >=constantinople // ---- // constructor(): 2 -> -// gas irOptimized: 192866 -// gas legacy: 241606 -// gas legacyOptimized: 193193 +// gas irOptimized: 192826 +// gas legacy: 241536 +// gas legacyOptimized: 193129 // f() -> 2 diff --git a/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol b/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol index 79a829aa77ba..8396cb12db8d 100644 --- a/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol +++ b/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol @@ -38,9 +38,9 @@ contract test { } // ---- // constructor(), 20 wei -> -// gas irOptimized: 252642 -// gas legacy: 391588 -// gas legacyOptimized: 268089 +// gas irOptimized: 252626 +// gas legacy: 391568 +// gas legacyOptimized: 268069 // sendAmount(uint256): 5 -> 5 // outOfGas() -> FAILURE # call to helper should not succeed but amount should be transferred anyway # // checkState() -> false, 15 diff --git a/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol b/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol index 71b6ebb2c7a3..9d9c3f603f78 100644 --- a/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol +++ b/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol @@ -37,9 +37,9 @@ contract test { } // ---- // constructor(), 20 wei -> -// gas irOptimized: 252642 -// gas legacy: 391588 -// gas legacyOptimized: 268089 +// gas irOptimized: 252626 +// gas legacy: 391568 +// gas legacyOptimized: 268069 // sendAmount(uint256): 5 -> 5 // outOfGas() -> FAILURE # call to helper should not succeed but amount should be transferred anyway # // checkState() -> false, 15 diff --git a/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol b/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol index 18a19979295b..a737de5cd4e7 100644 --- a/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol +++ b/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol @@ -16,6 +16,6 @@ contract Main { // ---- // constructor(), 20 wei -> // gas irOptimized: 100264 -// gas legacy: 113411 +// gas legacy: 110555 // gas legacyOptimized: 100361 // s() -> true diff --git a/test/libsolidity/semanticTests/immutable/use_scratch.sol b/test/libsolidity/semanticTests/immutable/use_scratch.sol index 0a5ccf729f59..a7c8ea6a502a 100644 --- a/test/libsolidity/semanticTests/immutable/use_scratch.sol +++ b/test/libsolidity/semanticTests/immutable/use_scratch.sol @@ -15,8 +15,8 @@ contract C { } // ---- // constructor(): 3 -> -// gas irOptimized: 123542 +// gas irOptimized: 123526 // gas legacy: 197645 -// gas legacyOptimized: 137678 +// gas legacyOptimized: 137658 // f() -> 84, 23 // m(uint256): 3 -> 7 diff --git a/test/libsolidity/semanticTests/inheritance/member_notation_ctor.sol b/test/libsolidity/semanticTests/inheritance/member_notation_ctor.sol index 33392e8d48c6..263588abab5c 100644 --- a/test/libsolidity/semanticTests/inheritance/member_notation_ctor.sol +++ b/test/libsolidity/semanticTests/inheritance/member_notation_ctor.sol @@ -19,6 +19,6 @@ contract A { } // ---- // g(int256): -1 -> -1 -// gas legacy: 102086 +// gas legacy: 102078 // g(int256): 10 -> 10 -// gas legacy: 101714 +// gas legacy: 101706 diff --git a/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol b/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol index d8fd8766fec6..9627ad025ddd 100644 --- a/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol +++ b/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol @@ -39,9 +39,9 @@ contract Main { } // ---- // constructor(), 22 wei -> -// gas irOptimized: 261888 +// gas irOptimized: 261864 // gas legacy: 392786 -// gas legacyOptimized: 261633 +// gas legacyOptimized: 261593 // getFlag() -> true // getName() -> "abc" // getBalances() -> 12, 10 diff --git a/test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol b/test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol index 306bc35670fe..1a5c9b19b416 100644 --- a/test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol +++ b/test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol @@ -16,9 +16,9 @@ contract ClientReceipt { } // ---- // constructor(), 2000 wei -> -// gas irOptimized: 169915 -// gas legacy: 230038 -// gas legacyOptimized: 173883 +// gas irOptimized: 169907 +// gas legacy: 230018 +// gas legacyOptimized: 173867 // balance -> 1500 // gas irOptimized: 191881 // gas legacy: 235167 diff --git a/test/libsolidity/semanticTests/salted_create/prediction_example.sol b/test/libsolidity/semanticTests/salted_create/prediction_example.sol index 1c5ce63ad456..e7171536d6bb 100644 --- a/test/libsolidity/semanticTests/salted_create/prediction_example.sol +++ b/test/libsolidity/semanticTests/salted_create/prediction_example.sol @@ -26,4 +26,4 @@ contract C { // compileViaYul: also // ---- // createDSalted(bytes32,uint256): 42, 64 -> -// gas legacy: 102943 +// gas legacy: 102841 diff --git a/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol b/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol index c4db17474223..58b3f076034c 100644 --- a/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol +++ b/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol @@ -23,4 +23,4 @@ contract A { // f(), 10 ether -> 3007, 3008, 3009 // gas irOptimized: 253005 // gas legacy: 381063 -// gas legacyOptimized: 279694 +// gas legacyOptimized: 279658 diff --git a/test/libsolidity/semanticTests/smoke/constructor.sol b/test/libsolidity/semanticTests/smoke/constructor.sol index 79b92867bc0d..55789b2f4b65 100644 --- a/test/libsolidity/semanticTests/smoke/constructor.sol +++ b/test/libsolidity/semanticTests/smoke/constructor.sol @@ -12,9 +12,9 @@ contract C { } // ---- // constructor(), 2 wei: 3 -> -// gas irOptimized: 104412 -// gas legacy: 148308 -// gas legacyOptimized: 106727 +// gas irOptimized: 104396 +// gas legacy: 148256 +// gas legacyOptimized: 106699 // state() -> 3 // balance() -> 2 // balance -> 2 diff --git a/test/libsolidity/semanticTests/state/blockhash_basic.sol b/test/libsolidity/semanticTests/state/blockhash_basic.sol index 01069aa0c6dc..03d6f9fe76ea 100644 --- a/test/libsolidity/semanticTests/state/blockhash_basic.sol +++ b/test/libsolidity/semanticTests/state/blockhash_basic.sol @@ -12,9 +12,9 @@ contract C { } // ---- // constructor() -// gas irOptimized: 108150 -// gas legacy: 152179 -// gas legacyOptimized: 106750 +// gas irOptimized: 108138 +// gas legacy: 152171 +// gas legacyOptimized: 106738 // genesisHash() -> 0x3737373737373737373737373737373737373737373737373737373737373737 // currentHash() -> 0 // f(uint256): 0 -> 0x3737373737373737373737373737373737373737373737373737373737373737 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/erc20.sol b/test/libsolidity/semanticTests/userDefinedValueType/erc20.sol index 27ab530cd76d..ac8609d71324 100644 --- a/test/libsolidity/semanticTests/userDefinedValueType/erc20.sol +++ b/test/libsolidity/semanticTests/userDefinedValueType/erc20.sol @@ -113,8 +113,8 @@ contract ERC20 { // ---- // constructor() // ~ emit Transfer(address,address,uint256): #0x00, #0x1212121212121212121212121212120000000012, 0x14 -// gas irOptimized: 352698 -// gas legacy: 834932 +// gas irOptimized: 352682 +// gas legacy: 834752 // gas legacyOptimized: 412648 // totalSupply() -> 20 // gas irOptimized: 23415 diff --git a/test/libsolidity/semanticTests/various/code_length.sol b/test/libsolidity/semanticTests/various/code_length.sol index 97a7637976eb..a8aac5306f50 100644 --- a/test/libsolidity/semanticTests/various/code_length.sol +++ b/test/libsolidity/semanticTests/various/code_length.sol @@ -59,5 +59,5 @@ contract C { } // ---- // constructor() -// gas legacy: 124168 +// gas legacy: 124136 // f(): true, true -> true, true diff --git a/test/libsolidity/semanticTests/various/erc20.sol b/test/libsolidity/semanticTests/various/erc20.sol index b752a5210152..00c12fb18a70 100644 --- a/test/libsolidity/semanticTests/various/erc20.sol +++ b/test/libsolidity/semanticTests/various/erc20.sol @@ -96,8 +96,8 @@ contract ERC20 { // ---- // constructor() // ~ emit Transfer(address,address,uint256): #0x00, #0x1212121212121212121212121212120000000012, 0x14 -// gas irOptimized: 353276 -// gas legacy: 807683 +// gas irOptimized: 353248 +// gas legacy: 807559 // gas legacyOptimized: 408718 // totalSupply() -> 20 // gas irOptimized: 23415 diff --git a/test/libsolidity/semanticTests/various/negative_stack_height.sol b/test/libsolidity/semanticTests/various/negative_stack_height.sol index 3c9130e9fdf0..7289d2bbd96f 100644 --- a/test/libsolidity/semanticTests/various/negative_stack_height.sol +++ b/test/libsolidity/semanticTests/various/negative_stack_height.sol @@ -65,5 +65,5 @@ contract C { // compileViaYul: false // ---- // constructor() -> -// gas legacy: 575272 -// gas legacyOptimized: 345026 +// gas legacy: 575268 +// gas legacyOptimized: 345022 diff --git a/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_multiple_beneficiaries.sol b/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_multiple_beneficiaries.sol index 57b2d5c66ac4..87e81a2902d1 100644 --- a/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_multiple_beneficiaries.sol +++ b/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_multiple_beneficiaries.sol @@ -36,7 +36,7 @@ contract D { // ---- // constructor(), 2 ether -> // gas irOptimized: 223918 -// gas legacy: 374228 +// gas legacy: 374024 // gas legacyOptimized: 239815 // balance: 0x1111111111111111111111111111111111111111 -> 0 // balance: 0x2222222222222222222222222222222222222222 -> 0 diff --git a/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_redeploy.sol b/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_redeploy.sol index 9c9949854a0a..e0885782c260 100644 --- a/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_redeploy.sol +++ b/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_redeploy.sol @@ -79,9 +79,9 @@ contract D { // EVMVersion: =shanghai // ---- // constructor(), 1 ether -> -// gas irOptimized: 430265 -// gas legacy: 690264 -// gas legacyOptimized: 412819 +// gas irOptimized: 430253 +// gas legacy: 690244 +// gas legacyOptimized: 412799 // exists() -> false // test_deploy_and_terminate() -> // ~ emit Deployed(address,bytes32) from 0x137aa4dfc0911524504fcd4d98501f179bc13b4a: 0x7e6580007e709ac52945fae182c61131d42634e8, 0x1234000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/various/senders_balance.sol b/test/libsolidity/semanticTests/various/senders_balance.sol index 6628454db9db..a1c7d4147fc6 100644 --- a/test/libsolidity/semanticTests/various/senders_balance.sol +++ b/test/libsolidity/semanticTests/various/senders_balance.sol @@ -16,7 +16,7 @@ contract D { } // ---- // constructor(), 27 wei -> -// gas irOptimized: 167865 -// gas legacy: 218459 -// gas legacyOptimized: 167292 +// gas irOptimized: 167857 +// gas legacy: 218435 +// gas legacyOptimized: 167276 // f() -> 27 diff --git a/test/libsolidity/semanticTests/various/value_complex.sol b/test/libsolidity/semanticTests/various/value_complex.sol index 9f262fbdeb51..2adac6bf4cd0 100644 --- a/test/libsolidity/semanticTests/various/value_complex.sol +++ b/test/libsolidity/semanticTests/various/value_complex.sol @@ -19,7 +19,7 @@ contract test { } // ---- // constructor(), 20 wei -> -// gas irOptimized: 172407 -// gas legacy: 252296 -// gas legacyOptimized: 180352 +// gas irOptimized: 172399 +// gas legacy: 252276 +// gas legacyOptimized: 180336 // sendAmount(uint256): 5 -> 8 diff --git a/test/libsolidity/semanticTests/various/value_insane.sol b/test/libsolidity/semanticTests/various/value_insane.sol index 1e591050c3b3..87fa01485d61 100644 --- a/test/libsolidity/semanticTests/various/value_insane.sol +++ b/test/libsolidity/semanticTests/various/value_insane.sol @@ -18,7 +18,7 @@ contract test { } // ---- // constructor(), 20 wei -> -// gas irOptimized: 173271 -// gas legacy: 253820 -// gas legacyOptimized: 180784 +// gas irOptimized: 173263 +// gas legacy: 253800 +// gas legacyOptimized: 180768 // sendAmount(uint256): 5 -> 8 diff --git a/test/libsolidity/syntaxTests/sizeLimits/combined_too_large_shanghai.sol b/test/libsolidity/syntaxTests/sizeLimits/combined_too_large_shanghai.sol index 3fc466db3927..32e8d9133be2 100644 --- a/test/libsolidity/syntaxTests/sizeLimits/combined_too_large_shanghai.sol +++ b/test/libsolidity/syntaxTests/sizeLimits/combined_too_large_shanghai.sol @@ -29,4 +29,4 @@ contract test { // Warning 5574: (0-27130): Contract code size is 27187 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. // Warning 5574: (27132-27224): Contract code size is 27213 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. // Warning 5574: (27226-27319): Contract code size is 27212 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. -// Warning 3860: (27321-27398): Contract initcode size is 54628 bytes and exceeds 49152 bytes (a limit introduced in Shanghai). This contract may not be deployable on Mainnet. Consider enabling the optimizer (with a low "runs" value!), turning off revert strings, or using libraries. +// Warning 3860: (27321-27398): Contract initcode size is 54618 bytes and exceeds 49152 bytes (a limit introduced in Shanghai). 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/sizeLimits/initcode_too_large_shanghai.sol b/test/libsolidity/syntaxTests/sizeLimits/initcode_too_large_shanghai.sol index f82434d5d663..d48693db05b1 100644 --- a/test/libsolidity/syntaxTests/sizeLimits/initcode_too_large_shanghai.sol +++ b/test/libsolidity/syntaxTests/sizeLimits/initcode_too_large_shanghai.sol @@ -27,4 +27,4 @@ contract test { // ==== // EVMVersion: =shanghai // ---- -// Warning 3860: (20321-20415): Contract initcode size is 60896 bytes and exceeds 49152 bytes (a limit introduced in Shanghai). This contract may not be deployable on Mainnet. Consider enabling the optimizer (with a low "runs" value!), turning off revert strings, or using libraries. +// Warning 3860: (20321-20415): Contract initcode size is 60882 bytes and exceeds 49152 bytes (a limit introduced in Shanghai). This contract may not be deployable on Mainnet. Consider enabling the optimizer (with a low "runs" value!), turning off revert strings, or using libraries. From c49d84784e62082756b671bd1ed85c076185fe31 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Tue, 20 Feb 2024 07:27:58 +0100 Subject: [PATCH 116/189] Force install python3.12 for Window bytecode compare job --- .circleci/config.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5ab0b056a287..72d96fe4f871 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1697,8 +1697,12 @@ jobs: # platforms so line ending conversions must absolutely be disabled. - run: git config --global core.autocrlf false - checkout - # Ensure windows has python3 alias required by prepare_bytecode_report - - run: ln -s /c/Python312/python /c/Python312/python3 + - run: + name: Force install python3.12.2 + command: choco install python3 --pre --force --version=3.12.2 + - run: + name: Create a symlink for python3 + command: ln -s /c/ProgramData/chocolatey/bin/python3.12 /c/ProgramData/chocolatey/bin/python3 - attach_workspace: at: build - prepare_bytecode_report: From 1185ad0400223a54777904d1521102eec599d666 Mon Sep 17 00:00:00 2001 From: r0qs Date: Tue, 20 Feb 2024 14:49:28 +0100 Subject: [PATCH 117/189] Build hardhat from latest release in t_ems_ext_hardhat --- .circleci/config.yml | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5ab0b056a287..5b13a794562c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1386,7 +1386,31 @@ jobs: - checkout - attach_workspace: at: /tmp/workspace - - run: git clone --depth 1 https://github.com/nomiclabs/hardhat.git + - run: + name: Ensure pnpm is installed if npm is present + command: | + if command -v npm &> /dev/null; then + sudo npm install -g pnpm + fi + - run: + name: Retrieve Hardhat 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}") + fi + HARDHAT_RELEASE_TAG=$( + curl \ + --silent \ + --location \ + --fail \ + --show-error \ + "${EXTRA_HEADERS[@]}" \ + https://api.github.com/repos/nomiclabs/hardhat/releases/latest \ + | jq --raw-output .tag_name \ + ) + echo "export HARDHAT_RELEASE_TAG='${HARDHAT_RELEASE_TAG}'" >> "$BASH_ENV" + - run: git clone --depth 1 https://github.com/nomiclabs/hardhat.git --branch $HARDHAT_RELEASE_TAG - run: name: Install dependencies command: | From d703b829b2f13d6a6d7b7bdd6271ec3fecdae4dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 11 Feb 2024 16:04:27 +0100 Subject: [PATCH 118/189] gas_diff_stats.py: Add basic unit tests --- .circleci/config.yml | 6 +- test/scripts/test_gas_diff_stats.py | 87 +++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 test/scripts/test_gas_diff_stats.py diff --git a/.circleci/config.yml b/.circleci/config.yml index 72d96fe4f871..e14a4ec78c6f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -973,6 +973,10 @@ jobs: <<: *base_ubuntu2204_small steps: - checkout + - run: + # TODO: Add these to the base image + name: Install gas_diff_stats.py dependencies + command: python3 -m pip install --user parsec tabulate - run: name: Python unit tests command: python3 test/pyscriptTests.py @@ -985,7 +989,7 @@ jobs: - checkout - run: name: Install dependencies - command: python -m pip install --user requests + command: python -m pip install --user requests parsec tabulate - run: name: Python unit tests command: python.exe test/pyscriptTests.py diff --git a/test/scripts/test_gas_diff_stats.py b/test/scripts/test_gas_diff_stats.py new file mode 100644 index 000000000000..4980293aea08 --- /dev/null +++ b/test/scripts/test_gas_diff_stats.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python + +import unittest +from textwrap import dedent + +# NOTE: This test file file only works with scripts/ added to PYTHONPATH so pylint can't find the imports +# pragma pylint: disable=import-error +from gas_diff_stats import collect_statistics +# pragma pylint: enable=import-error + +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)) + + 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)) + + def test_collect_statistics_should_report_sum_of_gas_costs(self): + diff_output = dedent(""" + diff --git a/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol + index 1306529d4..77a330f3c 100644 + --- a/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol + +++ b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol + @@ -38 +38,2 @@ contract D { + -// gas legacy: 102095 + +// gas legacy: 76495 + @@ -40,3 +41,6 @@ contract D { + -// gas irOptimized: 98438588 + -// gas legacy: 98438774 + -// gas legacyOptimized: 98438580 + +// gas irOptimized: 25388 + +// gas legacy: 98413174 + +// gas legacyOptimized: 25380 + @@ -44,3 +48,6 @@ contract D { + -// gas irOptimized: 98438589 + -// gas legacy: 98438774 + -// gas legacyOptimized: 98438580 + +// gas irOptimized: 25389 + +// gas legacy: 98413174 + +// gas legacyOptimized: 25380 + """).splitlines() + + self.assertEqual(collect_statistics(diff_output), ( + 98438588 + 98438589, # -irOptimized + 98438580 + 98438580, # -legacyOptimized + 102095 + 98438774 + 98438774, # -legacy + 25388 + 25389, # +irOptimized + 25380 + 25380, # +legacyOptimized + 76495 + 98413174 + 98413174, # +legacy + )) + + def test_collect_statistics_should_ignore_ir_costs(self): + diff_output = dedent(""" + -// gas legacy: 1 + -// gas ir: 2 + +// gas legacy: 3 + +// gas ir: 4 + """).splitlines() + + self.assertEqual(collect_statistics(diff_output), ( + 0, # -irOptimized + 0, # -legacyOptimized + 1, # -legacy + 0, # +irOptimized + 0, # +legacyOptimized + 3, # +legacy + )) + + def test_collect_statistics_should_ignore_unchanged_costs(self): + diff_output = dedent(""" + -// gas legacy: 1 + // gas legacyOptimized: 2 + +// gas legacy: 3 + """).splitlines() + + self.assertEqual(collect_statistics(diff_output), ( + 0, # -irOptimized + 0, # -legacyOptimized + 1, # -legacy + 0, # +irOptimized + 0, # +legacyOptimized + 3, # +legacy + )) From c6e23311cca599f25d1df87dcae8d2ce1f758793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 11 Feb 2024 13:29:28 +0100 Subject: [PATCH 119/189] gas_diff_stats.py: Add a Python shebang - The script is executable but executes as a Bash script and fails. --- scripts/gas_diff_stats.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/gas_diff_stats.py b/scripts/gas_diff_stats.py index 234d42df0c0e..90a153cd3179 100755 --- a/scripts/gas_diff_stats.py +++ b/scripts/gas_diff_stats.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 """A script to collect gas statistics and print it. Useful to summarize gas differences to semantic tests for a PR / branch. From e28a1d436a15e698b933f2a6cc5b160f2bcb3616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 11 Feb 2024 13:31:42 +0100 Subject: [PATCH 120/189] gas_diff_stats.py: Print errors to stderr, not stdout --- scripts/gas_diff_stats.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/gas_diff_stats.py b/scripts/gas_diff_stats.py index 90a153cd3179..b220485c003d 100755 --- a/scripts/gas_diff_stats.py +++ b/scripts/gas_diff_stats.py @@ -18,7 +18,9 @@ repository. The changes are compared against ``origin/develop``. """ + import subprocess +import sys from pathlib import Path from enum import Enum from parsec import generate, ParseError, regex, string @@ -110,8 +112,8 @@ def try_parse_git_diff(fname): if diff_output: return collect_statistics(diff_output) except subprocess.CalledProcessError as e: - print("Error in the git diff:") - print(e.output) + print("Error in the git diff:", file=sys.stderr) + print(e.output, file=sys.stderr) return None def stat(old, new): return ((new - old) / old) * 100 if old else 0 From 5518a3074f0804e7d075122d27733b71812549c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 11 Feb 2024 13:35:50 +0100 Subject: [PATCH 121/189] gas_diff_stats.py: Don't let shell evaluate file names and support names with spaces --- scripts/gas_diff_stats.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/gas_diff_stats.py b/scripts/gas_diff_stats.py index b220485c003d..6f8087d4813e 100755 --- a/scripts/gas_diff_stats.py +++ b/scripts/gas_diff_stats.py @@ -105,8 +105,7 @@ def semantictest_statistics(): def try_parse_git_diff(fname): try: diff_output = subprocess.check_output( - "git diff --unified=0 origin/develop HEAD " + fname, - shell=True, + ["git", "diff", "--unified=0", "origin/develop", "HEAD", fname], universal_newlines=True ).splitlines() if diff_output: From 39f3e7673f84f874a33515f383efca029b2abc49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 11 Feb 2024 13:36:26 +0100 Subject: [PATCH 122/189] gas_diff_stats.py: Fail when the semantic test dir does not exist --- scripts/gas_diff_stats.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/gas_diff_stats.py b/scripts/gas_diff_stats.py index 6f8087d4813e..922aa7de45ee 100755 --- a/scripts/gas_diff_stats.py +++ b/scripts/gas_diff_stats.py @@ -35,6 +35,8 @@ class Diff(Enum): Minus = 1 Plus = 2 +SEMANTIC_TEST_DIR = Path("test/libsolidity/semanticTests/") + minus = string("-").result(Diff.Minus) plus = string("+").result(Diff.Plus) @@ -119,7 +121,10 @@ def stat(old, new): table = [] - for path in Path("test/libsolidity/semanticTests").rglob("*.sol"): + if not SEMANTIC_TEST_DIR.is_dir(): + sys.exit(f"Semantic tests not found. '{SEMANTIC_TEST_DIR.absolute()}' is missing or not a directory.") + + for path in SEMANTIC_TEST_DIR.rglob("*.sol"): fname = path.as_posix() parsed = try_parse_git_diff(fname) if parsed is None: From 855096b84b71aa5f5dd949f83e2dc90831f1e3f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 11 Feb 2024 15:17:41 +0100 Subject: [PATCH 123/189] gas_diff_stats.py: Skip non-gas lines to avoid having to silence parsing errors --- scripts/gas_diff_stats.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/gas_diff_stats.py b/scripts/gas_diff_stats.py index 922aa7de45ee..5d95d5a1935f 100755 --- a/scripts/gas_diff_stats.py +++ b/scripts/gas_diff_stats.py @@ -89,7 +89,12 @@ def try_parse(line): pass return None - out = [parsed for line in lines if (parsed := try_parse(line)) is not None] + out = [ + parsed + for line in lines + if line.startswith('+// gas ') or line.startswith('-// gas ') + if (parsed := try_parse(line)) is not None + ] diff_kinds = [Diff.Minus, Diff.Plus] codegen_kinds = [Kind.IrOptimized, Kind.LegacyOptimized, Kind.Legacy] return tuple( From b9c7b69c76ed73c57aef3681d6f29dd155d1607e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 11 Feb 2024 15:57:36 +0100 Subject: [PATCH 124/189] gas_diff_stats.py: Explicitly ignore ir gas instead of failing to parse it --- scripts/gas_diff_stats.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/gas_diff_stats.py b/scripts/gas_diff_stats.py index 5d95d5a1935f..518d8c2c7580 100755 --- a/scripts/gas_diff_stats.py +++ b/scripts/gas_diff_stats.py @@ -27,9 +27,10 @@ from tabulate import tabulate class Kind(Enum): - IrOptimized = 1 - Legacy = 2 - LegacyOptimized = 3 + Ir = 1 + IrOptimized = 2 + Legacy = 3 + LegacyOptimized = 4 class Diff(Enum): Minus = 1 @@ -44,6 +45,7 @@ class Diff(Enum): comment = string("//") colon = string(":") +gas_ir = string("gas ir").result(Kind.Ir) 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) @@ -64,7 +66,7 @@ def diff_string() -> (Kind, Diff, int): diff_kind = yield minus | plus yield comment yield space - codegen_kind = yield gas_ir_optimized ^ gas_legacy_optimized ^ gas_legacy + codegen_kind = yield gas_ir_optimized ^ gas_ir ^ gas_legacy_optimized ^ gas_legacy yield colon yield space val = yield number() From 65031d3b3e128cd30283afcec254d2326030069d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 11 Feb 2024 15:22:17 +0100 Subject: [PATCH 125/189] gas_diff_stats.py: Handle errors instead of ignoring them - It's possible now that errors are something really exceptional and don't happen on every run. --- scripts/gas_diff_stats.py | 46 ++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/scripts/gas_diff_stats.py b/scripts/gas_diff_stats.py index 518d8c2c7580..352700252464 100755 --- a/scripts/gas_diff_stats.py +++ b/scripts/gas_diff_stats.py @@ -84,18 +84,11 @@ def collect_statistics(lines) -> (int, int, int, int, int, int): if not lines: raise RuntimeError("Empty list") - def try_parse(line): - try: - return diff_string.parse(line) - except ParseError: - pass - return None - out = [ parsed for line in lines if line.startswith('+// gas ') or line.startswith('-// gas ') - if (parsed := try_parse(line)) is not None + if (parsed := diff_string.parse(line)) is not None ] diff_kinds = [Diff.Minus, Diff.Plus] codegen_kinds = [Kind.IrOptimized, Kind.LegacyOptimized, Kind.Legacy] @@ -111,18 +104,15 @@ def try_parse(line): def semantictest_statistics(): """Prints the tabulated statistics that can be pasted in github.""" - def try_parse_git_diff(fname): - try: - diff_output = subprocess.check_output( - ["git", "diff", "--unified=0", "origin/develop", "HEAD", fname], - universal_newlines=True - ).splitlines() - if diff_output: - return collect_statistics(diff_output) - except subprocess.CalledProcessError as e: - print("Error in the git diff:", file=sys.stderr) - print(e.output, file=sys.stderr) - return None + def parse_git_diff(fname): + diff_output = subprocess.check_output( + ["git", "diff", "--unified=0", "origin/develop", "HEAD", fname], + universal_newlines=True + ).splitlines() + if len(diff_output) == 0: + return None + return collect_statistics(diff_output) + def stat(old, new): return ((new - old) / old) * 100 if old else 0 @@ -133,7 +123,7 @@ def stat(old, new): for path in SEMANTIC_TEST_DIR.rglob("*.sol"): fname = path.as_posix() - parsed = try_parse_git_diff(fname) + parsed = parse_git_diff(fname) if parsed is None: continue ir_optimized = stat(parsed[0], parsed[3]) @@ -150,5 +140,17 @@ def stat(old, new): else: print("No differences found.") +def main(): + try: + semantictest_statistics() + except subprocess.CalledProcessError as exception: + sys.exit(f"Error in the git diff:\n{exception.output}") + except ParseError as exception: + sys.exit( + f"ParseError: {exception}\n\n" + f"{exception.text}\n" + f"{' ' * exception.index}^\n" + ) + if __name__ == "__main__": - semantictest_statistics() + main() From 43274fddef0ecc55f4026d089fef3928b0617680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 11 Feb 2024 14:14:17 +0100 Subject: [PATCH 126/189] gas_diff_stats.py: Improve table and number formatting - Use blanks if percentage is infinite instead of misleadingly showing zero. - Round percentages to nearest integer (but indicate when numbers are only close to zero rather than exactly zero). Differences below one percent are rarely relevant, if ever. - Include % after the numbers. - Put file names in backticks to get them displayed using fixed-width font on github. --- scripts/gas_diff_stats.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/scripts/gas_diff_stats.py b/scripts/gas_diff_stats.py index 352700252464..0707b9857bc8 100755 --- a/scripts/gas_diff_stats.py +++ b/scripts/gas_diff_stats.py @@ -114,7 +114,16 @@ def parse_git_diff(fname): return collect_statistics(diff_output) def stat(old, new): - return ((new - old) / old) * 100 if old else 0 + if old == 0: + return '' + percentage = (new - old) / old * 100 + prefix = ( + # Distinguish actual zero from very small differences + '+' if round(percentage) == 0 and percentage > 0 else + '-' if round(percentage) == 0 and percentage < 0 else + '' + ) + return f'{prefix}{round(percentage)}%' table = [] @@ -129,12 +138,12 @@ def stat(old, new): ir_optimized = stat(parsed[0], parsed[3]) legacy_optimized = stat(parsed[1], parsed[4]) legacy = stat(parsed[2], parsed[5]) - fname = fname.split('/', 3)[-1] - table += [map(str, [fname, ir_optimized, legacy_optimized, legacy])] + fname = f"`{fname.split('/', 3)[-1]}`" + table += [[fname, ir_optimized, legacy_optimized, legacy]] 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"] print(tabulate(table, headers=table_header, tablefmt="github")) print("
") else: From b88d690c66b963282a77e214001b160873434ddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 11 Feb 2024 14:16:33 +0100 Subject: [PATCH 127/189] gas_diff_stats.py: Order rows in a deterministic way (by file path) --- scripts/gas_diff_stats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/gas_diff_stats.py b/scripts/gas_diff_stats.py index 0707b9857bc8..7403b391721c 100755 --- a/scripts/gas_diff_stats.py +++ b/scripts/gas_diff_stats.py @@ -144,7 +144,7 @@ def stat(old, new): if table: print("
Click for a table of gas differences\n") table_header = ["File name", "IR optimized", "Legacy optimized", "Legacy"] - print(tabulate(table, headers=table_header, tablefmt="github")) + print(tabulate(sorted(table), headers=table_header, tablefmt="github")) print("
") else: print("No differences found.") From 36f2d39282a9f951b0f4780193398f5084073874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 11 Feb 2024 16:15:52 +0100 Subject: [PATCH 128/189] CI: Add a smoke test run for gas_diff_stats.py --- .circleci/config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index e14a4ec78c6f..8a16effd0f6b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -980,6 +980,9 @@ jobs: - run: name: Python unit tests command: python3 test/pyscriptTests.py + - run: + name: Smoke test for gas_diff_stats.py + command: scripts/gas_diff_stats.py - matrix_notify_failure_unless_pr t_win_pyscripts: From a851c59bb9c6f226cfb483767a73ebf8b79e8329 Mon Sep 17 00:00:00 2001 From: r0qs Date: Wed, 21 Feb 2024 20:52:11 +0100 Subject: [PATCH 129/189] Fix ENS external test ensuring it applies memory-safe-assembly filter on files and not directories --- test/externalTests/ens.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/externalTests/ens.sh b/test/externalTests/ens.sh index aee291a1a468..178f468549a5 100755 --- a/test/externalTests/ens.sh +++ b/test/externalTests/ens.sh @@ -81,7 +81,7 @@ function ens_test sed -i "s|it\(('Cannot be called if CANNOT_CREATE_SUBDOMAIN is burned and is a new subdomain',\)|it.skip\1|g" test/wrapper/NameWrapper.js sed -i "s|it\(('Cannot be called if PARENT_CANNOT_CONTROL is burned and is an existing subdomain',\)|it.skip\1|g" test/wrapper/NameWrapper.js - find . -name "*.sol" -exec sed -i -e 's/^\(\s*\)\(assembly\)/\1\/\/\/ @solidity memory-safe-assembly\n\1\2/' '{}' \; + find . -name "*.sol" -type f -exec sed -i -e 's/^\(\s*\)\(assembly\)/\1\/\/\/ @solidity memory-safe-assembly\n\1\2/' '{}' \; for preset in $SELECTED_PRESETS; do hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn From d7aa37de2eaa20e5463d2a266d8804c5fa2c80e4 Mon Sep 17 00:00:00 2001 From: r0qs Date: Tue, 20 Feb 2024 18:35:07 +0100 Subject: [PATCH 130/189] Use latest release instead of main branch on external tests --- scripts/externalTests/common.sh | 39 +++++++++++++------------ scripts/externalTests/runners/base.py | 3 +- scripts/externalTests/test_helpers.py | 40 +++++++++++++++----------- test/externalTests/bleeps.sh | 3 +- test/externalTests/brink.sh | 12 ++------ test/externalTests/chainlink.sh | 3 +- test/externalTests/colony.sh | 3 +- test/externalTests/elementfi.sh | 5 ++-- test/externalTests/ens.sh | 3 +- test/externalTests/euler.sh | 3 +- test/externalTests/gnosis.sh | 6 ++-- test/externalTests/gp2.sh | 15 ++-------- test/externalTests/perpetual-pools.sh | 3 +- test/externalTests/pool-together.sh | 8 ++---- test/externalTests/prb-math.py | 3 +- test/externalTests/trident.sh | 3 +- test/externalTests/uniswap.sh | 3 +- test/externalTests/yield-liquidator.sh | 3 +- test/externalTests/zeppelin.sh | 18 ++---------- 19 files changed, 68 insertions(+), 108 deletions(-) diff --git a/scripts/externalTests/common.sh b/scripts/externalTests/common.sh index 3bca968a5aa5..e49be6c8a0a9 100644 --- a/scripts/externalTests/common.sh +++ b/scripts/externalTests/common.sh @@ -106,25 +106,28 @@ function setup_solc function download_project { - local repo="$1" - local ref_type="$2" - local solcjs_ref="$3" - local test_dir="$4" - - [[ $ref_type == commit || $ref_type == branch || $ref_type == tag ]] || assertFail - - printLog "Cloning ${ref_type} ${solcjs_ref} of ${repo}..." - if [[ $ref_type == commit ]]; then - mkdir ext - cd ext - git init - git remote add origin "$repo" - git fetch --depth 1 origin "$solcjs_ref" - git reset --hard FETCH_HEAD - else - git clone --depth 1 "$repo" -b "$solcjs_ref" "$test_dir/ext" - cd ext + local repo_url="$1" + local ref="$2" + local test_dir="$3" + + printLog "Cloning ${repo_url}..." + # Clone the repo ignoring all blobs until needed by git. + # This allows access to commit history but with a fast initial clone + git clone --filter=blob:none "$repo_url" "$test_dir/ext" + cd "$test_dir/ext" + + # If the ref is '' try to use the latest tag as ref + # NOTE: Sadly this will not work with monorepos and may not always + # return the latest tag. + if [[ "$ref" == "" ]]; then + ref=$(git tag --sort=-v:refname | head --lines=1) fi + + [[ $ref != "" ]] || assertFail + + printLog "Using ref: ${ref}" + git checkout "$ref" + echo "Current commit hash: $(git rev-parse HEAD)" } diff --git a/scripts/externalTests/runners/base.py b/scripts/externalTests/runners/base.py index a79ed53442e9..c3807c528c38 100644 --- a/scripts/externalTests/runners/base.py +++ b/scripts/externalTests/runners/base.py @@ -47,7 +47,6 @@ class TestConfig: name: str repo_url: str - ref_type: str ref: str compile_only_presets: List[SettingsPreset] = field(default_factory=list) settings_presets: List[SettingsPreset] = field(default_factory=lambda: list(SettingsPreset)) @@ -134,7 +133,7 @@ def run_test(runner: BaseRunner): print(f"Using compiler version {solc_version}") # Download project - download_project(runner.test_dir, runner.config.repo_url, runner.config.ref_type, runner.config.ref) + download_project(runner.test_dir, runner.config.repo_url, runner.config.ref) # Configure run environment runner.setup_environment() diff --git a/scripts/externalTests/test_helpers.py b/scripts/externalTests/test_helpers.py index 9a573d50adf0..576db4429b05 100644 --- a/scripts/externalTests/test_helpers.py +++ b/scripts/externalTests/test_helpers.py @@ -96,23 +96,29 @@ def parse_command_line(description: str, args: List[str]): return arg_parser.parse_args(args) -def download_project(test_dir: Path, repo_url: str, ref_type: str = "branch", ref: str = "master"): - assert ref_type in ("commit", "branch", "tag") - - print(f"Cloning {ref_type} {ref} of {repo_url}...") - if ref_type == "commit": - os.mkdir(test_dir) - os.chdir(test_dir) - subprocess.run(["git", "init"], check=True) - subprocess.run(["git", "remote", "add", "origin", repo_url], check=True) - subprocess.run(["git", "fetch", "--depth", "1", "origin", ref], check=True) - subprocess.run(["git", "reset", "--hard", "FETCH_HEAD"], check=True) - else: - os.chdir(test_dir.parent) - subprocess.run(["git", "clone", "--no-progress", "--depth", "1", repo_url, "-b", ref, test_dir.resolve()], check=True) - if not test_dir.exists(): - raise RuntimeError("Failed to clone the project.") - os.chdir(test_dir) +def download_project(test_dir: Path, repo_url: str, ref: str = ""): + print(f"Cloning {repo_url}...") + # Clone the repo ignoring all blobs until needed by git. + # This allows access to commit history but with a fast initial clone + subprocess.run(["git", "clone", "--filter", "blob:none", repo_url, test_dir.resolve()], check=True) + if not test_dir.exists(): + raise RuntimeError("Failed to clone the project.") + os.chdir(test_dir) + + # If the ref is '' try to use the latest tag as ref + # NOTE: Sadly this will not work with monorepos and may not always + # return the latest tag. + if ref == "": + tags = subprocess.check_output( + ["git", "tag", "--sort", "-v:refname"], + encoding="ascii" + ).strip().split('\n') + if len(tags) == 0: + raise RuntimeError("Failed to retrieve latest release tag.") + ref = tags[0] + + print(f"Using ref: {ref}") + subprocess.run(["git", "checkout", ref], check=True) if (test_dir / ".gitmodules").exists(): subprocess.run(["git", "submodule", "update", "--init"], check=True) diff --git a/test/externalTests/bleeps.sh b/test/externalTests/bleeps.sh index 30f0ec9240b9..5090e6c12e92 100755 --- a/test/externalTests/bleeps.sh +++ b/test/externalTests/bleeps.sh @@ -38,7 +38,6 @@ function test_fn { HARDHAT_DEPLOY_FIXTURE=true npx --no hardhat --no-compile tes function bleeps_test { local repo="https://github.com/wighawag/bleeps" - local ref_type=branch local ref=main local config_file="hardhat.config.ts" local config_var=config @@ -58,7 +57,7 @@ function bleeps_test print_presets_or_exit "$SELECTED_PRESETS" setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH" - download_project "$repo" "$ref_type" "$ref" "$DIR" + download_project "$repo" "$ref" "$DIR" pushd "common-lib/" neutralize_package_json_hooks diff --git a/test/externalTests/brink.sh b/test/externalTests/brink.sh index 1c3ad2e19a13..48cc4028f3d7 100755 --- a/test/externalTests/brink.sh +++ b/test/externalTests/brink.sh @@ -37,8 +37,7 @@ function test_fn { SNAPSHOT_UPDATE=1 npx --no hardhat test; } function brink_test { local repo="https://github.com/brinktrade/brink-core" - local ref_type=branch - local ref=master + local ref="" local config_file="hardhat.config.js" local config_var="" local extra_settings="metadata: {bytecodeHash: 'none'}" @@ -60,25 +59,18 @@ function brink_test print_presets_or_exit "$SELECTED_PRESETS" setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH" - download_project "$repo" "$ref_type" "$ref" "$DIR" + download_project "$repo" "$ref" "$DIR" # TODO: Remove this when Brink merges https://github.com/brinktrade/brink-core/pull/52 sed -i "s|\(function isValidSignature(bytes \)calldata\( _data, bytes \)calldata\( _signature)\)|\1memory\2memory\3|g" src/Test/MockEIP1271Validator.sol - neutralize_package_lock neutralize_package_json_hooks force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH" force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" "$config_var" "$CURRENT_EVM_VERSION" "$extra_settings" "$extra_optimizer_settings" yarn install yarn add hardhat-gas-reporter - # TODO: Remove when https://github.com/brinktrade/brink-core/issues/48 is fixed. - # TODO: Chai is ESM-only since version 5.x (see: https://github.com/chaijs/chai/issues/1561#issuecomment-1871134261), - # thus, we should stick to version 4.x until Brink and other dependencies also migrate to ESM. - yarn add chai@4.4.0 - replace_version_pragmas - for preset in $SELECTED_PRESETS; do hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn "$config_var" "$extra_settings" "$extra_optimizer_settings" store_benchmark_report hardhat brink "$repo" "$preset" diff --git a/test/externalTests/chainlink.sh b/test/externalTests/chainlink.sh index 4d5080730f80..f11156713c76 100755 --- a/test/externalTests/chainlink.sh +++ b/test/externalTests/chainlink.sh @@ -37,7 +37,6 @@ function test_fn { yarn test; } function chainlink_test { local repo="https://github.com/solidity-external-tests/chainlink" - local ref_type=branch local ref=develop_080 local config_file="hardhat.config.ts" local config_var=config @@ -58,7 +57,7 @@ function chainlink_test print_presets_or_exit "$SELECTED_PRESETS" setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH" - download_project "$repo" "$ref_type" "$ref" "$DIR" + download_project "$repo" "$ref" "$DIR" cd "contracts/" diff --git a/test/externalTests/colony.sh b/test/externalTests/colony.sh index 402865107a8d..1dcd7deaa399 100755 --- a/test/externalTests/colony.sh +++ b/test/externalTests/colony.sh @@ -37,7 +37,6 @@ function test_fn { yarn run test:contracts; } function colony_test { local repo="https://github.com/solidity-external-tests/colonyNetwork.git" - local ref_type=branch local ref="develop_080" local config_file="truffle.js" @@ -57,7 +56,7 @@ function colony_test print_presets_or_exit "$SELECTED_PRESETS" setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH" - download_project "$repo" "$ref_type" "$ref" "$DIR" + download_project "$repo" "$ref" "$DIR" [[ $BINARY_TYPE == native ]] && replace_global_solc "$BINARY_PATH" neutralize_package_json_hooks diff --git a/test/externalTests/elementfi.sh b/test/externalTests/elementfi.sh index 94e622d10c5a..f3ecbb14170f 100755 --- a/test/externalTests/elementfi.sh +++ b/test/externalTests/elementfi.sh @@ -37,8 +37,7 @@ function test_fn { npm run test; } function elementfi_test { local repo="https://github.com/element-fi/elf-contracts" - local ref_type=branch - local ref=main + local ref="" local config_file="hardhat.config.ts" local config_var=config @@ -60,7 +59,7 @@ function elementfi_test print_presets_or_exit "$SELECTED_PRESETS" setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH" - download_project "$repo" "$ref_type" "$ref" "$DIR" + download_project "$repo" "$ref" "$DIR" chmod +x scripts/load-balancer-contracts.sh scripts/load-balancer-contracts.sh diff --git a/test/externalTests/ens.sh b/test/externalTests/ens.sh index aee291a1a468..a79d26c4efb7 100755 --- a/test/externalTests/ens.sh +++ b/test/externalTests/ens.sh @@ -37,7 +37,6 @@ function test_fn { yarn test; } function ens_test { local repo="https://github.com/ensdomains/ens-contracts.git" - local ref_type=commit local ref="083d29a2c50cd0a8307386abf8fadc217b256256" local config_file="hardhat.config.js" @@ -57,7 +56,7 @@ function ens_test print_presets_or_exit "$SELECTED_PRESETS" setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH" - download_project "$repo" "$ref_type" "$ref" "$DIR" + download_project "$repo" "$ref" "$DIR" neutralize_package_lock neutralize_package_json_hooks diff --git a/test/externalTests/euler.sh b/test/externalTests/euler.sh index 80f18e702d92..9ede1a340673 100755 --- a/test/externalTests/euler.sh +++ b/test/externalTests/euler.sh @@ -40,7 +40,6 @@ function test_fn { function euler_test { local repo="https://github.com/euler-xyz/euler-contracts" - local ref_type=branch local ref="master" local config_file="hardhat.config.js" @@ -59,7 +58,7 @@ function euler_test print_presets_or_exit "$SELECTED_PRESETS" setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH" - download_project "$repo" "$ref_type" "$ref" "$DIR" + download_project "$repo" "$ref" "$DIR" # Disable tests that won't pass on the ir presets due to Hardhat heuristics. Note that this also disables # them for other presets but that's fine - we want same code run for benchmarks to be comparable. diff --git a/test/externalTests/gnosis.sh b/test/externalTests/gnosis.sh index 5f7ea4876527..218a5a7504b5 100755 --- a/test/externalTests/gnosis.sh +++ b/test/externalTests/gnosis.sh @@ -37,8 +37,7 @@ function test_fn { npm test; } function gnosis_safe_test { local repo="https://github.com/safe-global/safe-contracts.git" - local ref_type=branch - local ref=main + local ref="" local config_file="hardhat.config.ts" local config_var=userConfig @@ -58,7 +57,7 @@ function gnosis_safe_test print_presets_or_exit "$SELECTED_PRESETS" setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH" - download_project "$repo" "$ref_type" "$ref" "$DIR" + download_project "$repo" "$ref" "$DIR" [[ $BINARY_TYPE == native ]] && replace_global_solc "$BINARY_PATH" # NOTE: The patterns below intentionally have hard-coded versions. @@ -77,7 +76,6 @@ function gnosis_safe_test sed -i "s|\(it\)\((\"can be used only via DELEGATECALL opcode\"\)|\1.skip\2|g" test/libraries/SignMessageLib.spec.ts sed -i "s|it\((\"can only be called from Safe itself\"\)|it.skip\1|g" test/libraries/Migration.spec.ts - neutralize_package_lock neutralize_package_json_hooks force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH" force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" "$config_var" diff --git a/test/externalTests/gp2.sh b/test/externalTests/gp2.sh index bbb442226f62..1e52134a43de 100755 --- a/test/externalTests/gp2.sh +++ b/test/externalTests/gp2.sh @@ -37,8 +37,7 @@ function test_fn { yarn test; } function gp2_test { local repo="https://github.com/cowprotocol/contracts.git" - local ref_type=branch - local ref=main + local ref="" local config_file="hardhat.config.ts" local config_var="config" @@ -58,10 +57,9 @@ function gp2_test print_presets_or_exit "$SELECTED_PRESETS" setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH" - download_project "$repo" "$ref_type" "$ref" "$DIR" + download_project "$repo" "$ref" "$DIR" [[ $BINARY_TYPE == native ]] && replace_global_solc "$BINARY_PATH" - neutralize_package_lock neutralize_package_json_hooks name_hardhat_default_export "$config_file" "$config_var" force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH" @@ -69,14 +67,6 @@ function gp2_test force_hardhat_unlimited_contract_size "$config_file" "$config_var" yarn - # Workaround for error caused by the last release of hardhat-waffle@2.0.6 that bumps ethereum-waffle - # to version 4.0.10 and breaks gp2 build with the following error: - # - # Cannot find module 'ethereum-waffle/dist/cjs/src/deployContract' - # - # See: https://github.com/NomicFoundation/hardhat-waffle/commit/83ee9cb36ee59d0bedacbbd00043f030af104ad0 - yarn add '@nomiclabs/hardhat-waffle@2.0.5' - # Some dependencies come with pre-built artifacts. We want to build from scratch. rm -r node_modules/@gnosis.pm/safe-contracts/build/ @@ -104,7 +94,6 @@ function gp2_test sed -i 's|it\(("should revert when encoding invalid flags"\)|it.skip\1|g' test/GPv2Trade.test.ts replace_version_pragmas - for preset in $SELECTED_PRESETS; do hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn "$config_var" store_benchmark_report hardhat gp2 "$repo" "$preset" diff --git a/test/externalTests/perpetual-pools.sh b/test/externalTests/perpetual-pools.sh index 8d8dc34e212d..31748a40ca2a 100755 --- a/test/externalTests/perpetual-pools.sh +++ b/test/externalTests/perpetual-pools.sh @@ -37,7 +37,6 @@ function test_fn { yarn test; } function perpetual_pools_test { local repo="https://github.com/solidity-external-tests/perpetual-pools-contracts" - local ref_type=branch local ref=pools-v2 local config_file="hardhat.config.ts" local config_var="config" @@ -57,7 +56,7 @@ function perpetual_pools_test print_presets_or_exit "$SELECTED_PRESETS" setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH" - download_project "$repo" "$ref_type" "$ref" "$DIR" + download_project "$repo" "$ref" "$DIR" # Disable tests that won't pass on the ir presets due to Hardhat heuristics. Note that this also disables # them for other presets but that's fine - we want same code run for benchmarks to be comparable. diff --git a/test/externalTests/pool-together.sh b/test/externalTests/pool-together.sh index 3f7141d9bade..f841206ef2a9 100755 --- a/test/externalTests/pool-together.sh +++ b/test/externalTests/pool-together.sh @@ -36,9 +36,8 @@ function test_fn { yarn test; } function pool_together_test { - local repo="https://github.com/pooltogether/v4-core" - local ref_type=branch - local ref=master + local repo="https://github.com/pooltogether/v4-core.git" + local ref="" local config_file="hardhat.config.ts" local config_var="config" @@ -57,13 +56,12 @@ function pool_together_test print_presets_or_exit "$SELECTED_PRESETS" setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH" - download_project "$repo" "$ref_type" "$ref" "$DIR" + download_project "$repo" "$ref" "$DIR" # TODO: Remove this when https://github.com/NomicFoundation/hardhat/issues/3365 gets fixed. sed -i "s|it\(('should fail to return value if value passed does not fit in [0-9]\+ bits'\)|it.skip\1|g" test/libraries/ExtendedSafeCast.test.ts sed -i "s|it\(('should require an rng to be requested'\)|it.skip\1|g" test/DrawBeacon.test.ts - neutralize_package_lock neutralize_package_json_hooks force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH" force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" "$config_var" diff --git a/test/externalTests/prb-math.py b/test/externalTests/prb-math.py index e57688ec2e38..2d1469fc9015 100755 --- a/test/externalTests/prb-math.py +++ b/test/externalTests/prb-math.py @@ -51,8 +51,7 @@ def configure(self): test_config = TestConfig( name="PRBMath", repo_url="https://github.com/PaulRBerg/prb-math.git", - ref_type="branch", - ref="main", + 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. diff --git a/test/externalTests/trident.sh b/test/externalTests/trident.sh index d8d94eb3c101..e5fa914e9cb8 100755 --- a/test/externalTests/trident.sh +++ b/test/externalTests/trident.sh @@ -45,7 +45,6 @@ function test_fn { function trident_test { local repo="https://github.com/sushiswap/trident" - local ref_type=commit # FIXME: Switch back to master branch when https://github.com/sushiswap/trident/issues/303 gets fixed. local ref="0cab5ae884cc9a41223d52791be775c3a053cb26" # master as of 2021-12-16 local config_file="hardhat.config.ts" @@ -66,7 +65,7 @@ function trident_test print_presets_or_exit "$SELECTED_PRESETS" setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH" - download_project "$repo" "$ref_type" "$ref" "$DIR" + download_project "$repo" "$ref" "$DIR" # TODO: Currently tests work only with the exact versions from yarn.lock. # Re-enable this when https://github.com/sushiswap/trident/issues/284 is fixed. diff --git a/test/externalTests/uniswap.sh b/test/externalTests/uniswap.sh index 8d2551b61920..bf5f828987eb 100755 --- a/test/externalTests/uniswap.sh +++ b/test/externalTests/uniswap.sh @@ -37,7 +37,6 @@ function test_fn { UPDATE_SNAPSHOT=1 npx hardhat test; } function uniswap_test { local repo="https://github.com/solidity-external-tests/uniswap-v3-core.git" - local ref_type=branch local ref=main_080 local config_file="hardhat.config.ts" local config_var=config @@ -57,7 +56,7 @@ function uniswap_test print_presets_or_exit "$SELECTED_PRESETS" setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH" - download_project "$repo" "$ref_type" "$ref" "$DIR" + download_project "$repo" "$ref" "$DIR" # Disable tests that won't pass on the ir presets due to Hardhat heuristics. Note that this also disables # them for other presets but that's fine - we want same code run for benchmarks to be comparable. diff --git a/test/externalTests/yield-liquidator.sh b/test/externalTests/yield-liquidator.sh index 7a4353af578e..16a6287e53dd 100755 --- a/test/externalTests/yield-liquidator.sh +++ b/test/externalTests/yield-liquidator.sh @@ -37,7 +37,6 @@ function test_fn { npm run test; } function yield_liquidator_test { local repo="https://github.com/yieldprotocol/yield-liquidator-v2" - local ref_type=branch local ref="master" local config_file="hardhat.config.ts" local config_var="module.exports" @@ -57,7 +56,7 @@ function yield_liquidator_test print_presets_or_exit "$SELECTED_PRESETS" setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH" - download_project "$repo" "$ref_type" "$ref" "$DIR" + download_project "$repo" "$ref" "$DIR" neutralize_package_lock neutralize_package_json_hooks diff --git a/test/externalTests/zeppelin.sh b/test/externalTests/zeppelin.sh index 9f72aa4ec958..655e4814f783 100755 --- a/test/externalTests/zeppelin.sh +++ b/test/externalTests/zeppelin.sh @@ -42,8 +42,7 @@ function test_fn { npm test; } function zeppelin_test { local repo="https://github.com/OpenZeppelin/openzeppelin-contracts.git" - local ref_type=branch - local ref="master" + local ref="" local config_file="hardhat.config.js" local compile_only_presets=( @@ -62,7 +61,7 @@ function zeppelin_test print_presets_or_exit "$SELECTED_PRESETS" setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH" - download_project "$repo" "$ref_type" "$ref" "$DIR" + download_project "$repo" "$ref" "$DIR" # Disable tests that won't pass on the ir presets due to Hardhat heuristics. Note that this also disables # them for other presets but that's fine - we want same code run for benchmarks to be comparable. @@ -100,25 +99,12 @@ function zeppelin_test # Here only the testToInt(248) and testToInt(256) cases fail so change the loop range to skip them sed -i "s|range(8, 256, 8)\(.forEach(bits => testToInt(bits));\)|range(8, 240, 8)\1|" test/utils/math/SafeCast.test.js - neutralize_package_lock neutralize_package_json_hooks force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH" force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" npm install - # TODO: We fix the version to 2.0.3 instead of 2.0.4 since the latter does not work with ethers.js 6.10.0 - # Maybe related to the use of dynamic imports here: https://github.com/NomicFoundation/hardhat/commit/16ae15642951ac324ef7093a3342f7cf3a2a49a4 - npm install @nomicfoundation/hardhat-chai-matchers@2.0.3 - - # TODO: Remove when OpenZeppelin update to ethers 6.11.1+ - # Prior versions of ethers accepts an object instead of a string as an argument to the toUtf8Bytes function. - # However, starting from Ethers version 6.11.1, string arguments are enforced, - # which causes the introduced assertion to fail in some OpenZeppelin tests. - # See: https://github.com/ethers-io/ethers.js/issues/4583 - npm install ethers@6.11.0 - replace_version_pragmas - for preset in $SELECTED_PRESETS; do hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn store_benchmark_report hardhat zeppelin "$repo" "$preset" From a9a870d8fc87e43e89467cabfe9c6b84217c715b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 7 Feb 2024 14:53:31 +0100 Subject: [PATCH 131/189] Test coverage for existing parsing and formatting functionality of gas expectations --- test/libsolidity/util/TestFileParserTests.cpp | 70 +++++++++++++++---- .../util/TestFunctionCallTests.cpp | 28 ++++++++ 2 files changed, 86 insertions(+), 12 deletions(-) diff --git a/test/libsolidity/util/TestFileParserTests.cpp b/test/libsolidity/util/TestFileParserTests.cpp index 59934fdb25cc..46499c14751b 100644 --- a/test/libsolidity/util/TestFileParserTests.cpp +++ b/test/libsolidity/util/TestFileParserTests.cpp @@ -49,18 +49,18 @@ std::vector parse(std::string const& _source, std::map _rawArguments = std::vector{}, - bool _isConstructor = false, - bool _isLibrary = false + FunctionCall const& _call, + FunctionCall::DisplayMode _mode, + std::string _signature = "", + bool _failure = true, + bytes _arguments = bytes{}, + bytes _expectations = bytes{}, + FunctionValue _value = { 0 }, + std::string _argumentComment = "", + std::string _expectationComment = "", + std::vector _rawArguments = std::vector{}, + bool _isConstructor = false, + bool _isLibrary = false ) { BOOST_REQUIRE_EQUAL(_call.expectations.failure, _failure); @@ -1063,6 +1063,52 @@ BOOST_AUTO_TEST_CASE(call_effects) BOOST_CHECK_THROW(parse(source, builtins), std::exception); } +BOOST_AUTO_TEST_CASE(gas) +{ + char const* source = R"( + // f() -> + // gas ir: 3245 + // gas legacy: 5000 + // gas legacyOptimized: 0 + )"; + auto const calls = parse(source); + BOOST_REQUIRE_EQUAL(calls.size(), 1); + BOOST_REQUIRE_EQUAL(calls[0].expectations.failure, false); + BOOST_TEST(calls[0].expectations.gasUsed == (std::map{ + {"ir", 3245}, + {"legacy", 5000}, + {"legacyOptimized", 0}, + })); +} + +BOOST_AUTO_TEST_CASE(gas_before_call) +{ + char const* source = R"( + // gas ir: 3245 + // f() -> + )"; + BOOST_REQUIRE_THROW(parse(source), TestParserError); +} + +BOOST_AUTO_TEST_CASE(gas_invalid_run_type) +{ + char const* source = R"( + // f() -> + // gas ir: 3245 + // gas experimental: 5000 + )"; + BOOST_REQUIRE_THROW(parse(source), TestParserError); +} + +BOOST_AUTO_TEST_CASE(gas_duplicate_run_type) +{ + char const* source = R"( + // f() -> + // gas ir: 3245 + // gas ir: 3245 + )"; + BOOST_REQUIRE_THROW(parse(source), TestParserError); +} BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/util/TestFunctionCallTests.cpp b/test/libsolidity/util/TestFunctionCallTests.cpp index fa3fe995cac5..74a45c32687e 100644 --- a/test/libsolidity/util/TestFunctionCallTests.cpp +++ b/test/libsolidity/util/TestFunctionCallTests.cpp @@ -250,6 +250,34 @@ BOOST_AUTO_TEST_CASE(format_failure_singleline) BOOST_REQUIRE_EQUAL(test.format(), "// f(uint8): 1 -> FAILURE"); } +BOOST_AUTO_TEST_CASE(format_gas) +{ + FunctionCall call{ + "f()", + FunctionValue{0}, + FunctionCallArgs{}, + FunctionCallExpectations{ + std::vector{}, + false, // failure + "some comment", + { + {"ir", 3245}, + {"legacy", 5000}, + {"legacy optimized", 0}, + }, + } + }; + call.omitsArrow = false; + + BOOST_REQUIRE_EQUAL( + TestFunctionCall(call).format(), + "// f() -> #some comment#\n" + "// gas ir: 3245\n" + "// gas legacy: 5000\n" + "// gas legacy optimized: 0" + ); +} + BOOST_AUTO_TEST_SUITE_END() } From e47e948ff6d1319660b98d1502eff44356c303aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 11 Feb 2024 13:07:13 +0100 Subject: [PATCH 132/189] SoltesTypes: Fix outdated comment about allowed keys in gasUsed --- test/libsolidity/util/SoltestTypes.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/libsolidity/util/SoltestTypes.h b/test/libsolidity/util/SoltestTypes.h index b5b746e325e5..672e705534ce 100644 --- a/test/libsolidity/util/SoltestTypes.h +++ b/test/libsolidity/util/SoltestTypes.h @@ -216,7 +216,9 @@ struct FunctionCallExpectations return raw; } /// Gas used by function call - /// Should have values for Yul, YulOptimized, Legacy and LegacyOptimized + /// Keys represent all distinct combinations of compilation settings that affect produced + /// bytecode (and therefore the cost), except for EVM version. E.g. IR codegen without + /// optimization legacy codegen with optimization. std::map gasUsed; }; From d98c9438b2779ad554a179ca2d537d3da2dda33b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 7 Feb 2024 18:39:44 +0100 Subject: [PATCH 133/189] TestFunctionCall::formatGasExpectations(): runType can't be empty in practice --- test/libsolidity/util/TestFunctionCall.cpp | 43 +++++++++++----------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index 64dfbd98253e..716dbff13b44 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -339,27 +339,28 @@ std::string TestFunctionCall::formatGasExpectations( { std::stringstream os; for (auto const& [runType, gasUsed]: (_useActualCost ? m_gasCosts : m_call.expectations.gasUsed)) - if (!runType.empty()) - { - bool differentResults = - m_gasCosts.count(runType) > 0 && - m_call.expectations.gasUsed.count(runType) > 0 && - m_gasCosts.at(runType) != m_call.expectations.gasUsed.at(runType); - - s256 difference = 0; - if (differentResults) - difference = - static_cast(m_gasCosts.at(runType)) - - static_cast(m_call.expectations.gasUsed.at(runType)); - int percent = 0; - if (differentResults) - percent = static_cast( - 100.0 * (static_cast(difference) / static_cast(m_call.expectations.gasUsed.at(runType))) - ); - os << std::endl << _linePrefix << "// gas " << runType << ": " << (gasUsed.str()); - if (_showDifference && differentResults && _useActualCost) - os << " [" << std::showpos << difference << " (" << percent << "%)]"; - } + { + soltestAssert(runType != ""); + + bool differentResults = + m_gasCosts.count(runType) > 0 && + m_call.expectations.gasUsed.count(runType) > 0 && + m_gasCosts.at(runType) != m_call.expectations.gasUsed.at(runType); + + s256 difference = 0; + if (differentResults) + difference = + static_cast(m_gasCosts.at(runType)) - + static_cast(m_call.expectations.gasUsed.at(runType)); + int percent = 0; + if (differentResults) + percent = static_cast( + 100.0 * (static_cast(difference) / static_cast(m_call.expectations.gasUsed.at(runType))) + ); + os << std::endl << _linePrefix << "// gas " << runType << ": " << (gasUsed.str()); + if (_showDifference && differentResults && _useActualCost) + os << " [" << std::showpos << difference << " (" << percent << "%)]"; + } return os.str(); } From c55c8b5504fdb8789741883e5af6f890be2b5cb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 7 Feb 2024 19:42:41 +0100 Subject: [PATCH 134/189] TestFunctionCall::formatGasExpectations(): Move diffing into a helper --- test/libsolidity/util/TestFunctionCall.cpp | 43 +++++++++++++--------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index 716dbff13b44..1d8c72191979 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -21,6 +21,7 @@ #include +#include #include #include #include @@ -331,6 +332,23 @@ std::string TestFunctionCall::formatRawParameters( return os.str(); } +namespace +{ + +std::string formatGasDiff(std::optional const& _gasUsed, std::optional const& _reference) +{ + if (!_reference.has_value() || !_gasUsed.has_value() || _gasUsed == _reference) + return ""; + + s256 difference = static_cast(*_gasUsed) - static_cast(*_reference); + int percent = static_cast( + 100.0 * (static_cast(difference) / static_cast(*_reference)) + ); + return fmt::format("{} ({:+}%)", difference.str(), percent); +} + +} + std::string TestFunctionCall::formatGasExpectations( std::string const& _linePrefix, bool _useActualCost, @@ -342,24 +360,13 @@ std::string TestFunctionCall::formatGasExpectations( { soltestAssert(runType != ""); - bool differentResults = - m_gasCosts.count(runType) > 0 && - m_call.expectations.gasUsed.count(runType) > 0 && - m_gasCosts.at(runType) != m_call.expectations.gasUsed.at(runType); - - s256 difference = 0; - if (differentResults) - difference = - static_cast(m_gasCosts.at(runType)) - - static_cast(m_call.expectations.gasUsed.at(runType)); - int percent = 0; - if (differentResults) - percent = static_cast( - 100.0 * (static_cast(difference) / static_cast(m_call.expectations.gasUsed.at(runType))) - ); - os << std::endl << _linePrefix << "// gas " << runType << ": " << (gasUsed.str()); - if (_showDifference && differentResults && _useActualCost) - os << " [" << std::showpos << difference << " (" << percent << "%)]"; + os << std::endl << _linePrefix << "// gas " << runType << ": " << gasUsed.str(); + std::string gasDiff = formatGasDiff( + m_gasCosts.count(runType) > 0 ? std::make_optional(m_gasCosts.at(runType)) : std::nullopt, + m_call.expectations.gasUsed.count(runType) > 0 ? std::make_optional(m_call.expectations.gasUsed.at(runType)) : std::nullopt + ); + if (_showDifference && !gasDiff.empty() && _useActualCost) + os << " [" << gasDiff << "]"; } return os.str(); } From cd94139f3f666ea3087122a46ab8b461722bd4fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 7 Feb 2024 19:51:57 +0100 Subject: [PATCH 135/189] TestFunctionCall::formatGasExpectations(): Handle corner cases properly --- test/libsolidity/util/TestFunctionCall.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index 1d8c72191979..7a77f7287928 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -340,7 +340,13 @@ std::string formatGasDiff(std::optional const& _gasUsed, std::optional(*_gasUsed) - static_cast(*_reference); + + if (*_reference == 0) + return fmt::format("{}", difference.str()); + int percent = static_cast( 100.0 * (static_cast(difference) / static_cast(*_reference)) ); From d5bae3f5973c54395e450762ce8df25ba1dd3285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 7 Feb 2024 20:00:19 +0100 Subject: [PATCH 136/189] TestFunctionCall::formatGasExpectations(): Helper for getting optional values from a map --- test/libsolidity/util/TestFunctionCall.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index 7a77f7287928..54dd769d6116 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -353,6 +353,16 @@ std::string formatGasDiff(std::optional const& _gasUsed, std::optional gasOrNullopt(std::map const& _map, std::string const& _key) +{ + auto it = _map.find(_key); + if (it == _map.end()) + return std::nullopt; + + return it->second; +} + } std::string TestFunctionCall::formatGasExpectations( @@ -368,8 +378,8 @@ std::string TestFunctionCall::formatGasExpectations( os << std::endl << _linePrefix << "// gas " << runType << ": " << gasUsed.str(); std::string gasDiff = formatGasDiff( - m_gasCosts.count(runType) > 0 ? std::make_optional(m_gasCosts.at(runType)) : std::nullopt, - m_call.expectations.gasUsed.count(runType) > 0 ? std::make_optional(m_call.expectations.gasUsed.at(runType)) : std::nullopt + gasOrNullopt(m_gasCosts, runType), + gasOrNullopt(m_call.expectations.gasUsed, runType) ); if (_showDifference && !gasDiff.empty() && _useActualCost) os << " [" << gasDiff << "]"; From 2ee4d6b1eeaee104007a64ce0b23faa57862800d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 9 Feb 2024 19:24:34 +0100 Subject: [PATCH 137/189] Keep track of code deposit gas when executing contract code --- test/EVMHost.cpp | 7 ++++++- test/EVMHost.h | 7 +++++++ test/ExecutionFramework.cpp | 2 ++ test/ExecutionFramework.h | 6 ++++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 9af4d7099dd2..c43d53d6bc43 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -161,6 +161,7 @@ void EVMHost::reset() recorded_calls.clear(); // Clear EIP-2929 account access indicator recorded_account_accesses.clear(); + m_totalCodeDepositGas = 0; // Mark all precompiled contracts as existing. Existing here means to have a balance (as per EIP-161). // NOTE: keep this in sync with `EVMHost::call` below. @@ -203,6 +204,7 @@ void EVMHost::newTransactionFrame() // Otherwise, the previous behavior (pre-Cancun) is maintained. accounts.erase(address); newlyCreatedAccounts.clear(); + m_totalCodeDepositGas = 0; recorded_selfdestructs.clear(); } @@ -392,15 +394,18 @@ evmc::Result EVMHost::call(evmc_message const& _message) noexcept if (message.kind == EVMC_CREATE || message.kind == EVMC_CREATE2) { - result.gas_left -= static_cast(evmasm::GasCosts::createDataGas * result.output_size); + int64_t codeDepositGas = static_cast(evmasm::GasCosts::createDataGas * result.output_size); + result.gas_left -= codeDepositGas; if (result.gas_left < 0) { + m_totalCodeDepositGas += -result.gas_left; result.gas_left = 0; result.status_code = EVMC_OUT_OF_GAS; // TODO clear some fields? } else { + m_totalCodeDepositGas += codeDepositGas; result.create_address = message.recipient; destination.code = evmc::bytes(result.output_data, result.output_data + result.output_size); destination.codehash = convertToEVMC(keccak256({result.output_data, result.output_size})); diff --git a/test/EVMHost.h b/test/EVMHost.h index 144596e05e38..848314c87974 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -94,6 +94,8 @@ class EVMHost: public evmc::MockedHost /// @returns contents of storage at @param _addr. StorageMap const& get_address_storage(evmc::address const& _addr); + u256 totalCodeDepositGas() const { return m_totalCodeDepositGas; } + static Address convertFromEVMC(evmc::address const& _addr); static evmc::address convertToEVMC(Address const& _addr); static util::h256 convertFromEVMC(evmc::bytes32 const& _data); @@ -137,6 +139,11 @@ class EVMHost: public evmc::MockedHost langutil::EVMVersion m_evmVersion; /// EVM version requested from EVMC (matches the above) evmc_revision m_evmRevision; + + /// The part of the total cost of the current transaction that paid for the code deposits. + /// I.e. GAS_CODE_DEPOSIT times the total size of deployed code of all newly created contracts, + /// including the current contract itself if it was a creation transaction. + u256 m_totalCodeDepositGas; }; class EVMHostPrinter diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index 9fc36440b336..0081ba4a9d27 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -191,6 +191,7 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 auto const gasRefund = std::min(u256(result.gas_refund), totalGasUsed / refundRatio); m_gasUsed = totalGasUsed - gasRefund; + m_gasUsedForCodeDeposit = m_evmcHost->totalCodeDepositGas(); m_transactionSuccessful = (result.status_code == EVMC_SUCCESS); if (m_showMessages) @@ -199,6 +200,7 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 std::cout << " result: " << static_cast(result.status_code) << std::endl; std::cout << " gas used: " << m_gasUsed.str() << std::endl; std::cout << " gas used (without refund): " << totalGasUsed.str() << std::endl; + std::cout << " code deposits only: " << m_gasUsedForCodeDeposit.str() << std::endl; std::cout << " gas refund (total): " << result.gas_refund << std::endl; std::cout << " gas refund (bound): " << gasRefund.str() << std::endl; } diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index 27ceb4f28033..5d66ea10d042 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -303,7 +303,13 @@ class ExecutionFramework util::h160 m_sender = account(0); util::h160 m_contractAddress; bytes m_output; + + /// Total gas used by the transaction, after refund. u256 m_gasUsed; + + /// The portion of @a m_gasUsed spent on code deposits of newly created contracts. + /// May exceed @a m_gasUsed in rare corner cases due to refunds. + u256 m_gasUsedForCodeDeposit; }; #define ABI_CHECK(result, expectation) do { \ From 96233864437e304b6c02c867b29ffe428a72c630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 9 Feb 2024 19:27:07 +0100 Subject: [PATCH 138/189] Separate 'code' gas settings in semantic tests --- test/libsolidity/SemanticTest.cpp | 10 +- test/libsolidity/util/SoltestTypes.h | 5 + test/libsolidity/util/TestFileParser.cpp | 42 ++++++-- test/libsolidity/util/TestFileParserTests.cpp | 102 ++++++++++++++++++ test/libsolidity/util/TestFunctionCall.cpp | 22 ++++ test/libsolidity/util/TestFunctionCall.h | 9 +- .../util/TestFunctionCallTests.cpp | 65 ++++++++--- 7 files changed, 231 insertions(+), 24 deletions(-) diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index 25113b689d17..676c39eaf979 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -569,6 +569,11 @@ bool SemanticTest::checkGasCostExpectation(TestFunctionCall& io_test, bool _comp (_compileViaYul ? "ir"s : "legacy"s) + (m_optimiserSettings == OptimiserSettings::full() ? "Optimized" : ""); + soltestAssert( + io_test.call().expectations.gasUsed.count(setting) == + 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 @@ -587,9 +592,12 @@ bool SemanticTest::checkGasCostExpectation(TestFunctionCall& io_test, bool _comp solAssert(!m_runWithABIEncoderV1Only, ""); io_test.setGasCost(setting, m_gasUsed); + io_test.setCodeDepositGasCost(setting, m_gasUsedForCodeDeposit); + return io_test.call().expectations.gasUsed.count(setting) > 0 && - m_gasUsed == io_test.call().expectations.gasUsed.at(setting); + m_gasUsed == io_test.call().expectations.gasUsed.at(setting) && + m_gasUsedForCodeDeposit == io_test.call().expectations.gasUsedForCodeDeposit.at(setting); } void SemanticTest::printSource(std::ostream& _stream, std::string const& _linePrefix, bool _formatted) const diff --git a/test/libsolidity/util/SoltestTypes.h b/test/libsolidity/util/SoltestTypes.h index 672e705534ce..a2d593fd3c65 100644 --- a/test/libsolidity/util/SoltestTypes.h +++ b/test/libsolidity/util/SoltestTypes.h @@ -220,6 +220,11 @@ struct FunctionCallExpectations /// bytecode (and therefore the cost), except for EVM version. E.g. IR codegen without /// optimization legacy codegen with optimization. std::map gasUsed; + + /// The portion of @a gasUsed spent on code deposits of newly created contracts. + /// May exceed @a gasUsed in rare corner cases due to refunds. + /// Keys must always match @a gasUsedExcludingCode. + std::map gasUsedForCodeDeposit; }; /** diff --git a/test/libsolidity/util/TestFileParser.cpp b/test/libsolidity/util/TestFileParser.cpp index 3fba998a07bf..e7e20f7c5cb5 100644 --- a/test/libsolidity/util/TestFileParser.cpp +++ b/test/libsolidity/util/TestFileParser.cpp @@ -84,18 +84,31 @@ 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) - { - m_scanner.scanNextToken(); - expect(Token::Colon); - if (calls.back().expectations.gasUsed.count(runType) > 0) - throw TestParserError("Gas usage expectation set multiple times."); - calls.back().expectations.gasUsed[runType] = u256(parseDecimalNumber()); - } - else + if (std::set{"ir", "irOptimized", "legacy", "legacyOptimized"}.count(runType) == 0) BOOST_THROW_EXCEPTION(TestParserError( "Expected \"ir\", \"irOptimized\", \"legacy\", or \"legacyOptimized\"." )); + m_scanner.scanNextToken(); + + bool isCodeDepositCost = false; + if (accept(Token::Identifier)) + { + if (m_scanner.currentLiteral() != "code") + BOOST_THROW_EXCEPTION(TestParserError("Expected \"code\" or \":\".")); + isCodeDepositCost = true; + m_scanner.scanNextToken(); + } + + expect(Token::Colon); + + std::map& gasExpectationMap = (isCodeDepositCost ? + calls.back().expectations.gasUsedForCodeDeposit : + calls.back().expectations.gasUsed + ); + if (gasExpectationMap.count(runType) > 0) + throw TestParserError("Gas usage expectation set multiple times."); + + gasExpectationMap[runType] = u256(parseDecimalNumber()); } else { @@ -189,6 +202,17 @@ std::vector TestFileParser::parseFunctio } } } + + for (FunctionCall& call: calls) + { + // Ensure that each specified gas expectation has both components to simplify working with them. + for (auto const& [runType, gas]: call.expectations.gasUsedForCodeDeposit) + call.expectations.gasUsed.try_emplace({runType, 0}); + + for (auto const& [runType, gas]: call.expectations.gasUsed) + call.expectations.gasUsedForCodeDeposit.try_emplace({runType, 0}); + } + return calls; } diff --git a/test/libsolidity/util/TestFileParserTests.cpp b/test/libsolidity/util/TestFileParserTests.cpp index 46499c14751b..7a794f915265 100644 --- a/test/libsolidity/util/TestFileParserTests.cpp +++ b/test/libsolidity/util/TestFileParserTests.cpp @@ -1079,6 +1079,11 @@ BOOST_AUTO_TEST_CASE(gas) {"legacy", 5000}, {"legacyOptimized", 0}, })); + BOOST_TEST(calls[0].expectations.gasUsedForCodeDeposit == (std::map{ + {"ir", 0}, + {"legacy", 0}, + {"legacyOptimized", 0}, + })); } BOOST_AUTO_TEST_CASE(gas_before_call) @@ -1109,6 +1114,103 @@ BOOST_AUTO_TEST_CASE(gas_duplicate_run_type) )"; BOOST_REQUIRE_THROW(parse(source), TestParserError); } + +BOOST_AUTO_TEST_CASE(gas_with_code_deposit_cost) +{ + char const* source = R"( + // f() -> + // gas legacyOptimized code: 1 + // gas ir: 13000 + // gas irOptimized: 6666 + // gas irOptimized code: 666 + // gas legacy code: 0 + // gas legacyOptimized: 2 + )"; + auto const calls = parse(source); + BOOST_REQUIRE_EQUAL(calls.size(), 1); + BOOST_REQUIRE_EQUAL(calls[0].expectations.failure, false); + BOOST_TEST(calls[0].expectations.gasUsed == (std::map{ + {"ir", 13000}, + {"irOptimized", 6666}, + {"legacy", 0}, + {"legacyOptimized", 2}, + })); + BOOST_TEST(calls[0].expectations.gasUsedForCodeDeposit == (std::map{ + {"ir", 0}, + {"irOptimized", 666}, + {"legacy", 0}, + {"legacyOptimized", 1}, + })); +} + +BOOST_AUTO_TEST_CASE(gas_with_code_deposit_cost_invalid_suffix) +{ + char const* source = R"( + // f() -> + // gas ir data: 3245 + )"; + BOOST_REQUIRE_THROW(parse(source), TestParserError); +} + +BOOST_AUTO_TEST_CASE(gas_with_code_deposit_cost_tokens_after_suffix) +{ + char const* source = R"( + // f() -> + // gas ir code code: 3245 + )"; + BOOST_REQUIRE_THROW(parse(source), TestParserError); +} + +BOOST_AUTO_TEST_CASE(gas_with_code_deposit_cost_double_code_gas) +{ + char const* source = R"( + // f() -> + // gas ir: 3245 + // gas ir code: 1 + // gas ir code: 1 + )"; + BOOST_REQUIRE_THROW(parse(source), TestParserError); +} + +BOOST_AUTO_TEST_CASE(gas_with_code_deposit_cost_negative_non_code_cost) +{ + // NOTE: This arrangement is unlikely but may still be possible due to refunds. + char const* source = R"( + // f() -> + // gas ir: 10 + // gas ir code: 20 + )"; + auto const calls = parse(source); + BOOST_REQUIRE_EQUAL(calls.size(), 1); + BOOST_REQUIRE_EQUAL(calls[0].expectations.failure, false); + BOOST_TEST(calls[0].expectations.gasUsed == (std::map{ + {"ir", 10}, + })); + BOOST_TEST(calls[0].expectations.gasUsedForCodeDeposit == (std::map{ + {"ir", 20}, + })); +} + +BOOST_AUTO_TEST_CASE(gas_with_code_deposit_cost_negative_total_cost) +{ + char const* source = R"( + // f() -> + // gas ir: -10 + // gas ir code: 20 + )"; + BOOST_REQUIRE_THROW(parse(source), TestParserError); +} + +BOOST_AUTO_TEST_CASE(gas_with_code_deposit_cost_negative_code_cost) +{ + char const* source = R"( + // f() -> + // gas ir: 20 + // gas ir code: -10 + )"; + BOOST_REQUIRE_THROW(parse(source), TestParserError); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index 54dd769d6116..b22ac3716a13 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -22,6 +22,9 @@ #include #include +#include +#include + #include #include #include @@ -371,11 +374,19 @@ std::string TestFunctionCall::formatGasExpectations( bool _showDifference ) const { + using ranges::views::keys; + using ranges::views::set_symmetric_difference; + + soltestAssert(set_symmetric_difference(m_codeDepositGasCosts | keys, m_gasCosts | keys).empty()); + soltestAssert(set_symmetric_difference(m_call.expectations.gasUsedForCodeDeposit | keys, m_call.expectations.gasUsed | keys).empty()); + std::stringstream os; for (auto const& [runType, gasUsed]: (_useActualCost ? m_gasCosts : m_call.expectations.gasUsed)) { soltestAssert(runType != ""); + u256 gasUsedForCodeDeposit = (_useActualCost ? m_codeDepositGasCosts : m_call.expectations.gasUsedForCodeDeposit).at(runType); + os << std::endl << _linePrefix << "// gas " << runType << ": " << gasUsed.str(); std::string gasDiff = formatGasDiff( gasOrNullopt(m_gasCosts, runType), @@ -383,6 +394,17 @@ std::string TestFunctionCall::formatGasExpectations( ); if (_showDifference && !gasDiff.empty() && _useActualCost) os << " [" << gasDiff << "]"; + + if (gasUsedForCodeDeposit != 0) + { + os << std::endl << _linePrefix << "// gas " << runType << " code: " << gasUsedForCodeDeposit.str(); + std::string codeGasDiff = formatGasDiff( + gasOrNullopt(m_codeDepositGasCosts, runType), + gasOrNullopt(m_call.expectations.gasUsedForCodeDeposit, runType) + ); + if (_showDifference && !codeGasDiff.empty() && _useActualCost) + os << " [" << codeGasDiff << "]"; + } } return os.str(); } diff --git a/test/libsolidity/util/TestFunctionCall.h b/test/libsolidity/util/TestFunctionCall.h index 8b4ed5019d41..cf05b6a216d7 100644 --- a/test/libsolidity/util/TestFunctionCall.h +++ b/test/libsolidity/util/TestFunctionCall.h @@ -51,7 +51,11 @@ class TestFunctionCall ExpectedValuesActualGas }; - TestFunctionCall(FunctionCall _call): m_call(std::move(_call)), m_gasCosts(m_call.expectations.gasUsed) {} + TestFunctionCall(FunctionCall _call): + m_call(std::move(_call)), + m_gasCosts(m_call.expectations.gasUsed), + m_codeDepositGasCosts(m_call.expectations.gasUsedForCodeDeposit) + {} /// Formats this function call test and applies the format that was detected during parsing. /// _renderMode determines the source of values to be inserted into the updated test expectations. @@ -94,6 +98,7 @@ class TestFunctionCall void setFailure(const bool _failure) { m_failure = _failure; } void setRawBytes(const bytes _rawBytes) { m_rawBytes = _rawBytes; } void setGasCost(std::string const& _runType, u256 const& _gasCost) { m_gasCosts[_runType] = _gasCost; } + void setCodeDepositGasCost(std::string const& _runType, u256 const& _gasCost) { m_codeDepositGasCosts[_runType] = _gasCost; } void setContractABI(Json::Value _contractABI) { m_contractABI = std::move(_contractABI); } void setSideEffects(std::vector _sideEffects) { m_call.actualSideEffects = _sideEffects; } @@ -143,6 +148,8 @@ class TestFunctionCall bytes m_rawBytes = bytes{}; /// Actual gas costs std::map m_gasCosts; + /// Actual code deposit gas costs + std::map m_codeDepositGasCosts; /// Transaction status of the actual call. False in case of a REVERT or any other failure. bool m_failure = true; /// JSON object which holds the contract ABI and that is used to set the output formatting diff --git a/test/libsolidity/util/TestFunctionCallTests.cpp b/test/libsolidity/util/TestFunctionCallTests.cpp index 74a45c32687e..b78b7e5f290d 100644 --- a/test/libsolidity/util/TestFunctionCallTests.cpp +++ b/test/libsolidity/util/TestFunctionCallTests.cpp @@ -38,7 +38,7 @@ BOOST_AUTO_TEST_CASE(format_unsigned_singleline) bytes expectedBytes = toBigEndian(u256{1}); ABIType abiType{ABIType::UnsignedDec, ABIType::AlignRight, 32}; Parameter param{expectedBytes, "1", abiType, FormatInfo{}}; - FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}}; + FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}, {}}; FunctionCallArgs arguments{std::vector{param}, std::string{}}; FunctionCall call{"f(uint8)", {0}, arguments, expectations}; call.omitsArrow = false; @@ -58,7 +58,7 @@ BOOST_AUTO_TEST_CASE(format_unsigned_singleline_signed_encoding) bytes expectedBytes = toBigEndian(u256{1}); ABIType abiType{ABIType::UnsignedDec, ABIType::AlignRight, 32}; Parameter param{expectedBytes, "1", abiType, FormatInfo{}}; - FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}}; + FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}, {}}; FunctionCallArgs arguments{std::vector{param}, std::string{}}; FunctionCall call{"f(uint8)", {0}, arguments, expectations}; call.omitsArrow = false; @@ -78,7 +78,7 @@ BOOST_AUTO_TEST_CASE(format_unsigned_multiline) bytes expectedBytes = toBigEndian(u256{1}); ABIType abiType{ABIType::UnsignedDec, ABIType::AlignRight, 32}; Parameter result{expectedBytes, "1", abiType, FormatInfo{}}; - FunctionCallExpectations expectations{std::vector{result}, false, std::string{}, {}}; + FunctionCallExpectations expectations{std::vector{result}, false, std::string{}, {}, {}}; FunctionCallArgs arguments{std::vector{}, std::string{}}; FunctionCall call{"f(uint8)", {0}, arguments, expectations}; call.omitsArrow = false; @@ -93,7 +93,7 @@ BOOST_AUTO_TEST_CASE(format_multiple_unsigned_singleline) bytes expectedBytes = toBigEndian(u256{1}); ABIType abiType{ABIType::UnsignedDec, ABIType::AlignRight, 32}; Parameter param{expectedBytes, "1", abiType, FormatInfo{}}; - FunctionCallExpectations expectations{std::vector{param, param}, false, std::string{}, {}}; + FunctionCallExpectations expectations{std::vector{param, param}, false, std::string{}, {}, {}}; FunctionCallArgs arguments{std::vector{param, param}, std::string{}}; FunctionCall call{"f(uint8, uint8)", {0}, arguments, expectations}; call.omitsArrow = false; @@ -107,7 +107,7 @@ BOOST_AUTO_TEST_CASE(format_signed_singleline) bytes expectedBytes = toBigEndian(u256{-1}); ABIType abiType{ABIType::UnsignedDec, ABIType::AlignRight, 32}; Parameter param{expectedBytes, "-1", abiType, FormatInfo{}}; - FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}}; + FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}, {}}; FunctionCallArgs arguments{std::vector{param}, std::string{}}; FunctionCall call{"f(int8)", {0}, arguments, expectations}; call.omitsArrow = false; @@ -127,7 +127,7 @@ BOOST_AUTO_TEST_CASE(format_hex_singleline) bytes expectedBytes = result + bytes(32 - result.size(), 0); ABIType abiType{ABIType::Hex, ABIType::AlignRight, 32}; Parameter param{expectedBytes, "0x31", abiType, FormatInfo{}}; - FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}}; + FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}, {}}; FunctionCallArgs arguments{std::vector{param}, std::string{}}; FunctionCall call{"f(bytes32)", {0}, arguments, expectations}; call.omitsArrow = false; @@ -149,7 +149,7 @@ BOOST_AUTO_TEST_CASE(format_hex_string_singleline) bytes expectedBytes = fromHex("4200ef"); ABIType abiType{ABIType::HexString, ABIType::AlignLeft, 3}; Parameter param{expectedBytes, "hex\"4200ef\"", abiType, FormatInfo{}}; - FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}}; + FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}, {}}; FunctionCallArgs arguments{std::vector{param}, std::string{}}; FunctionCall call{"f(string)", {0}, arguments, expectations}; call.omitsArrow = false; @@ -163,7 +163,7 @@ BOOST_AUTO_TEST_CASE(format_bool_true_singleline) bytes expectedBytes = toBigEndian(u256{true}); ABIType abiType{ABIType::Boolean, ABIType::AlignRight, 32}; Parameter param{expectedBytes, "true", abiType, FormatInfo{}}; - FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}}; + FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}, {}}; FunctionCallArgs arguments{std::vector{param}, std::string{}}; FunctionCall call{"f(bool)", {0}, arguments, expectations}; call.omitsArrow = false; @@ -184,7 +184,7 @@ BOOST_AUTO_TEST_CASE(format_bool_false_singleline) bytes expectedBytes = toBigEndian(u256{false}); ABIType abiType{ABIType::Boolean, ABIType::AlignRight, 32}; Parameter param{expectedBytes, "false", abiType, FormatInfo{}}; - FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}}; + FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}, {}}; FunctionCallArgs arguments{std::vector{param}, std::string{}}; FunctionCall call{"f(bool)", {0}, arguments, expectations}; call.omitsArrow = false; @@ -198,7 +198,7 @@ BOOST_AUTO_TEST_CASE(format_bool_left_singleline) bytes expectedBytes = toBigEndian(u256{false}); ABIType abiType{ABIType::Boolean, ABIType::AlignLeft, 32}; Parameter param{expectedBytes, "left(false)", abiType, FormatInfo{}}; - FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}}; + FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}, {}}; FunctionCallArgs arguments{std::vector{param}, std::string{}}; FunctionCall call{"f(bool)", {0}, arguments, expectations}; call.omitsArrow = false; @@ -213,7 +213,7 @@ BOOST_AUTO_TEST_CASE(format_hex_number_right_singleline) bytes expectedBytes = result + bytes(32 - result.size(), 0); ABIType abiType{ABIType::Hex, ABIType::AlignRight, 32}; Parameter param{expectedBytes, "right(0x42)", abiType, FormatInfo{}}; - FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}}; + FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}, {}}; FunctionCallArgs arguments{std::vector{param}, std::string{}}; FunctionCall call{"f(bool)", {0}, arguments, expectations}; call.omitsArrow = false; @@ -227,7 +227,7 @@ BOOST_AUTO_TEST_CASE(format_empty_byte_range) bytes expectedBytes; ABIType abiType{ABIType::None, ABIType::AlignNone, 0}; Parameter param{expectedBytes, "1", abiType, FormatInfo{}}; - FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}}; + FunctionCallExpectations expectations{std::vector{param}, false, std::string{}, {}, {}}; FunctionCallArgs arguments{std::vector{}, std::string{}}; FunctionCall call{"f()", {0}, arguments, expectations}; call.omitsArrow = false; @@ -241,7 +241,7 @@ BOOST_AUTO_TEST_CASE(format_failure_singleline) bytes expectedBytes = toBigEndian(u256{1}); ABIType abiType{ABIType::UnsignedDec, ABIType::AlignRight, 32}; Parameter param{expectedBytes, "1", abiType, FormatInfo{}}; - FunctionCallExpectations expectations{std::vector{}, true, std::string{}, {}}; + FunctionCallExpectations expectations{std::vector{}, true, std::string{}, {}, {}}; FunctionCallArgs arguments{std::vector{param}, std::string{}}; FunctionCall call{"f(uint8)", {0}, arguments, expectations}; call.omitsArrow = false; @@ -265,6 +265,11 @@ BOOST_AUTO_TEST_CASE(format_gas) {"legacy", 5000}, {"legacy optimized", 0}, }, + { + {"ir", 0}, + {"legacy", 0}, + {"legacy optimized", 0}, + }, } }; call.omitsArrow = false; @@ -278,6 +283,40 @@ BOOST_AUTO_TEST_CASE(format_gas) ); } +BOOST_AUTO_TEST_CASE(format_gas_with_code_deposit) +{ + FunctionCall call{ + "f()", + FunctionValue{0}, + FunctionCallArgs{}, + FunctionCallExpectations{ + std::vector{}, + false, // failure + "some comment", + { + {"ir", 0}, // Zero costs are shown + {"legacy", 5000}, + {"legacy optimized", 300}, + }, + { + {"ir", 0}, // Zero code deposit costs are omitted + {"legacy", 125}, + {"legacy optimized", 0}, + }, + } + }; + call.omitsArrow = false; + + BOOST_REQUIRE_EQUAL( + TestFunctionCall(call).format(), + "// f() -> #some comment#\n" + "// gas ir: 0\n" + "// gas legacy: 5000\n" + "// gas legacy code: 125\n" + "// gas legacy optimized: 300" + ); +} + BOOST_AUTO_TEST_SUITE_END() } From 96ce95aa3a21f285ee32d30d8b2eed8017a2bdd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 9 Feb 2024 21:30:46 +0100 Subject: [PATCH 139/189] Do not include code deposit gas in the total visible in test expectations --- test/libsolidity/SemanticTest.cpp | 13 +++++---- ...2_in_function_inherited_in_v1_contract.sol | 9 ++++-- ...ode_v2_in_modifier_used_in_v1_contract.sol | 3 +- .../arithmetics/check_var_init.sol | 3 +- .../array/constant_var_as_array_length.sol | 9 ++++-- ...nup_during_multi_element_per_slot_copy.sol | 9 ++++-- .../array/fixed_arrays_as_return_type.sol | 9 ++++-- .../array/fixed_arrays_in_constructors.sol | 9 ++++-- .../array/function_array_cross_calls.sol | 9 ++++-- .../semanticTests/array/reusing_memory.sol | 9 ++++-- .../byte_array_to_storage_cleanup.sol | 9 ++++-- .../constructor/arrays_in_constructors.sol | 9 ++++-- .../bytes_in_constructors_packer.sol | 9 ++++-- .../bytes_in_constructors_unpacker.sol | 9 ++++-- .../constructor_arguments_external.sol | 9 ++++-- .../constructor_function_complex.sol | 3 +- .../constructor_static_array_argument.sol | 9 ++++-- .../constructor/no_callvalue_check.sol | 9 ++++-- .../constructor_inheritance_init_order.sol | 3 +- .../constructor_inheritance_init_order_2.sol | 9 ++++-- .../semanticTests/constructor_with_params.sol | 6 ++-- ...ructor_with_params_diamond_inheritance.sol | 9 ++++-- .../constructor_with_params_inheritance.sol | 9 ++++-- .../events/event_emit_from_other_contract.sol | 9 ++++-- .../externalContracts/FixedFeeRegistrar.sol | 9 ++++-- .../externalContracts/base64.sol | 9 ++++-- .../externalContracts/deposit_contract.sol | 9 ++++-- .../externalContracts/prbmath_signed.sol | 9 ++++-- .../externalContracts/prbmath_unsigned.sol | 9 ++++-- .../externalContracts/ramanujan_pi.sol | 9 ++++-- .../externalContracts/strings.sol | 9 ++++-- .../freeFunctions/new_operator.sol | 3 +- .../creation_function_call_no_args.sol | 3 +- .../creation_function_call_with_args.sol | 9 ++++-- .../creation_function_call_with_salt.sol | 9 ++++-- .../external_call_to_nonexisting.sol | 9 ++++-- ...ernal_call_to_nonexisting_debugstrings.sol | 9 ++++-- .../functionCall/failed_create.sol | 9 ++++-- .../functionCall/gas_and_value_basic.sol | 9 ++++-- .../gas_and_value_brace_syntax.sol | 9 ++++-- .../functionCall/send_zero_ether.sol | 3 +- .../functionTypes/store_function.sol | 6 ++-- .../immutable/multi_creation.sol | 9 ++++-- .../semanticTests/immutable/use_scratch.sol | 9 ++++-- .../address_overload_resolution.sol | 6 ++-- ...d_function_calldata_calldata_interface.sol | 3 +- ...ted_function_calldata_memory_interface.sol | 6 ++-- .../inheritance/member_notation_ctor.sol | 6 ++-- .../inheritance/value_for_constructor.sol | 9 ++++-- .../balance_other_contract.sol | 9 ++++-- .../operator_making_pure_external_call.sol | 12 +++++--- .../operator_making_view_external_call.sol | 12 +++++--- .../salted_create/prediction_example.sol | 3 +- .../salted_create/salted_create.sol | 9 ++++-- .../salted_create_with_value.sol | 9 ++++-- .../semanticTests/smoke/alignment.sol | 3 +- .../semanticTests/smoke/constructor.sol | 9 ++++-- .../semanticTests/state/blockhash_basic.sol | 9 ++++-- .../userDefinedValueType/erc20.sol | 9 ++++-- .../using/using_global_invisible.sol | 3 +- .../semanticTests/various/address_code.sol | 9 ++++-- .../various/code_access_content.sol | 6 ++-- .../various/code_access_create.sol | 3 +- .../various/code_access_runtime.sol | 3 +- .../semanticTests/various/code_length.sol | 3 +- .../semanticTests/various/create_calldata.sol | 9 ++++-- .../semanticTests/various/erc20.sol | 9 ++++-- .../various/external_types_in_calls.sol | 3 +- .../various/many_subassemblies.sol | 9 ++++-- .../various/negative_stack_height.sol | 6 ++-- .../various/selfdestruct_pre_cancun.sol | 9 ++++-- ...ruct_pre_cancun_multiple_beneficiaries.sol | 21 +++++++++----- .../selfdestruct_pre_cancun_redeploy.sol | 18 ++++++++---- .../semanticTests/various/senders_balance.sol | 9 ++++-- .../various/staticcall_for_view_and_pure.sol | 21 +++++++++----- .../semanticTests/various/value_complex.sol | 9 ++++-- .../semanticTests/various/value_insane.sol | 9 ++++-- test/libsolidity/util/SoltestTypes.h | 6 ++-- test/libsolidity/util/TestFileParser.cpp | 6 ++-- test/libsolidity/util/TestFileParserTests.cpp | 29 +++++++------------ test/libsolidity/util/TestFunctionCall.cpp | 12 ++++---- test/libsolidity/util/TestFunctionCall.h | 6 ++-- .../util/TestFunctionCallTests.cpp | 4 +-- 83 files changed, 444 insertions(+), 244 deletions(-) diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index 676c39eaf979..381ec5bda54e 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -570,7 +570,7 @@ bool SemanticTest::checkGasCostExpectation(TestFunctionCall& io_test, bool _comp (m_optimiserSettings == OptimiserSettings::full() ? "Optimized" : ""); soltestAssert( - io_test.call().expectations.gasUsed.count(setting) == + io_test.call().expectations.gasUsedExcludingCode.count(setting) == io_test.call().expectations.gasUsedForCodeDeposit.count(setting) ); @@ -584,19 +584,22 @@ bool SemanticTest::checkGasCostExpectation(TestFunctionCall& io_test, bool _comp !m_enforceGasCost || m_gasUsed < m_enforceGasCostMinValue || m_gasUsed >= InitialGas || - (setting == "ir" && io_test.call().expectations.gasUsed.count(setting) == 0) || + (setting == "ir" && io_test.call().expectations.gasUsedExcludingCode.count(setting) == 0) || io_test.call().kind == FunctionCall::Kind::Builtin ) return true; solAssert(!m_runWithABIEncoderV1Only, ""); - io_test.setGasCost(setting, m_gasUsed); + // 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. + solUnimplementedAssert(m_gasUsed >= m_gasUsedForCodeDeposit); + io_test.setGasCostExcludingCode(setting, m_gasUsed - m_gasUsedForCodeDeposit); io_test.setCodeDepositGasCost(setting, m_gasUsedForCodeDeposit); return - io_test.call().expectations.gasUsed.count(setting) > 0 && - m_gasUsed == io_test.call().expectations.gasUsed.at(setting) && + io_test.call().expectations.gasUsedExcludingCode.count(setting) > 0 && + m_gasUsed - m_gasUsedForCodeDeposit == io_test.call().expectations.gasUsedExcludingCode.at(setting) && m_gasUsedForCodeDeposit == io_test.call().expectations.gasUsedForCodeDeposit.at(setting); } diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol index 55f7991def80..c41905e2a491 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol @@ -30,6 +30,9 @@ contract C is B { } // ---- // test() -> 77 -// gas irOptimized: 110348 -// gas legacy: 151866 -// gas legacyOptimized: 110373 +// gas irOptimized: 55148 +// gas irOptimized code: 55200 +// gas legacy: 57266 +// gas legacy code: 94600 +// gas legacyOptimized: 55173 +// gas legacyOptimized code: 55200 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 7de26301205c..7a2395549b5f 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 @@ -39,4 +39,5 @@ contract C is B { // ---- // test() -> 5, 10 // gas irOptimized: 87337 -// gas legacy: 102651 +// gas legacy: 66251 +// gas legacy code: 36400 diff --git a/test/libsolidity/semanticTests/arithmetics/check_var_init.sol b/test/libsolidity/semanticTests/arithmetics/check_var_init.sol index fc5418cb0ed7..9061cd284de9 100644 --- a/test/libsolidity/semanticTests/arithmetics/check_var_init.sol +++ b/test/libsolidity/semanticTests/arithmetics/check_var_init.sol @@ -16,4 +16,5 @@ contract D { // ---- // f() -> FAILURE, hex"4e487b71", 0x11 // g(), 100 wei -> 1 -// gas legacy: 100380 +// gas legacy: 76780 +// gas legacy code: 23600 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 45861babaaf3..19acb7cba14b 100644 --- a/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol +++ b/test/libsolidity/semanticTests/array/constant_var_as_array_length.sol @@ -8,9 +8,12 @@ contract C { } // ---- // constructor(): 1, 2, 3 -> -// gas irOptimized: 139616 -// gas legacy: 180517 -// gas legacyOptimized: 150462 +// gas irOptimized: 124816 +// gas irOptimized code: 14800 +// gas legacy: 134317 +// gas legacy code: 46200 +// gas legacyOptimized: 127062 +// gas legacyOptimized code: 23400 // a(uint256): 0 -> 1 // a(uint256): 1 -> 2 // a(uint256): 2 -> 3 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 8be83289266e..36b94d7c3974 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,7 +16,10 @@ contract C { } // ---- // constructor() -// gas irOptimized: 226321 -// gas legacy: 215753 -// gas legacyOptimized: 181756 +// gas irOptimized: 89121 +// gas irOptimized code: 137200 +// gas legacy: 89553 +// gas legacy code: 126200 +// gas legacyOptimized: 83556 +// gas legacyOptimized code: 98200 // f() -> 0 diff --git a/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol b/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol index dd90985735b3..0fb513543bea 100644 --- a/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol +++ b/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol @@ -18,6 +18,9 @@ contract B { } // ---- // f() -> 2, 3, 4, 5, 6, 1000, 1001, 1002, 1003, 1004 -// gas irOptimized: 114404 -// gas legacy: 230001 -// gas legacyOptimized: 130637 +// gas irOptimized: 59204 +// gas irOptimized code: 55200 +// gas legacy: 68001 +// gas legacy code: 162000 +// gas legacyOptimized: 60037 +// gas legacyOptimized code: 70600 diff --git a/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol b/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol index c7b2dc77a34b..0eb0f1e90e7d 100644 --- a/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol +++ b/test/libsolidity/semanticTests/array/fixed_arrays_in_constructors.sol @@ -9,8 +9,11 @@ contract Creator { } // ---- // constructor(): 1, 2, 3, 4 -> -// gas irOptimized: 126327 -// gas legacy: 174186 -// gas legacyOptimized: 128709 +// gas irOptimized: 103927 +// gas irOptimized code: 22400 +// gas legacy: 115186 +// gas legacy code: 59000 +// gas legacyOptimized: 104909 +// gas legacyOptimized code: 23800 // r() -> 4 // ch() -> 3 diff --git a/test/libsolidity/semanticTests/array/function_array_cross_calls.sol b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol index 773e904cc095..e35e24bbf12a 100644 --- a/test/libsolidity/semanticTests/array/function_array_cross_calls.sol +++ b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol @@ -42,6 +42,9 @@ contract C { } // ---- // test() -> 5, 6, 7 -// gas irOptimized: 255728 -// gas legacy: 440376 -// gas legacyOptimized: 278651 +// gas irOptimized: 86128 +// gas irOptimized code: 169600 +// gas legacy: 97576 +// gas legacy code: 342800 +// gas legacyOptimized: 87851 +// gas legacyOptimized code: 190800 diff --git a/test/libsolidity/semanticTests/array/reusing_memory.sol b/test/libsolidity/semanticTests/array/reusing_memory.sol index 8f6afe30ba2c..a7608f6e0c81 100644 --- a/test/libsolidity/semanticTests/array/reusing_memory.sol +++ b/test/libsolidity/semanticTests/array/reusing_memory.sol @@ -24,6 +24,9 @@ contract Main { } // ---- // f(uint256): 0x34 -> 0x46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c1 -// gas irOptimized: 111924 -// gas legacy: 125154 -// gas legacyOptimized: 113012 +// gas irOptimized: 99524 +// gas irOptimized code: 12400 +// gas legacy: 101554 +// gas legacy code: 23600 +// gas legacyOptimized: 99612 +// gas legacyOptimized code: 13400 diff --git a/test/libsolidity/semanticTests/byte_array_to_storage_cleanup.sol b/test/libsolidity/semanticTests/byte_array_to_storage_cleanup.sol index 877e2a1bce03..14130926e7fb 100644 --- a/test/libsolidity/semanticTests/byte_array_to_storage_cleanup.sol +++ b/test/libsolidity/semanticTests/byte_array_to_storage_cleanup.sol @@ -28,9 +28,12 @@ contract C { // compileViaYul: also // ---- // constructor() -> -// gas irOptimized: 443402 -// gas legacy: 711295 -// gas legacyOptimized: 482378 +// gas irOptimized: 82402 +// gas irOptimized code: 361000 +// gas legacy: 101895 +// gas legacy code: 609400 +// gas legacyOptimized: 85378 +// gas legacyOptimized code: 397000 // h() -> 0x20, 0x40, 0x00, 0 // ~ emit ev(uint256[],uint256): 0x40, 0x21, 0x02, 0x00, 0x00 // g() -> 0x20, 0x40, 0, 0x00 diff --git a/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol index 19d79b6c4fcf..3e60ba451c8c 100644 --- a/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol +++ b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol @@ -24,6 +24,9 @@ contract Creator { } // ---- // f(uint256,address[]): 7, 0x40, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 7, 8 -// gas irOptimized: 424526 -// gas legacy: 581426 -// gas legacyOptimized: 444599 +// gas irOptimized: 328126 +// gas irOptimized code: 96400 +// gas legacy: 336626 +// gas legacy code: 244800 +// gas legacyOptimized: 329599 +// gas legacyOptimized code: 115000 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol index cfa74f016f6d..7885d3ceecf0 100644 --- a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol @@ -24,6 +24,9 @@ contract Creator { } // ---- // f(uint256,bytes): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> 7, "h" -// gas irOptimized: 275102 -// gas legacy: 418433 -// gas legacyOptimized: 291960 +// gas irOptimized: 169902 +// gas irOptimized code: 105200 +// gas legacy: 173433 +// gas legacy code: 245000 +// gas legacyOptimized: 170360 +// gas legacyOptimized code: 121600 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol index 39da45e8a57e..1d6b01127c5a 100644 --- a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol @@ -8,8 +8,11 @@ contract Test { } // ---- // constructor(): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> -// gas irOptimized: 269225 -// gas legacy: 310820 -// gas legacyOptimized: 258604 +// gas irOptimized: 182025 +// gas irOptimized code: 87200 +// gas legacy: 196220 +// gas legacy code: 114600 +// gas legacyOptimized: 182604 +// gas legacyOptimized code: 76000 // m_x() -> 7 // m_s() -> 0x20, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" diff --git a/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol b/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol index 714ae3e5548c..8fa725dad569 100644 --- a/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol +++ b/test/libsolidity/semanticTests/constructor/constructor_arguments_external.sol @@ -17,8 +17,11 @@ contract Main { } // ---- // constructor(): "abc", true -// gas irOptimized: 104374 -// gas legacy: 143300 -// gas legacyOptimized: 102933 +// gas irOptimized: 80174 +// gas irOptimized code: 24200 +// gas legacy: 85100 +// gas legacy code: 58200 +// gas legacyOptimized: 80133 +// gas legacyOptimized code: 22800 // getFlag() -> true // getName() -> "abc" diff --git a/test/libsolidity/semanticTests/constructor/constructor_function_complex.sol b/test/libsolidity/semanticTests/constructor/constructor_function_complex.sol index 4117b84b64f9..33650aff6fb6 100644 --- a/test/libsolidity/semanticTests/constructor/constructor_function_complex.sol +++ b/test/libsolidity/semanticTests/constructor/constructor_function_complex.sol @@ -17,4 +17,5 @@ contract C { } // ---- // f() -> 16 -// gas legacy: 102082 +// gas legacy: 78482 +// gas legacy code: 23600 diff --git a/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol b/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol index a61192742d04..e6ce445ada39 100644 --- a/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol +++ b/test/libsolidity/semanticTests/constructor/constructor_static_array_argument.sol @@ -9,9 +9,12 @@ contract C { } // ---- // constructor(): 1, 2, 3, 4 -> -// gas irOptimized: 170975 -// gas legacy: 218378 -// gas legacyOptimized: 176211 +// gas irOptimized: 147975 +// gas irOptimized code: 23000 +// gas legacy: 157978 +// gas legacy code: 60400 +// gas legacyOptimized: 150011 +// gas legacyOptimized code: 26200 // a() -> 1 // b(uint256): 0 -> 2 // b(uint256): 1 -> 3 diff --git a/test/libsolidity/semanticTests/constructor/no_callvalue_check.sol b/test/libsolidity/semanticTests/constructor/no_callvalue_check.sol index eec01005ebd2..b1dc80a408d8 100644 --- a/test/libsolidity/semanticTests/constructor/no_callvalue_check.sol +++ b/test/libsolidity/semanticTests/constructor/no_callvalue_check.sol @@ -17,6 +17,9 @@ contract C { } // ---- // f(), 2000 ether -> true -// gas irOptimized: 119441 -// gas legacy: 122621 -// gas legacyOptimized: 122490 +// gas irOptimized: 117641 +// gas irOptimized code: 1800 +// gas legacy: 117821 +// gas legacy code: 4800 +// gas legacyOptimized: 117690 +// gas legacyOptimized code: 4800 diff --git a/test/libsolidity/semanticTests/constructor_inheritance_init_order.sol b/test/libsolidity/semanticTests/constructor_inheritance_init_order.sol index ece0cc31a651..ef53142d458a 100644 --- a/test/libsolidity/semanticTests/constructor_inheritance_init_order.sol +++ b/test/libsolidity/semanticTests/constructor_inheritance_init_order.sol @@ -14,5 +14,6 @@ contract B is A { // compileViaYul: true // ---- // constructor() -> -// gas irOptimized: 119636 +// gas irOptimized: 99436 +// gas irOptimized code: 20200 // y() -> 42 diff --git a/test/libsolidity/semanticTests/constructor_inheritance_init_order_2.sol b/test/libsolidity/semanticTests/constructor_inheritance_init_order_2.sol index 654c9af56df1..e19ad39cfad7 100644 --- a/test/libsolidity/semanticTests/constructor_inheritance_init_order_2.sol +++ b/test/libsolidity/semanticTests/constructor_inheritance_init_order_2.sol @@ -9,7 +9,10 @@ contract B is A { } // ---- // constructor() -> -// gas irOptimized: 119636 -// gas legacy: 133574 -// gas legacyOptimized: 115337 +// gas irOptimized: 99436 +// gas irOptimized code: 20200 +// gas legacy: 100974 +// gas legacy code: 32600 +// gas legacyOptimized: 99137 +// gas legacyOptimized code: 16200 // y() -> 42 diff --git a/test/libsolidity/semanticTests/constructor_with_params.sol b/test/libsolidity/semanticTests/constructor_with_params.sol index e427f6d9ab7b..1c5a170202f3 100644 --- a/test/libsolidity/semanticTests/constructor_with_params.sol +++ b/test/libsolidity/semanticTests/constructor_with_params.sol @@ -9,7 +9,9 @@ contract C { } // ---- // constructor(): 2, 0 -> -// gas irOptimized: 101370 -// gas legacy: 115614 +// gas irOptimized: 81170 +// gas irOptimized code: 20200 +// gas legacy: 83614 +// gas legacy code: 32000 // i() -> 2 // k() -> 0 diff --git a/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol b/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol index 65070814476d..29f401a77322 100644 --- a/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol +++ b/test/libsolidity/semanticTests/constructor_with_params_diamond_inheritance.sol @@ -21,9 +21,12 @@ contract D is B, C { } // ---- // constructor(): 2, 0 -> -// gas irOptimized: 151950 -// gas legacy: 168623 -// gas legacyOptimized: 144521 +// gas irOptimized: 124350 +// gas irOptimized code: 27600 +// gas legacy: 128223 +// gas legacy code: 40400 +// gas legacyOptimized: 123921 +// gas legacyOptimized code: 20600 // i() -> 2 // j() -> 2 // k() -> 1 diff --git a/test/libsolidity/semanticTests/constructor_with_params_inheritance.sol b/test/libsolidity/semanticTests/constructor_with_params_inheritance.sol index 8cc7c69f689b..b0373b74f193 100644 --- a/test/libsolidity/semanticTests/constructor_with_params_inheritance.sol +++ b/test/libsolidity/semanticTests/constructor_with_params_inheritance.sol @@ -12,8 +12,11 @@ contract D is C { } // ---- // constructor(): 2, 0 -> -// gas irOptimized: 121781 -// gas legacy: 137193 -// gas legacyOptimized: 118504 +// gas irOptimized: 101581 +// gas irOptimized code: 20200 +// gas legacy: 105193 +// gas legacy code: 32000 +// gas legacyOptimized: 101504 +// gas legacyOptimized code: 17000 // i() -> 2 // k() -> 1 diff --git a/test/libsolidity/semanticTests/events/event_emit_from_other_contract.sol b/test/libsolidity/semanticTests/events/event_emit_from_other_contract.sol index 0c56cbb671fc..6a01d0019295 100644 --- a/test/libsolidity/semanticTests/events/event_emit_from_other_contract.sol +++ b/test/libsolidity/semanticTests/events/event_emit_from_other_contract.sol @@ -15,8 +15,11 @@ contract C { } // ---- // constructor() -> -// gas irOptimized: 165370 -// gas legacy: 244776 -// gas legacyOptimized: 171587 +// gas irOptimized: 113970 +// gas irOptimized code: 51400 +// gas legacy: 119776 +// gas legacy code: 125000 +// gas legacyOptimized: 114187 +// gas legacyOptimized code: 57400 // deposit(bytes32), 18 wei: 0x1234 -> // ~ emit Deposit(address,bytes32,uint256) from 0x137aa4dfc0911524504fcd4d98501f179bc13b4a: #0xc06afe3a8444fc0004668591e8306bfb9968e79e, #0x1234, 0x00 diff --git a/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol b/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol index 011fecbd3422..d308d4597e53 100644 --- a/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol +++ b/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol @@ -74,9 +74,12 @@ contract FixedFeeRegistrar is Registrar { } // ---- // constructor() -// gas irOptimized: 384606 -// gas legacy: 913417 -// gas legacyOptimized: 476924 +// gas irOptimized: 78006 +// gas irOptimized code: 306600 +// gas legacy: 115817 +// gas legacy code: 797600 +// gas legacyOptimized: 84924 +// gas legacyOptimized code: 392000 // reserve(string), 69 ether: 0x20, 3, "abc" -> // ~ emit Changed(string): #0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 // gas irOptimized: 45967 diff --git a/test/libsolidity/semanticTests/externalContracts/base64.sol b/test/libsolidity/semanticTests/externalContracts/base64.sol index ee2745d1acb9..b712092ed022 100644 --- a/test/libsolidity/semanticTests/externalContracts/base64.sol +++ b/test/libsolidity/semanticTests/externalContracts/base64.sol @@ -33,9 +33,12 @@ contract test { // EVMVersion: >=constantinople // ---- // constructor() -// gas irOptimized: 405828 -// gas legacy: 735050 -// gas legacyOptimized: 522718 +// gas irOptimized: 79428 +// gas irOptimized code: 326400 +// gas legacy: 102450 +// gas legacy code: 632600 +// gas legacyOptimized: 88318 +// gas legacyOptimized code: 434400 // encode_inline_asm(bytes): 0x20, 0 -> 0x20, 0 // encode_inline_asm(bytes): 0x20, 1, "f" -> 0x20, 4, "Zg==" // encode_inline_asm(bytes): 0x20, 2, "fo" -> 0x20, 4, "Zm8=" diff --git a/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol b/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol index caa7fc39c867..0ef87edd4f08 100644 --- a/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol +++ b/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol @@ -176,9 +176,12 @@ contract DepositContract is IDepositContract, ERC165 { } // ---- // constructor() -// gas irOptimized: 1386886 -// gas legacy: 2368733 -// gas legacyOptimized: 1740004 +// gas irOptimized: 815686 +// gas irOptimized code: 571200 +// gas legacy: 925933 +// gas legacy code: 1442800 +// gas legacyOptimized: 854404 +// gas legacyOptimized code: 885600 // supportsInterface(bytes4): 0x0 -> 0 // supportsInterface(bytes4): 0xffffffff00000000000000000000000000000000000000000000000000000000 -> false # defined to be false by ERC-165 # // supportsInterface(bytes4): 0x01ffc9a700000000000000000000000000000000000000000000000000000000 -> true # ERC-165 id # diff --git a/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol b/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol index ba7a208fd71b..ddd63971836c 100644 --- a/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol +++ b/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol @@ -48,9 +48,12 @@ contract test { } // ---- // constructor() -// gas irOptimized: 1841736 -// gas legacy: 2414087 -// gas legacyOptimized: 1847612 +// gas irOptimized: 177336 +// gas irOptimized code: 1664400 +// gas legacy: 209687 +// gas legacy code: 2204400 +// gas legacyOptimized: 178012 +// gas legacyOptimized code: 1669600 // div(int256,int256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 // gas irOptimized: 22137 // gas legacy: 22767 diff --git a/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol index 90b283db4613..deacfd05641b 100644 --- a/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol +++ b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol @@ -48,9 +48,12 @@ contract test { } // ---- // constructor() -// gas irOptimized: 1716323 -// gas legacy: 2193546 -// gas legacyOptimized: 1725057 +// gas irOptimized: 168523 +// gas irOptimized code: 1547800 +// gas legacy: 195146 +// gas legacy code: 1998400 +// gas legacyOptimized: 168857 +// gas legacyOptimized code: 1556200 // div(uint256,uint256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 // gas irOptimized: 22004 // gas legacy: 22497 diff --git a/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol b/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol index 4442b6d479d3..cdaec8797eb7 100644 --- a/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol +++ b/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol @@ -33,9 +33,12 @@ contract test { } // ---- // constructor() -// gas irOptimized: 407503 -// gas legacy: 615086 -// gas legacyOptimized: 451867 +// gas irOptimized: 79503 +// gas irOptimized code: 328000 +// gas legacy: 92086 +// gas legacy code: 523000 +// gas legacyOptimized: 82667 +// gas legacyOptimized code: 369200 // prb_pi() -> 3141592656369545286 // gas irOptimized: 57478 // gas legacy: 100947 diff --git a/test/libsolidity/semanticTests/externalContracts/strings.sol b/test/libsolidity/semanticTests/externalContracts/strings.sol index 3f88fc39c65e..a4b0f9fd353d 100644 --- a/test/libsolidity/semanticTests/externalContracts/strings.sol +++ b/test/libsolidity/semanticTests/externalContracts/strings.sol @@ -49,9 +49,12 @@ contract test { } // ---- // constructor() -// gas irOptimized: 630220 -// gas legacy: 1061953 -// gas legacyOptimized: 718933 +// gas irOptimized: 96420 +// gas irOptimized code: 533800 +// gas legacy: 126553 +// gas legacy code: 935400 +// gas legacyOptimized: 102933 +// gas legacyOptimized code: 616000 // toSlice(string): 0x20, 11, "hello world" -> 11, 0xa0 // gas irOptimized: 22660 // gas legacy: 23190 diff --git a/test/libsolidity/semanticTests/freeFunctions/new_operator.sol b/test/libsolidity/semanticTests/freeFunctions/new_operator.sol index dd44a1b5950c..d621629c3b49 100644 --- a/test/libsolidity/semanticTests/freeFunctions/new_operator.sol +++ b/test/libsolidity/semanticTests/freeFunctions/new_operator.sol @@ -13,4 +13,5 @@ contract D { } // ---- // f() -> 2 -// gas legacy: 100211 +// gas legacy: 76611 +// gas legacy code: 23600 diff --git a/test/libsolidity/semanticTests/functionCall/creation_function_call_no_args.sol b/test/libsolidity/semanticTests/functionCall/creation_function_call_no_args.sol index b6a33fb7e835..6044065958a4 100644 --- a/test/libsolidity/semanticTests/functionCall/creation_function_call_no_args.sol +++ b/test/libsolidity/semanticTests/functionCall/creation_function_call_no_args.sol @@ -11,4 +11,5 @@ contract D { } // ---- // f() -> 2 -// gas legacy: 100185 +// gas legacy: 76585 +// gas legacy code: 23600 diff --git a/test/libsolidity/semanticTests/functionCall/creation_function_call_with_args.sol b/test/libsolidity/semanticTests/functionCall/creation_function_call_with_args.sol index 34aac74efa04..1133dc2be300 100644 --- a/test/libsolidity/semanticTests/functionCall/creation_function_call_with_args.sol +++ b/test/libsolidity/semanticTests/functionCall/creation_function_call_with_args.sol @@ -15,7 +15,10 @@ contract D { } // ---- // constructor(): 2 -> -// gas irOptimized: 192663 -// gas legacy: 241170 -// gas legacyOptimized: 192897 +// gas irOptimized: 138863 +// gas irOptimized code: 53800 +// gas legacy: 145570 +// gas legacy code: 95600 +// gas legacyOptimized: 138297 +// gas legacyOptimized code: 54600 // f() -> 2 diff --git a/test/libsolidity/semanticTests/functionCall/creation_function_call_with_salt.sol b/test/libsolidity/semanticTests/functionCall/creation_function_call_with_salt.sol index cb26a6e26adf..ba90a8ecd5a6 100644 --- a/test/libsolidity/semanticTests/functionCall/creation_function_call_with_salt.sol +++ b/test/libsolidity/semanticTests/functionCall/creation_function_call_with_salt.sol @@ -17,7 +17,10 @@ contract D { // EVMVersion: >=constantinople // ---- // constructor(): 2 -> -// gas irOptimized: 192826 -// gas legacy: 241536 -// gas legacyOptimized: 193129 +// gas irOptimized: 139026 +// gas irOptimized code: 53800 +// gas legacy: 145936 +// gas legacy code: 95600 +// gas legacyOptimized: 138529 +// gas legacyOptimized code: 54600 // f() -> 2 diff --git a/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting.sol b/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting.sol index a1c74cf89838..9d521f7777f9 100644 --- a/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting.sol +++ b/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting.sol @@ -22,9 +22,12 @@ contract C { } // ---- // constructor(), 1 ether -> -// gas irOptimized: 262355 -// gas legacy: 441442 -// gas legacyOptimized: 292862 +// gas irOptimized: 89555 +// gas irOptimized code: 172800 +// gas legacy: 103042 +// gas legacy code: 338400 +// gas legacyOptimized: 91862 +// gas legacyOptimized code: 201000 // f(uint256): 0 -> FAILURE // f(uint256): 1 -> FAILURE // f(uint256): 2 -> FAILURE diff --git a/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting_debugstrings.sol b/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting_debugstrings.sol index 96f097ce7f4a..9baacd812773 100644 --- a/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting_debugstrings.sol +++ b/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting_debugstrings.sol @@ -25,9 +25,12 @@ contract C { // revertStrings: debug // ---- // constructor(), 1 ether -> -// gas irOptimized: 390464 -// gas legacy: 809985 -// gas legacyOptimized: 498331 +// gas irOptimized: 99264 +// gas irOptimized code: 291200 +// gas legacy: 123585 +// gas legacy code: 686400 +// gas legacyOptimized: 107331 +// gas legacyOptimized code: 391000 // f(uint256): 0 -> FAILURE, hex"08c379a0", 0x20, 37, "Target contract does not contain", " code" // f(uint256): 1 -> FAILURE, hex"08c379a0", 0x20, 37, "Target contract does not contain", " code" // f(uint256): 2 -> FAILURE, hex"08c379a0", 0x20, 37, "Target contract does not contain", " code" diff --git a/test/libsolidity/semanticTests/functionCall/failed_create.sol b/test/libsolidity/semanticTests/functionCall/failed_create.sol index 091e5a77a62a..b6c077753a51 100644 --- a/test/libsolidity/semanticTests/functionCall/failed_create.sol +++ b/test/libsolidity/semanticTests/functionCall/failed_create.sol @@ -17,9 +17,12 @@ contract C { // EVMVersion: >=byzantium // ---- // constructor(), 20 wei -// gas irOptimized: 166148 -// gas legacy: 285547 -// gas legacyOptimized: 168515 +// gas irOptimized: 61548 +// gas irOptimized code: 104600 +// gas legacy: 70147 +// gas legacy code: 215400 +// gas legacyOptimized: 61715 +// gas legacyOptimized code: 106800 // f(uint256): 20 -> 0x137aa4dfc0911524504fcd4d98501f179bc13b4a // x() -> 1 // f(uint256): 20 -> FAILURE diff --git a/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol b/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol index 8396cb12db8d..806c8eb41b9d 100644 --- a/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol +++ b/test/libsolidity/semanticTests/functionCall/gas_and_value_basic.sol @@ -38,9 +38,12 @@ contract test { } // ---- // constructor(), 20 wei -> -// gas irOptimized: 252626 -// gas legacy: 391568 -// gas legacyOptimized: 268069 +// gas irOptimized: 120226 +// gas irOptimized code: 132400 +// gas legacy: 130568 +// gas legacy code: 261000 +// gas legacyOptimized: 121069 +// gas legacyOptimized code: 147000 // sendAmount(uint256): 5 -> 5 // outOfGas() -> FAILURE # call to helper should not succeed but amount should be transferred anyway # // checkState() -> false, 15 diff --git a/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol b/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol index 9d9c3f603f78..c186fc40b22c 100644 --- a/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol +++ b/test/libsolidity/semanticTests/functionCall/gas_and_value_brace_syntax.sol @@ -37,9 +37,12 @@ contract test { } // ---- // constructor(), 20 wei -> -// gas irOptimized: 252626 -// gas legacy: 391568 -// gas legacyOptimized: 268069 +// gas irOptimized: 120226 +// gas irOptimized code: 132400 +// gas legacy: 130568 +// gas legacy code: 261000 +// gas legacyOptimized: 121069 +// gas legacyOptimized code: 147000 // sendAmount(uint256): 5 -> 5 // outOfGas() -> FAILURE # call to helper should not succeed but amount should be transferred anyway # // checkState() -> false, 15 diff --git a/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol b/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol index a737de5cd4e7..94a721323244 100644 --- a/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol +++ b/test/libsolidity/semanticTests/functionCall/send_zero_ether.sol @@ -16,6 +16,7 @@ contract Main { // ---- // constructor(), 20 wei -> // gas irOptimized: 100264 -// gas legacy: 110555 +// gas legacy: 57555 +// gas legacy code: 53000 // gas legacyOptimized: 100361 // s() -> true diff --git a/test/libsolidity/semanticTests/functionTypes/store_function.sol b/test/libsolidity/semanticTests/functionTypes/store_function.sol index 132a6c95659a..5397d0b70187 100644 --- a/test/libsolidity/semanticTests/functionTypes/store_function.sol +++ b/test/libsolidity/semanticTests/functionTypes/store_function.sol @@ -26,5 +26,7 @@ contract C { // ---- // t() -> 9 // gas irOptimized: 99064 -// gas legacy: 149095 -// gas legacyOptimized: 106188 +// gas legacy: 79495 +// gas legacy code: 69600 +// gas legacyOptimized: 77588 +// gas legacyOptimized code: 28600 diff --git a/test/libsolidity/semanticTests/immutable/multi_creation.sol b/test/libsolidity/semanticTests/immutable/multi_creation.sol index 145c32add6e9..c8806c50d060 100644 --- a/test/libsolidity/semanticTests/immutable/multi_creation.sol +++ b/test/libsolidity/semanticTests/immutable/multi_creation.sol @@ -27,8 +27,11 @@ contract C { } // ---- // f() -> 3, 7, 5 -// gas irOptimized: 124021 -// gas legacy: 148528 -// gas legacyOptimized: 123971 +// gas irOptimized: 86821 +// gas irOptimized code: 37200 +// gas legacy: 87728 +// gas legacy code: 60800 +// gas legacyOptimized: 86771 +// gas legacyOptimized code: 37200 // x() -> 7 // y() -> 5 diff --git a/test/libsolidity/semanticTests/immutable/use_scratch.sol b/test/libsolidity/semanticTests/immutable/use_scratch.sol index a7c8ea6a502a..dc07086c29c3 100644 --- a/test/libsolidity/semanticTests/immutable/use_scratch.sol +++ b/test/libsolidity/semanticTests/immutable/use_scratch.sol @@ -15,8 +15,11 @@ contract C { } // ---- // constructor(): 3 -> -// gas irOptimized: 123526 -// gas legacy: 197645 -// gas legacyOptimized: 137658 +// gas irOptimized: 81126 +// gas irOptimized code: 42400 +// gas legacy: 88245 +// gas legacy code: 109400 +// gas legacyOptimized: 81858 +// gas legacyOptimized code: 55800 // f() -> 84, 23 // m(uint256): 3 -> 7 diff --git a/test/libsolidity/semanticTests/inheritance/address_overload_resolution.sol b/test/libsolidity/semanticTests/inheritance/address_overload_resolution.sol index c65d1abec558..0865ed2876ad 100644 --- a/test/libsolidity/semanticTests/inheritance/address_overload_resolution.sol +++ b/test/libsolidity/semanticTests/inheritance/address_overload_resolution.sol @@ -21,7 +21,9 @@ contract D { // ---- // f() -> 1 // gas irOptimized: 77051 -// gas legacy: 112280 +// gas legacy: 54480 +// gas legacy code: 57800 // g() -> 5 // gas irOptimized: 77106 -// gas legacy: 112816 +// gas legacy: 55016 +// 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 3114c80f1912..0567c2ab1412 100644 --- a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_calldata_interface.sol +++ b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_calldata_interface.sol @@ -23,4 +23,5 @@ contract B { // ---- // g() -> 42 // gas irOptimized: 80813 -// gas legacy: 122471 +// gas legacy: 55871 +// 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 74bf42ec7e61..d5ff54b68a16 100644 --- a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol +++ b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol @@ -23,5 +23,7 @@ contract B { // ---- // g() -> 42 // gas irOptimized: 100282 -// gas legacy: 180440 -// gas legacyOptimized: 112596 +// gas legacy: 56840 +// gas legacy code: 123600 +// gas legacyOptimized: 54996 +// gas legacyOptimized code: 57600 diff --git a/test/libsolidity/semanticTests/inheritance/member_notation_ctor.sol b/test/libsolidity/semanticTests/inheritance/member_notation_ctor.sol index 263588abab5c..e704a297bbac 100644 --- a/test/libsolidity/semanticTests/inheritance/member_notation_ctor.sol +++ b/test/libsolidity/semanticTests/inheritance/member_notation_ctor.sol @@ -19,6 +19,8 @@ contract A { } // ---- // g(int256): -1 -> -1 -// gas legacy: 102078 +// gas legacy: 77878 +// gas legacy code: 24200 // g(int256): 10 -> 10 -// gas legacy: 101706 +// gas legacy: 77506 +// gas legacy code: 24200 diff --git a/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol b/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol index 9627ad025ddd..fdd0bafaadfe 100644 --- a/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol +++ b/test/libsolidity/semanticTests/inheritance/value_for_constructor.sol @@ -39,9 +39,12 @@ contract Main { } // ---- // constructor(), 22 wei -> -// gas irOptimized: 261864 -// gas legacy: 392786 -// gas legacyOptimized: 261593 +// gas irOptimized: 143864 +// gas irOptimized code: 118000 +// gas legacy: 156586 +// gas legacy code: 236200 +// gas legacyOptimized: 143593 +// gas legacyOptimized code: 118000 // getFlag() -> true // getName() -> "abc" // getBalances() -> 12, 10 diff --git a/test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol b/test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol index 1a5c9b19b416..0ad55b5ddff1 100644 --- a/test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol +++ b/test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol @@ -16,9 +16,12 @@ contract ClientReceipt { } // ---- // constructor(), 2000 wei -> -// gas irOptimized: 169907 -// gas legacy: 230018 -// gas legacyOptimized: 173867 +// gas irOptimized: 114107 +// gas irOptimized code: 55800 +// gas legacy: 118618 +// gas legacy code: 111400 +// gas legacyOptimized: 114067 +// gas legacyOptimized code: 59800 // balance -> 1500 // gas irOptimized: 191881 // gas legacy: 235167 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 ce6a0f48db09..a621b4c12ad5 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 @@ -53,9 +53,13 @@ contract C { // ---- // testMul(int32,int32): 42, 10 -> 420 // gas irOptimized: 102563 -// gas legacy: 183981 -// gas legacyOptimized: 123563 +// gas legacy: 56981 +// gas legacy code: 127000 +// gas legacyOptimized: 55163 +// gas legacyOptimized code: 68400 // testInc(int32): 42 -> 43 // gas irOptimized: 102386 -// gas legacy: 183239 -// gas legacyOptimized: 123251 +// gas legacy: 56239 +// gas legacy code: 127000 +// gas legacyOptimized: 54851 +// gas legacyOptimized code: 68400 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 f3f8ebdc63db..84f6bf08e7a6 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 @@ -59,9 +59,13 @@ contract C { // ---- // testMul(int32,int32): 42, 10 -> 420 // gas irOptimized: 102563 -// gas legacy: 183981 -// gas legacyOptimized: 123563 +// gas legacy: 56981 +// gas legacy code: 127000 +// gas legacyOptimized: 55163 +// gas legacyOptimized code: 68400 // testInc(int32): 42 -> 43 // gas irOptimized: 102386 -// gas legacy: 183239 -// gas legacyOptimized: 123251 +// gas legacy: 56239 +// gas legacy code: 127000 +// gas legacyOptimized: 54851 +// gas legacyOptimized code: 68400 diff --git a/test/libsolidity/semanticTests/salted_create/prediction_example.sol b/test/libsolidity/semanticTests/salted_create/prediction_example.sol index e7171536d6bb..6fa865440bd4 100644 --- a/test/libsolidity/semanticTests/salted_create/prediction_example.sol +++ b/test/libsolidity/semanticTests/salted_create/prediction_example.sol @@ -26,4 +26,5 @@ contract C { // compileViaYul: also // ---- // createDSalted(bytes32,uint256): 42, 64 -> -// gas legacy: 102841 +// gas legacy: 79241 +// gas legacy code: 23600 diff --git a/test/libsolidity/semanticTests/salted_create/salted_create.sol b/test/libsolidity/semanticTests/salted_create/salted_create.sol index 743ff5fd2a23..535a2813c17c 100644 --- a/test/libsolidity/semanticTests/salted_create/salted_create.sol +++ b/test/libsolidity/semanticTests/salted_create/salted_create.sol @@ -21,6 +21,9 @@ contract A { // ---- // different_salt() -> true // same_salt() -> true -// gas irOptimized: 98438895 -// gas legacy: 98439109 -// gas legacyOptimized: 98438967 +// gas irOptimized: 98438295 +// gas irOptimized code: 600 +// gas legacy: 98437509 +// gas legacy code: 1600 +// gas legacyOptimized: 98437367 +// gas legacyOptimized code: 1600 diff --git a/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol b/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol index 58b3f076034c..735731c8abf7 100644 --- a/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol +++ b/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol @@ -21,6 +21,9 @@ contract A { // EVMVersion: >=constantinople // ---- // f(), 10 ether -> 3007, 3008, 3009 -// gas irOptimized: 253005 -// gas legacy: 381063 -// gas legacyOptimized: 279658 +// gas irOptimized: 187005 +// gas irOptimized code: 66000 +// gas legacy: 190863 +// gas legacy code: 190200 +// gas legacyOptimized: 187258 +// gas legacyOptimized code: 92400 diff --git a/test/libsolidity/semanticTests/smoke/alignment.sol b/test/libsolidity/semanticTests/smoke/alignment.sol index 3e8016f988f8..296ae0161711 100644 --- a/test/libsolidity/semanticTests/smoke/alignment.sol +++ b/test/libsolidity/semanticTests/smoke/alignment.sol @@ -25,5 +25,6 @@ contract D { // stateDecimal() -> right(42) // stateBytes() -> left(0x4200ef) // internalStateDecimal() -> 0x20 -// gas legacy: 100265 +// gas legacy: 76665 +// gas legacy code: 23600 // update(bool,uint256,bytes32): false, -23, left(0x2300ef) -> false, -23, left(0x2300ef) diff --git a/test/libsolidity/semanticTests/smoke/constructor.sol b/test/libsolidity/semanticTests/smoke/constructor.sol index 55789b2f4b65..2783fe4a8c9c 100644 --- a/test/libsolidity/semanticTests/smoke/constructor.sol +++ b/test/libsolidity/semanticTests/smoke/constructor.sol @@ -12,9 +12,12 @@ contract C { } // ---- // constructor(), 2 wei: 3 -> -// gas irOptimized: 104396 -// gas legacy: 148256 -// gas legacyOptimized: 106699 +// gas irOptimized: 78996 +// gas irOptimized code: 25400 +// gas legacy: 83056 +// gas legacy code: 65200 +// gas legacyOptimized: 78899 +// gas legacyOptimized code: 27800 // state() -> 3 // balance() -> 2 // balance -> 2 diff --git a/test/libsolidity/semanticTests/state/blockhash_basic.sol b/test/libsolidity/semanticTests/state/blockhash_basic.sol index 03d6f9fe76ea..64b8d55a26b6 100644 --- a/test/libsolidity/semanticTests/state/blockhash_basic.sol +++ b/test/libsolidity/semanticTests/state/blockhash_basic.sol @@ -12,9 +12,12 @@ contract C { } // ---- // constructor() -// gas irOptimized: 108138 -// gas legacy: 152171 -// gas legacyOptimized: 106738 +// gas irOptimized: 80338 +// gas irOptimized code: 27800 +// gas legacy: 83571 +// gas legacy code: 68600 +// gas legacyOptimized: 80338 +// gas legacyOptimized code: 26400 // genesisHash() -> 0x3737373737373737373737373737373737373737373737373737373737373737 // currentHash() -> 0 // f(uint256): 0 -> 0x3737373737373737373737373737373737373737373737373737373737373737 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/erc20.sol b/test/libsolidity/semanticTests/userDefinedValueType/erc20.sol index ac8609d71324..8976cf3c52cc 100644 --- a/test/libsolidity/semanticTests/userDefinedValueType/erc20.sol +++ b/test/libsolidity/semanticTests/userDefinedValueType/erc20.sol @@ -113,9 +113,12 @@ contract ERC20 { // ---- // constructor() // ~ emit Transfer(address,address,uint256): #0x00, #0x1212121212121212121212121212120000000012, 0x14 -// gas irOptimized: 352682 -// gas legacy: 834752 -// gas legacyOptimized: 412648 +// gas irOptimized: 121082 +// gas irOptimized code: 231600 +// gas legacy: 163352 +// gas legacy code: 671400 +// gas legacyOptimized: 127448 +// gas legacyOptimized code: 285200 // totalSupply() -> 20 // gas irOptimized: 23415 // gas legacy: 23653 diff --git a/test/libsolidity/semanticTests/using/using_global_invisible.sol b/test/libsolidity/semanticTests/using/using_global_invisible.sol index 3e767a185d90..818707f6fe35 100644 --- a/test/libsolidity/semanticTests/using/using_global_invisible.sol +++ b/test/libsolidity/semanticTests/using/using_global_invisible.sol @@ -40,4 +40,5 @@ contract D { // ---- // library: "A":L // test() -> 3 -// gas legacy: 120881 +// gas legacy: 59681 +// gas legacy code: 61200 diff --git a/test/libsolidity/semanticTests/various/address_code.sol b/test/libsolidity/semanticTests/various/address_code.sol index 7e48b00ca840..6a97c0bf7ee2 100644 --- a/test/libsolidity/semanticTests/various/address_code.sol +++ b/test/libsolidity/semanticTests/various/address_code.sol @@ -14,9 +14,12 @@ contract C { } // ---- // constructor() -> -// gas irOptimized: 173273 -// gas legacy: 241796 -// gas legacyOptimized: 153670 +// gas irOptimized: 70873 +// gas irOptimized code: 102400 +// gas legacy: 82796 +// gas legacy code: 159000 +// gas legacyOptimized: 69870 +// gas legacyOptimized code: 83800 // initCode() -> 0x20, 0 // f() -> true // g() -> 0 diff --git a/test/libsolidity/semanticTests/various/code_access_content.sol b/test/libsolidity/semanticTests/various/code_access_content.sol index 92d31d621433..dc4e49b2dae0 100644 --- a/test/libsolidity/semanticTests/various/code_access_content.sol +++ b/test/libsolidity/semanticTests/various/code_access_content.sol @@ -38,6 +38,8 @@ contract C { } // ---- // testRuntime() -> true -// gas legacy: 100177 +// gas legacy: 76577 +// gas legacy code: 23600 // testCreation() -> true -// gas legacy: 100600 +// gas legacy: 77000 +// gas legacy code: 23600 diff --git a/test/libsolidity/semanticTests/various/code_access_create.sol b/test/libsolidity/semanticTests/various/code_access_create.sol index 075682129cda..d2b7f92f4e66 100644 --- a/test/libsolidity/semanticTests/various/code_access_create.sol +++ b/test/libsolidity/semanticTests/various/code_access_create.sol @@ -23,4 +23,5 @@ contract C { } // ---- // test() -> 7 -// gas legacy: 100849 +// gas legacy: 76649 +// gas legacy code: 24200 diff --git a/test/libsolidity/semanticTests/various/code_access_runtime.sol b/test/libsolidity/semanticTests/various/code_access_runtime.sol index 9a9e7a80bf90..b62eb0b8535c 100644 --- a/test/libsolidity/semanticTests/various/code_access_runtime.sol +++ b/test/libsolidity/semanticTests/various/code_access_runtime.sol @@ -23,4 +23,5 @@ contract C { // EVMVersion: >=constantinople // ---- // test() -> 42 -// gas legacy: 100235 +// gas legacy: 76035 +// gas legacy code: 24200 diff --git a/test/libsolidity/semanticTests/various/code_length.sol b/test/libsolidity/semanticTests/various/code_length.sol index a8aac5306f50..c30ac838fe4c 100644 --- a/test/libsolidity/semanticTests/various/code_length.sol +++ b/test/libsolidity/semanticTests/various/code_length.sol @@ -59,5 +59,6 @@ contract C { } // ---- // constructor() -// gas legacy: 124136 +// gas legacy: 66936 +// gas legacy code: 57200 // f(): true, true -> true, true diff --git a/test/libsolidity/semanticTests/various/create_calldata.sol b/test/libsolidity/semanticTests/various/create_calldata.sol index c3d038136f6e..2db42adebb86 100644 --- a/test/libsolidity/semanticTests/various/create_calldata.sol +++ b/test/libsolidity/semanticTests/various/create_calldata.sol @@ -8,7 +8,10 @@ contract C { } // ---- // constructor(): 42 -> -// gas irOptimized: 145578 -// gas legacy: 173845 -// gas legacyOptimized: 137877 +// gas irOptimized: 68378 +// gas irOptimized code: 77200 +// gas legacy: 78445 +// gas legacy code: 95400 +// gas legacyOptimized: 68677 +// gas legacyOptimized code: 69200 // s() -> 0x20, 0 diff --git a/test/libsolidity/semanticTests/various/erc20.sol b/test/libsolidity/semanticTests/various/erc20.sol index 00c12fb18a70..f7956c4c50eb 100644 --- a/test/libsolidity/semanticTests/various/erc20.sol +++ b/test/libsolidity/semanticTests/various/erc20.sol @@ -96,9 +96,12 @@ contract ERC20 { // ---- // constructor() // ~ emit Transfer(address,address,uint256): #0x00, #0x1212121212121212121212121212120000000012, 0x14 -// gas irOptimized: 353248 -// gas legacy: 807559 -// gas legacyOptimized: 408718 +// gas irOptimized: 121248 +// gas irOptimized code: 232000 +// gas legacy: 159959 +// gas legacy code: 647600 +// gas legacyOptimized: 126918 +// gas legacyOptimized code: 281800 // totalSupply() -> 20 // gas irOptimized: 23415 // gas legacy: 23524 diff --git a/test/libsolidity/semanticTests/various/external_types_in_calls.sol b/test/libsolidity/semanticTests/various/external_types_in_calls.sol index e8742c2346b0..aff302d26e8f 100644 --- a/test/libsolidity/semanticTests/various/external_types_in_calls.sol +++ b/test/libsolidity/semanticTests/various/external_types_in_calls.sol @@ -24,5 +24,6 @@ contract C { } // ---- // test() -> 9, 7 -// gas legacy: 127514 +// gas legacy: 80314 +// gas legacy code: 47200 // t2() -> 9 diff --git a/test/libsolidity/semanticTests/various/many_subassemblies.sol b/test/libsolidity/semanticTests/various/many_subassemblies.sol index 26c3a1bbff7b..67a576da7889 100644 --- a/test/libsolidity/semanticTests/various/many_subassemblies.sol +++ b/test/libsolidity/semanticTests/various/many_subassemblies.sol @@ -30,6 +30,9 @@ contract D { } // ---- // run() -> -// gas irOptimized: 381615 -// gas legacy: 392719 -// gas legacyOptimized: 392719 +// gas irOptimized: 375015 +// gas irOptimized code: 6600 +// gas legacy: 375119 +// gas legacy code: 17600 +// gas legacyOptimized: 375119 +// gas legacyOptimized code: 17600 diff --git a/test/libsolidity/semanticTests/various/negative_stack_height.sol b/test/libsolidity/semanticTests/various/negative_stack_height.sol index 7289d2bbd96f..44fd0089cf70 100644 --- a/test/libsolidity/semanticTests/various/negative_stack_height.sol +++ b/test/libsolidity/semanticTests/various/negative_stack_height.sol @@ -65,5 +65,7 @@ contract C { // compileViaYul: false // ---- // constructor() -> -// gas legacy: 575268 -// gas legacyOptimized: 345022 +// gas legacy: 92268 +// gas legacy code: 483000 +// gas legacyOptimized: 75022 +// gas legacyOptimized code: 270000 diff --git a/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun.sol b/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun.sol index 4142eb25cf2d..a39b6c42355c 100644 --- a/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun.sol +++ b/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun.sol @@ -62,9 +62,12 @@ contract D { // EVMVersion: =shanghai // ---- // constructor(), 1 ether -> -// gas irOptimized: 242428 -// gas legacy: 373563 -// gas legacyOptimized: 234516 +// gas irOptimized: 67028 +// gas irOptimized code: 175400 +// gas legacy: 76163 +// gas legacy code: 297400 +// gas legacyOptimized: 66516 +// gas legacyOptimized code: 168000 // exists() -> false // test_create_and_terminate() -> // exists() -> false diff --git a/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_multiple_beneficiaries.sol b/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_multiple_beneficiaries.sol index 87e81a2902d1..a09a7cac68b1 100644 --- a/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_multiple_beneficiaries.sol +++ b/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_multiple_beneficiaries.sol @@ -35,23 +35,30 @@ contract D { // EVMVersion: <=shanghai // ---- // constructor(), 2 ether -> -// gas irOptimized: 223918 -// gas legacy: 374024 -// gas legacyOptimized: 239815 +// gas irOptimized: 107718 +// gas irOptimized code: 116200 +// gas legacy: 120424 +// gas legacy code: 253600 +// gas legacyOptimized: 109015 +// gas legacyOptimized code: 130800 // balance: 0x1111111111111111111111111111111111111111 -> 0 // balance: 0x2222222222222222222222222222222222222222 -> 0 // balance -> 2000000000000000000 // exists() -> false // test_deploy_and_terminate_twice() -> -// gas irOptimized: 135350 -// gas legacy: 165584 -// gas legacyOptimized: 144396 +// gas irOptimized: 121350 +// gas irOptimized code: 14000 +// gas legacy: 122384 +// gas legacy code: 43200 +// gas legacyOptimized: 121596 +// gas legacyOptimized code: 22800 // exists() -> false // balance: 0x1111111111111111111111111111111111111111 -> 1000000000000000000 // balance: 0x2222222222222222222222222222222222222222 -> 0 // balance -> 1000000000000000000 // deploy() -> -// gas legacy: 101691 +// gas legacy: 58491 +// gas legacy code: 43200 // exists() -> true // balance: 0x1111111111111111111111111111111111111111 -> 1000000000000000000 // balance: 0x2222222222222222222222222222222222222222 -> 0 diff --git a/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_redeploy.sol b/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_redeploy.sol index e0885782c260..fc650ebba741 100644 --- a/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_redeploy.sol +++ b/test/libsolidity/semanticTests/various/selfdestruct_pre_cancun_redeploy.sol @@ -79,15 +79,21 @@ contract D { // EVMVersion: =shanghai // ---- // constructor(), 1 ether -> -// gas irOptimized: 430253 -// gas legacy: 690244 -// gas legacyOptimized: 412799 +// gas irOptimized: 133253 +// gas irOptimized code: 297000 +// gas legacy: 151644 +// gas legacy code: 538600 +// gas legacyOptimized: 131799 +// gas legacyOptimized code: 281000 // exists() -> false // test_deploy_and_terminate() -> // ~ emit Deployed(address,bytes32) from 0x137aa4dfc0911524504fcd4d98501f179bc13b4a: 0x7e6580007e709ac52945fae182c61131d42634e8, 0x1234000000000000000000000000000000000000000000000000000000000000 -// gas irOptimized: 117489 -// gas legacy: 118895 -// gas legacyOptimized: 117137 +// gas irOptimized: 96689 +// gas irOptimized code: 20800 +// gas legacy: 98095 +// gas legacy code: 20800 +// gas legacyOptimized: 96337 +// gas legacyOptimized code: 20800 // exists() -> false // deploy_create2() -> // ~ emit Deployed(address,bytes32) from 0x137aa4dfc0911524504fcd4d98501f179bc13b4a: 0x7e6580007e709ac52945fae182c61131d42634e8, 0x1234000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/various/senders_balance.sol b/test/libsolidity/semanticTests/various/senders_balance.sol index a1c7d4147fc6..5b4371149f42 100644 --- a/test/libsolidity/semanticTests/various/senders_balance.sol +++ b/test/libsolidity/semanticTests/various/senders_balance.sol @@ -16,7 +16,10 @@ contract D { } // ---- // constructor(), 27 wei -> -// gas irOptimized: 167857 -// gas legacy: 218435 -// gas legacyOptimized: 167276 +// gas irOptimized: 114057 +// gas irOptimized code: 53800 +// gas legacy: 117835 +// gas legacy code: 100600 +// gas legacyOptimized: 113676 +// gas legacyOptimized code: 53600 // f() -> 27 diff --git a/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol index 1306529d4183..77a330f3cd54 100644 --- a/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol +++ b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol @@ -35,12 +35,19 @@ contract D { // EVMVersion: >=byzantium // ---- // f() -> 0x1 # This should work, next should throw # -// gas legacy: 102095 +// gas legacy: 76495 +// gas legacy code: 25600 // fview() -> FAILURE -// gas irOptimized: 98438588 -// gas legacy: 98438774 -// gas legacyOptimized: 98438580 +// gas irOptimized: 98425388 +// gas irOptimized code: 13200 +// gas legacy: 98413174 +// gas legacy code: 25600 +// gas legacyOptimized: 98425380 +// gas legacyOptimized code: 13200 // fpure() -> FAILURE -// gas irOptimized: 98438589 -// gas legacy: 98438774 -// gas legacyOptimized: 98438580 +// gas irOptimized: 98425389 +// gas irOptimized code: 13200 +// gas legacy: 98413174 +// gas legacy code: 25600 +// gas legacyOptimized: 98425380 +// gas legacyOptimized code: 13200 diff --git a/test/libsolidity/semanticTests/various/value_complex.sol b/test/libsolidity/semanticTests/various/value_complex.sol index 2adac6bf4cd0..427a006dc102 100644 --- a/test/libsolidity/semanticTests/various/value_complex.sol +++ b/test/libsolidity/semanticTests/various/value_complex.sol @@ -19,7 +19,10 @@ contract test { } // ---- // constructor(), 20 wei -> -// gas irOptimized: 172399 -// gas legacy: 252276 -// gas legacyOptimized: 180336 +// gas irOptimized: 114399 +// gas irOptimized code: 58000 +// gas legacy: 120076 +// gas legacy code: 132200 +// gas legacyOptimized: 114536 +// gas legacyOptimized code: 65800 // sendAmount(uint256): 5 -> 8 diff --git a/test/libsolidity/semanticTests/various/value_insane.sol b/test/libsolidity/semanticTests/various/value_insane.sol index 87fa01485d61..97fbbab1e634 100644 --- a/test/libsolidity/semanticTests/various/value_insane.sol +++ b/test/libsolidity/semanticTests/various/value_insane.sol @@ -18,7 +18,10 @@ contract test { } // ---- // constructor(), 20 wei -> -// gas irOptimized: 173263 -// gas legacy: 253800 -// gas legacyOptimized: 180768 +// gas irOptimized: 114463 +// gas irOptimized code: 58800 +// gas legacy: 120200 +// gas legacy code: 133600 +// gas legacyOptimized: 114568 +// gas legacyOptimized code: 66200 // sendAmount(uint256): 5 -> 8 diff --git a/test/libsolidity/util/SoltestTypes.h b/test/libsolidity/util/SoltestTypes.h index a2d593fd3c65..791cea011610 100644 --- a/test/libsolidity/util/SoltestTypes.h +++ b/test/libsolidity/util/SoltestTypes.h @@ -215,11 +215,11 @@ struct FunctionCallExpectations raw += param.rawBytes; return raw; } - /// Gas used by function call - /// Keys represent all distinct combinations of compilation settings that affect produced + /// Gas used by function call minus the portion spent on code deposits (which is tracked + /// separately, in @a gasUsedForCodeDeposit). /// bytecode (and therefore the cost), except for EVM version. E.g. IR codegen without /// optimization legacy codegen with optimization. - std::map gasUsed; + std::map gasUsedExcludingCode; /// The portion of @a gasUsed spent on code deposits of newly created contracts. /// May exceed @a gasUsed in rare corner cases due to refunds. diff --git a/test/libsolidity/util/TestFileParser.cpp b/test/libsolidity/util/TestFileParser.cpp index e7e20f7c5cb5..5a32a1591a16 100644 --- a/test/libsolidity/util/TestFileParser.cpp +++ b/test/libsolidity/util/TestFileParser.cpp @@ -103,7 +103,7 @@ std::vector TestFileParser::parseFunctio std::map& gasExpectationMap = (isCodeDepositCost ? calls.back().expectations.gasUsedForCodeDeposit : - calls.back().expectations.gasUsed + calls.back().expectations.gasUsedExcludingCode ); if (gasExpectationMap.count(runType) > 0) throw TestParserError("Gas usage expectation set multiple times."); @@ -207,9 +207,9 @@ std::vector TestFileParser::parseFunctio { // Ensure that each specified gas expectation has both components to simplify working with them. for (auto const& [runType, gas]: call.expectations.gasUsedForCodeDeposit) - call.expectations.gasUsed.try_emplace({runType, 0}); + call.expectations.gasUsedExcludingCode.try_emplace({runType, 0}); - for (auto const& [runType, gas]: call.expectations.gasUsed) + for (auto const& [runType, gas]: call.expectations.gasUsedExcludingCode) call.expectations.gasUsedForCodeDeposit.try_emplace({runType, 0}); } diff --git a/test/libsolidity/util/TestFileParserTests.cpp b/test/libsolidity/util/TestFileParserTests.cpp index 7a794f915265..4bb4aed3aced 100644 --- a/test/libsolidity/util/TestFileParserTests.cpp +++ b/test/libsolidity/util/TestFileParserTests.cpp @@ -1074,7 +1074,7 @@ BOOST_AUTO_TEST_CASE(gas) auto const calls = parse(source); BOOST_REQUIRE_EQUAL(calls.size(), 1); BOOST_REQUIRE_EQUAL(calls[0].expectations.failure, false); - BOOST_TEST(calls[0].expectations.gasUsed == (std::map{ + BOOST_TEST(calls[0].expectations.gasUsedExcludingCode == (std::map{ {"ir", 3245}, {"legacy", 5000}, {"legacyOptimized", 0}, @@ -1121,19 +1121,19 @@ BOOST_AUTO_TEST_CASE(gas_with_code_deposit_cost) // f() -> // gas legacyOptimized code: 1 // gas ir: 13000 - // gas irOptimized: 6666 + // gas irOptimized: 6000 // gas irOptimized code: 666 // gas legacy code: 0 - // gas legacyOptimized: 2 + // gas legacyOptimized: 1 )"; auto const calls = parse(source); BOOST_REQUIRE_EQUAL(calls.size(), 1); BOOST_REQUIRE_EQUAL(calls[0].expectations.failure, false); - BOOST_TEST(calls[0].expectations.gasUsed == (std::map{ + BOOST_TEST(calls[0].expectations.gasUsedExcludingCode == (std::map{ {"ir", 13000}, - {"irOptimized", 6666}, + {"irOptimized", 6000}, {"legacy", 0}, - {"legacyOptimized", 2}, + {"legacyOptimized", 1}, })); BOOST_TEST(calls[0].expectations.gasUsedForCodeDeposit == (std::map{ {"ir", 0}, @@ -1175,27 +1175,20 @@ BOOST_AUTO_TEST_CASE(gas_with_code_deposit_cost_double_code_gas) BOOST_AUTO_TEST_CASE(gas_with_code_deposit_cost_negative_non_code_cost) { // NOTE: This arrangement is unlikely but may still be possible due to refunds. + // We'll deal with it when we actually have a test case like that. char const* source = R"( // f() -> - // gas ir: 10 + // gas ir: -10 // gas ir code: 20 )"; - auto const calls = parse(source); - BOOST_REQUIRE_EQUAL(calls.size(), 1); - BOOST_REQUIRE_EQUAL(calls[0].expectations.failure, false); - BOOST_TEST(calls[0].expectations.gasUsed == (std::map{ - {"ir", 10}, - })); - BOOST_TEST(calls[0].expectations.gasUsedForCodeDeposit == (std::map{ - {"ir", 20}, - })); + BOOST_REQUIRE_THROW(parse(source), TestParserError); } BOOST_AUTO_TEST_CASE(gas_with_code_deposit_cost_negative_total_cost) { char const* source = R"( // f() -> - // gas ir: -10 + // gas ir: -30 // gas ir code: 20 )"; BOOST_REQUIRE_THROW(parse(source), TestParserError); @@ -1205,7 +1198,7 @@ BOOST_AUTO_TEST_CASE(gas_with_code_deposit_cost_negative_code_cost) { char const* source = R"( // f() -> - // gas ir: 20 + // gas ir: 10 // gas ir code: -10 )"; BOOST_REQUIRE_THROW(parse(source), TestParserError); diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index b22ac3716a13..dd74d4035c68 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -377,20 +377,20 @@ std::string TestFunctionCall::formatGasExpectations( using ranges::views::keys; using ranges::views::set_symmetric_difference; - soltestAssert(set_symmetric_difference(m_codeDepositGasCosts | keys, m_gasCosts | keys).empty()); - soltestAssert(set_symmetric_difference(m_call.expectations.gasUsedForCodeDeposit | keys, m_call.expectations.gasUsed | keys).empty()); + soltestAssert(set_symmetric_difference(m_codeDepositGasCosts | keys, m_gasCostsExcludingCode | keys).empty()); + soltestAssert(set_symmetric_difference(m_call.expectations.gasUsedForCodeDeposit | keys, m_call.expectations.gasUsedExcludingCode | keys).empty()); std::stringstream os; - for (auto const& [runType, gasUsed]: (_useActualCost ? m_gasCosts : m_call.expectations.gasUsed)) + for (auto const& [runType, gasUsedExcludingCode]: (_useActualCost ? m_gasCostsExcludingCode : m_call.expectations.gasUsedExcludingCode)) { soltestAssert(runType != ""); u256 gasUsedForCodeDeposit = (_useActualCost ? m_codeDepositGasCosts : m_call.expectations.gasUsedForCodeDeposit).at(runType); - os << std::endl << _linePrefix << "// gas " << runType << ": " << gasUsed.str(); + os << std::endl << _linePrefix << "// gas " << runType << ": " << gasUsedExcludingCode.str(); std::string gasDiff = formatGasDiff( - gasOrNullopt(m_gasCosts, runType), - gasOrNullopt(m_call.expectations.gasUsed, runType) + gasOrNullopt(m_gasCostsExcludingCode, runType), + gasOrNullopt(m_call.expectations.gasUsedExcludingCode, runType) ); if (_showDifference && !gasDiff.empty() && _useActualCost) os << " [" << gasDiff << "]"; diff --git a/test/libsolidity/util/TestFunctionCall.h b/test/libsolidity/util/TestFunctionCall.h index cf05b6a216d7..724cb8286c19 100644 --- a/test/libsolidity/util/TestFunctionCall.h +++ b/test/libsolidity/util/TestFunctionCall.h @@ -53,7 +53,7 @@ class TestFunctionCall TestFunctionCall(FunctionCall _call): m_call(std::move(_call)), - m_gasCosts(m_call.expectations.gasUsed), + m_gasCostsExcludingCode(m_call.expectations.gasUsedExcludingCode), m_codeDepositGasCosts(m_call.expectations.gasUsedForCodeDeposit) {} @@ -97,7 +97,7 @@ class TestFunctionCall void calledNonExistingFunction() { m_calledNonExistingFunction = true; } void setFailure(const bool _failure) { m_failure = _failure; } void setRawBytes(const bytes _rawBytes) { m_rawBytes = _rawBytes; } - void setGasCost(std::string const& _runType, u256 const& _gasCost) { m_gasCosts[_runType] = _gasCost; } + void setGasCostExcludingCode(std::string const& _runType, u256 const& _gasCost) { m_gasCostsExcludingCode[_runType] = _gasCost; } void setCodeDepositGasCost(std::string const& _runType, u256 const& _gasCost) { m_codeDepositGasCosts[_runType] = _gasCost; } void setContractABI(Json::Value _contractABI) { m_contractABI = std::move(_contractABI); } void setSideEffects(std::vector _sideEffects) { m_call.actualSideEffects = _sideEffects; } @@ -147,7 +147,7 @@ class TestFunctionCall /// Result of the actual call been made. bytes m_rawBytes = bytes{}; /// Actual gas costs - std::map m_gasCosts; + std::map m_gasCostsExcludingCode; /// Actual code deposit gas costs std::map m_codeDepositGasCosts; /// Transaction status of the actual call. False in case of a REVERT or any other failure. diff --git a/test/libsolidity/util/TestFunctionCallTests.cpp b/test/libsolidity/util/TestFunctionCallTests.cpp index b78b7e5f290d..5741ccd0debe 100644 --- a/test/libsolidity/util/TestFunctionCallTests.cpp +++ b/test/libsolidity/util/TestFunctionCallTests.cpp @@ -295,7 +295,7 @@ BOOST_AUTO_TEST_CASE(format_gas_with_code_deposit) "some comment", { {"ir", 0}, // Zero costs are shown - {"legacy", 5000}, + {"legacy", 4875}, {"legacy optimized", 300}, }, { @@ -311,7 +311,7 @@ BOOST_AUTO_TEST_CASE(format_gas_with_code_deposit) TestFunctionCall(call).format(), "// f() -> #some comment#\n" "// gas ir: 0\n" - "// gas legacy: 5000\n" + "// gas legacy: 4875\n" "// gas legacy code: 125\n" "// gas legacy optimized: 300" ); From 381b149b8dd3fe87ae9d1af49514788adaa3e997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 11 Feb 2024 16:03:50 +0100 Subject: [PATCH 140/189] gas_diff_stats.py: Include code deposit costs in total cost --- scripts/gas_diff_stats.py | 5 ++++- test/scripts/test_gas_diff_stats.py | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/scripts/gas_diff_stats.py b/scripts/gas_diff_stats.py index 7403b391721c..13d8a59e8d76 100755 --- a/scripts/gas_diff_stats.py +++ b/scripts/gas_diff_stats.py @@ -23,7 +23,7 @@ import sys from pathlib import Path from enum import Enum -from parsec import generate, ParseError, regex, string +from parsec import generate, ParseError, regex, string, optional from tabulate import tabulate class Kind(Enum): @@ -49,6 +49,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) +code_suffix = string("code") def number() -> int: """Parse number.""" @@ -67,6 +68,8 @@ def diff_string() -> (Kind, Diff, int): yield comment yield space codegen_kind = yield gas_ir_optimized ^ gas_ir ^ gas_legacy_optimized ^ gas_legacy + yield optional(space) + yield optional(code_suffix) yield colon yield space val = yield number() diff --git a/test/scripts/test_gas_diff_stats.py b/test/scripts/test_gas_diff_stats.py index 4980293aea08..e67bd0be8930 100644 --- a/test/scripts/test_gas_diff_stats.py +++ b/test/scripts/test_gas_diff_stats.py @@ -85,3 +85,25 @@ def test_collect_statistics_should_ignore_unchanged_costs(self): 0, # +legacyOptimized 3, # +legacy )) + + def test_collect_statistics_should_include_code_deposit_in_total_cost(self): + diff_output = dedent(""" + -// gas irOptimized: 1 + -// gas legacy: 20 + -// gas legacyOptimized: 300 + +// gas irOptimized: 4000 + +// gas irOptimized code: 50000 + +// gas legacy: 600000 + +// gas legacyOptimized: 7000000 + +// gas legacyOptimized code: 80000000 + -// gas legacy code: 900000000 + """).splitlines() + + self.assertEqual(collect_statistics(diff_output), ( + 1, # -irOptimized + 300, # -legacyOptimized + 900000020, # -legacy + 54000, # +irOptimized + 87000000, # +legacyOptimized + 600000, # +legacy + )) From e7312a501086e1b0751bc3abbc7e7f08de8b8acd Mon Sep 17 00:00:00 2001 From: r0qs Date: Wed, 21 Feb 2024 21:09:09 +0100 Subject: [PATCH 141/189] Use CircleCI rust+node convenience image for t_ems_ext_hardhat job --- .circleci/config.yml | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 663c64520e4a..30c0229642a0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1385,7 +1385,7 @@ jobs: t_ems_ext_hardhat: <<: *base_node_small docker: - - image: cimg/node:18.16 + - image: cimg/rust:1.74.0-node environment: <<: *base_node_small_env HARDHAT_TESTS_SOLC_PATH: /tmp/workspace/soljson.js @@ -1395,18 +1395,15 @@ jobs: at: /tmp/workspace - run: name: Ensure pnpm is installed if npm is present - command: | - if command -v npm &> /dev/null; then - sudo npm install -g pnpm - fi + command: sudo npm install -g pnpm - run: name: Retrieve Hardhat 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}") + EXTRA_HEADERS=(--header "Authorization: Bearer ${GITHUB_ACCESS_TOKEN}") fi - HARDHAT_RELEASE_TAG=$( + HARDHAT_LATEST_RELEASE_TAG=$( curl \ --silent \ --location \ @@ -1416,8 +1413,8 @@ jobs: https://api.github.com/repos/nomiclabs/hardhat/releases/latest \ | jq --raw-output .tag_name \ ) - echo "export HARDHAT_RELEASE_TAG='${HARDHAT_RELEASE_TAG}'" >> "$BASH_ENV" - - run: git clone --depth 1 https://github.com/nomiclabs/hardhat.git --branch $HARDHAT_RELEASE_TAG + echo "export HARDHAT_LATEST_RELEASE_TAG='${HARDHAT_LATEST_RELEASE_TAG}'" >> "$BASH_ENV" + - run: git clone --depth 1 https://github.com/nomiclabs/hardhat.git --branch "$HARDHAT_LATEST_RELEASE_TAG" - run: name: Install dependencies command: | From c8358f5677a786847fb4783866f995d0e140389c Mon Sep 17 00:00:00 2001 From: r0qs Date: Wed, 21 Feb 2024 21:01:28 +0100 Subject: [PATCH 142/189] Fix detect_hardhat_artifact_dir in externalTests scripts --- scripts/externalTests/common.sh | 41 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/scripts/externalTests/common.sh b/scripts/externalTests/common.sh index 3bca968a5aa5..082a25c4c58c 100644 --- a/scripts/externalTests/common.sh +++ b/scripts/externalTests/common.sh @@ -570,17 +570,15 @@ function gas_report_to_json cat - | "${REPO_ROOT}/scripts/externalTests/parse_eth_gas_report.py" | jq '{gas: .}' } -function detect_hardhat_artifact_dir -{ - if [[ -e build/ && -e artifacts/ ]]; then - fail "Cannot determine Hardhat artifact location. Both build/ and artifacts/ exist" - elif [[ -e build/ ]]; then - echo -n build/artifacts - elif [[ -e artifacts/ ]]; then - echo -n artifacts - else - fail "Hardhat build artifacts not found." - fi +function detect_hardhat_artifact_dirs +{ + # NOTE: The artifacts path is a configured parameter in Hardhat, so the below may fail for new external tests + # See: https://hardhat.org/hardhat-runner/docs/config#path-configuration + local artifact_dir=() + [[ -e build/artifacts ]] && artifact_dir+=("build/artifacts") + [[ -e artifacts/ ]] && artifact_dir+=("artifacts") + (( ${#artifact_dir[@]} != 0 )) || assertFail + echo -n "${artifact_dir[@]}" } function bytecode_size_json_from_truffle_artifacts @@ -605,16 +603,17 @@ function bytecode_size_json_from_truffle_artifacts function bytecode_size_json_from_hardhat_artifacts { # NOTE: The output of this function is a series of concatenated JSON dicts rather than a list. - - for artifact in "$(detect_hardhat_artifact_dir)"/build-info/*.json; do - # Each artifact contains Standard JSON output under the `output` key. - # Process it into a dict of the form `{"": {"": }}`, - # Note that one Hardhat artifact often represents multiple input files. - jq '.output.contracts | to_entries[] | { - "\(.key)": .value | to_entries[] | { - "\(.key)": (.value.evm.bytecode.object | length / 2) - } - }' "$artifact" + for artifact_dir in $(detect_hardhat_artifact_dirs); do + for artifact in "$artifact_dir"/build-info/*.json; do + # Each artifact contains Standard JSON output under the `output` key. + # Process it into a dict of the form `{"": {"": }}`, + # Note that one Hardhat artifact often represents multiple input files. + jq '.output.contracts | to_entries[] | { + "\(.key)": .value | to_entries[] | { + "\(.key)": (.value.evm.bytecode.object | length / 2) + } + }' "$artifact" + done done } From 4af244f4aa0dd97e3e2178a3a1fd4dd53760b529 Mon Sep 17 00:00:00 2001 From: r0qs Date: Fri, 2 Feb 2024 14:55:12 +0100 Subject: [PATCH 143/189] Separate nightly-ossfuzz workflow from nightly workflow --- .circleci/config.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 30c0229642a0..9d3105a27701 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1978,10 +1978,6 @@ workflows: <<: *on_develop jobs: - # OSSFUZZ builds and (regression) tests - - b_ubu_ossfuzz: *requires_nothing - - t_ubu_ossfuzz: *requires_b_ubu_ossfuzz - # Code Coverage enabled build and tests - b_ubu_codecov: *requires_nothing - t_ubu_codecov: *requires_b_ubu_codecov @@ -1997,3 +1993,15 @@ workflows: - b_ubu_san_clang: *job_b_ubu_ubsan_clang - t_ubu_ubsan_clang_soltest: *requires_b_ubu_ubsan_clang - t_ubu_ubsan_clang_cli: *requires_b_ubu_ubsan_clang + + nightly-ossfuzz: + + triggers: + - schedule: + cron: "0 0 * * *" + <<: *on_develop + + jobs: + # OSSFUZZ builds and (regression) tests + - b_ubu_ossfuzz: *requires_nothing + - t_ubu_ossfuzz: *requires_b_ubu_ossfuzz From e095590330dfbd3022db12eb5f106606f818dd21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 2 Nov 2023 18:59:14 +0100 Subject: [PATCH 144/189] Parser::parseFunctionDefinition(): Return FunctionDefinition, not ASTNode --- libsolidity/parsing/Parser.cpp | 2 +- libsolidity/parsing/Parser.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 42be1ab9eecf..c7f0c313fd18 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -627,7 +627,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari return result; } -ASTPointer Parser::parseFunctionDefinition(bool _freeFunction, bool _allowBody) +ASTPointer Parser::parseFunctionDefinition(bool _freeFunction, bool _allowBody) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index d4c48c7a638a..86dd2843fdbc 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -102,7 +102,7 @@ class Parser: public langutil::ParserBase ASTPointer parseOverrideSpecifier(); StateMutability parseStateMutability(); FunctionHeaderParserResult parseFunctionHeader(bool _isStateVariable); - ASTPointer parseFunctionDefinition(bool _freeFunction = false, bool _allowBody = true); + ASTPointer parseFunctionDefinition(bool _freeFunction = false, bool _allowBody = true); ASTPointer parseStructDefinition(); ASTPointer parseEnumDefinition(); ASTPointer parseUserDefinedValueTypeDefinition(); From fa35ed5b98baa56e6e03abe61f3e6b9b42643e5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 2 Nov 2023 18:37:14 +0100 Subject: [PATCH 145/189] Add ForAllQuantifier AST node --- libsolidity/ast/AST.h | 32 ++++++++++++++++++++++++++++++++ libsolidity/ast/ASTAnnotations.h | 4 ++++ libsolidity/ast/ASTForward.h | 1 + libsolidity/ast/ASTVisitor.h | 4 ++++ libsolidity/ast/AST_accept.h | 20 ++++++++++++++++++++ 5 files changed, 61 insertions(+) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index b9cc2118ef54..27926721aa6f 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -2625,6 +2625,38 @@ class Builtin: public Expression SourceLocation m_nameParameterLocation; }; +// TODO: NatSpec used on the quantifier should be recognized as applying to the function. +class ForAllQuantifier: public ASTNode, public Scopable, public ScopeOpener +{ +public: + ForAllQuantifier( + int64_t _id, + SourceLocation _location, + ASTPointer _typeVariableDeclarations, + ASTPointer _quantifiedDeclaration + ): + ASTNode(_id, std::move(_location)), + m_typeVariableDeclarations(std::move(_typeVariableDeclarations)), + m_quantifiedDeclaration(std::move(_quantifiedDeclaration)) + { + solAssert(m_typeVariableDeclarations); + solAssert(m_quantifiedDeclaration); + } + + void accept(ASTVisitor& _visitor) override; + void accept(ASTConstVisitor& _visitor) const override; + ForAllQuantifierAnnotation& annotation() const override { return initAnnotation(); } + + bool experimentalSolidityOnly() const override { return true; } + + ParameterList const& typeVariableDeclarations() const { return *m_typeVariableDeclarations; } + FunctionDefinition const& quantifiedDeclaration() const { return *m_quantifiedDeclaration; } + +private: + ASTPointer m_typeVariableDeclarations; + ASTPointer m_quantifiedDeclaration; +}; + /// @} } diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index e68614f464c0..f7519250a3a8 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -350,6 +350,10 @@ struct FunctionCallAnnotation: ExpressionAnnotation struct TypeClassDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation { }; + +struct ForAllQuantifierAnnotation: StatementAnnotation, ScopableAnnotation +{ +}; /// @} } diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index f7c3d60237d7..b8a3a4ae0057 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -106,6 +106,7 @@ class TypeClassInstantiation; class TypeClassName; class TypeDefinition; class Builtin; +class ForAllQuantifier; /// @} class VariableScope; diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index f9196d6b95c8..f985a449019a 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -116,6 +116,7 @@ class ASTVisitor virtual bool visit(TypeDefinition& _node) { return visitNode(_node); } virtual bool visit(TypeClassName& _node) { return visitNode(_node); } virtual bool visit(Builtin& _node) { return visitNode(_node); } + virtual bool visit(ForAllQuantifier& _node) { return visitNode(_node); } /// @} virtual void endVisit(SourceUnit& _node) { endVisitNode(_node); } @@ -180,6 +181,7 @@ class ASTVisitor virtual void endVisit(TypeDefinition& _node) { endVisitNode(_node); } virtual void endVisit(TypeClassName& _node) { endVisitNode(_node); } virtual void endVisit(Builtin& _node) { endVisitNode(_node); } + virtual void endVisit(ForAllQuantifier& _node) { endVisitNode(_node); } /// @} protected: @@ -266,6 +268,7 @@ class ASTConstVisitor virtual bool visit(TypeDefinition const& _node) { return visitNode(_node); } virtual bool visit(TypeClassName const& _node) { return visitNode(_node); } virtual bool visit(Builtin const& _node) { return visitNode(_node); } + virtual bool visit(ForAllQuantifier const& _node) { return visitNode(_node); } /// @} virtual void endVisit(SourceUnit const& _node) { endVisitNode(_node); } @@ -330,6 +333,7 @@ class ASTConstVisitor virtual void endVisit(TypeDefinition const& _node) { endVisitNode(_node); } virtual void endVisit(TypeClassName const& _node) { endVisitNode(_node); } virtual void endVisit(Builtin const& _node) { endVisitNode(_node); } + virtual void endVisit(ForAllQuantifier const& _node) { endVisitNode(_node); } /// @} protected: diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index be9de64db7a2..55ebe2a78c61 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -1140,6 +1140,26 @@ void Builtin::accept(ASTConstVisitor& _visitor) const _visitor.visit(*this); _visitor.endVisit(*this); } + +void ForAllQuantifier::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_typeVariableDeclarations->accept(_visitor); + m_quantifiedDeclaration->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void ForAllQuantifier::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_typeVariableDeclarations->accept(_visitor); + m_quantifiedDeclaration->accept(_visitor); + } + _visitor.endVisit(*this); +} /// @} } From 4d051c1fcefa9c8766f7083139e294703503d88a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 2 Nov 2023 19:07:44 +0100 Subject: [PATCH 146/189] Add parser support for quantified function definitions --- liblangutil/Token.h | 1 + .../experimental/analysis/TypeInference.cpp | 13 ++++++ .../experimental/analysis/TypeInference.h | 1 + libsolidity/parsing/Parser.cpp | 24 +++++++++++ libsolidity/parsing/Parser.h | 1 + .../parsing/forall_free_function.sol | 27 ++++++++++++ .../forall_free_function_no_type_var.sol | 14 ++++++ .../forall_free_function_with_sorts.sol | 43 +++++++++++++++++++ .../parsing/forall_type_class.sol | 8 ++++ .../forall_type_class_instantiation.sol | 12 ++++++ 10 files changed, 144 insertions(+) create mode 100644 test/libsolidity/syntaxTests/experimental/parsing/forall_free_function.sol create mode 100644 test/libsolidity/syntaxTests/experimental/parsing/forall_free_function_no_type_var.sol create mode 100644 test/libsolidity/syntaxTests/experimental/parsing/forall_free_function_with_sorts.sol create mode 100644 test/libsolidity/syntaxTests/experimental/parsing/forall_type_class.sol create mode 100644 test/libsolidity/syntaxTests/experimental/parsing/forall_type_class_instantiation.sol diff --git a/liblangutil/Token.h b/liblangutil/Token.h index 98de0b228d04..0f7c2df08034 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -276,6 +276,7 @@ namespace solidity::langutil K(Itself, "itself", 0) \ K(StaticAssert, "static_assert", 0) \ K(Builtin, "__builtin", 0) \ + K(ForAll, "forall", 0) \ T(ExperimentalEnd, nullptr, 0) /* used as experimental enum end marker */ \ \ /* Illegal token - not able to scan. */ \ diff --git a/libsolidity/experimental/analysis/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp index 63a377f90d56..8625937a35d0 100644 --- a/libsolidity/experimental/analysis/TypeInference.cpp +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -129,6 +129,19 @@ bool TypeInference::analyze(SourceUnit const& _sourceUnit) return !m_errorReporter.hasErrors(); } +bool TypeInference::visit(ForAllQuantifier const& _quantifier) +{ + solAssert(m_expressionContext == ExpressionContext::Term); + + { + ScopedSaveAndRestore expressionContext{m_expressionContext, ExpressionContext::Type}; + _quantifier.typeVariableDeclarations().accept(*this); + } + + _quantifier.quantifiedDeclaration().accept(*this); + return false; +} + bool TypeInference::visit(FunctionDefinition const& _functionDefinition) { solAssert(m_expressionContext == ExpressionContext::Term); diff --git a/libsolidity/experimental/analysis/TypeInference.h b/libsolidity/experimental/analysis/TypeInference.h index 3eb2ad94cf71..093236799555 100644 --- a/libsolidity/experimental/analysis/TypeInference.h +++ b/libsolidity/experimental/analysis/TypeInference.h @@ -56,6 +56,7 @@ class TypeInference: public ASTConstVisitor void endVisit(VariableDeclarationStatement const& _variableDeclarationStatement) override; bool visit(VariableDeclaration const& _variableDeclaration) override; + bool visit(ForAllQuantifier const& _forAllQuantifier) override; bool visit(FunctionDefinition const& _functionDefinition) override; void endVisit(FunctionDefinition const& _functionDefinition) override; bool visit(ParameterList const&) override { return true; } diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index c7f0c313fd18..f650e0dbf1dc 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -137,6 +137,9 @@ ASTPointer Parser::parse(CharStream& _charStream) case Token::Function: nodes.push_back(parseFunctionDefinition(true)); break; + case Token::ForAll: + nodes.push_back(parseQuantifiedFunctionDefinition()); + break; case Token::Event: nodes.push_back(parseEventDefinition()); break; @@ -627,6 +630,27 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari return result; } +ASTPointer Parser::parseQuantifiedFunctionDefinition() +{ + solAssert(m_experimentalSolidityEnabledInCurrentSourceUnit); + RecursionGuard recursionGuard(*this); + ASTNodeFactory nodeFactory(*this); + + expectToken(Token::ForAll); + ASTPointer typeVariableDeclarations = parseParameterList(); + nodeFactory.markEndPosition(); + + if (m_scanner->currentToken() != Token::Function) + fatalParserError(5709_error, "Expected a function definition."); + + ASTPointer quantifiedFunction = parseFunctionDefinition(true /* _freeFunction */, true /* _allowBody */); + + return nodeFactory.createNode( + std::move(typeVariableDeclarations), + std::move(quantifiedFunction) + ); +} + ASTPointer Parser::parseFunctionDefinition(bool _freeFunction, bool _allowBody) { RecursionGuard recursionGuard(*this); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 86dd2843fdbc..aedfdb7ddd37 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -102,6 +102,7 @@ class Parser: public langutil::ParserBase ASTPointer parseOverrideSpecifier(); StateMutability parseStateMutability(); FunctionHeaderParserResult parseFunctionHeader(bool _isStateVariable); + ASTPointer parseQuantifiedFunctionDefinition(); ASTPointer parseFunctionDefinition(bool _freeFunction = false, bool _allowBody = true); ASTPointer parseStructDefinition(); ASTPointer parseEnumDefinition(); diff --git a/test/libsolidity/syntaxTests/experimental/parsing/forall_free_function.sol b/test/libsolidity/syntaxTests/experimental/parsing/forall_free_function.sol new file mode 100644 index 000000000000..ba22a9fae4b4 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/parsing/forall_free_function.sol @@ -0,0 +1,27 @@ +pragma experimental solidity; + +forall (A) +function f(a: A) {} + +forall (A, B) +function g(a: A, b: B) {} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// Info 4164: (38-41): Inferred type: 's:type +// Info 4164: (39-40): Inferred type: 's:type +// Info 4164: (42-61): Inferred type: 's:type -> () +// Info 4164: (52-58): Inferred type: 's:type +// Info 4164: (53-57): Inferred type: 's:type +// Info 4164: (56-57): Inferred type: 's:type +// Info 4164: (70-76): Inferred type: ('v:type, 'w:type) +// Info 4164: (71-72): Inferred type: 'v:type +// Info 4164: (74-75): Inferred type: 'w:type +// Info 4164: (77-102): Inferred type: ('v:type, 'w:type) -> () +// Info 4164: (87-99): Inferred type: ('v:type, 'w:type) +// Info 4164: (88-92): Inferred type: 'v:type +// Info 4164: (91-92): Inferred type: 'v:type +// Info 4164: (94-98): Inferred type: 'w:type +// Info 4164: (97-98): Inferred type: 'w:type 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 new file mode 100644 index 000000000000..66753fdd0f68 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/parsing/forall_free_function_no_type_var.sol @@ -0,0 +1,14 @@ +pragma experimental solidity; + +forall () +function f(x: ()) {} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// Info 4164: (38-40): Inferred type: () +// Info 4164: (41-61): Inferred type: () -> () +// Info 4164: (51-58): Inferred type: () +// Info 4164: (52-57): Inferred type: () +// Info 4164: (55-57): Inferred type: () 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 new file mode 100644 index 000000000000..1ab09fd2bd24 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/parsing/forall_free_function_with_sorts.sol @@ -0,0 +1,43 @@ +pragma experimental solidity; + +class Self: Class1 {} +class Self: Class2 {} + +forall (A: (Class1, Class2), B: Class1) +function f(a: A: Class1, b: B: Class1) {} + +forall A: Class1 +function g(a: A) {} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: true +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// Info 4164: (31-52): Inferred type: Class1 +// Info 4164: (37-41): Inferred type: 'k:(type, Class1) +// Info 4164: (53-74): Inferred type: Class2 +// Info 4164: (59-63): Inferred type: 'l:(type, Class2) +// Info 4164: (83-115): Inferred type: ('ba:(type, Class1, Class2), 'bg:(type, Class1)) +// Info 4164: (84-103): Inferred type: 'ba:(type, Class1, Class2) +// Info 4164: (87-103): Inferred type: 'ba:(type, Class1, Class2) +// Info 4164: (88-94): Inferred type: 'ba:(type, Class1, Class2) +// Info 4164: (96-102): Inferred type: 'ba:(type, Class1, Class2) +// Info 4164: (105-114): Inferred type: 'bg:(type, Class1) +// Info 4164: (108-114): Inferred type: 'bg:(type, Class1) +// Info 4164: (116-157): Inferred type: ('ba:(type, Class1, Class2), 'bg:(type, Class1)) -> () +// Info 4164: (126-154): Inferred type: ('ba:(type, Class1, Class2), 'bg:(type, Class1)) +// Info 4164: (127-139): Inferred type: 'ba:(type, Class1, Class2) +// Info 4164: (130-139): Inferred type: 'ba:(type, Class1, Class2) +// Info 4164: (130-131): Inferred type: 'ba:(type, Class1, Class2) +// Info 4164: (133-139): Inferred type: 'ba:(type, Class1, Class2) +// Info 4164: (141-153): Inferred type: 'bg:(type, Class1) +// Info 4164: (144-153): Inferred type: 'bg:(type, Class1) +// Info 4164: (144-145): Inferred type: 'bg:(type, Class1) +// Info 4164: (147-153): Inferred type: 'bg:(type, Class1) +// Info 4164: (166-175): Inferred type: 'bi:(type, Class1) +// Info 4164: (166-175): Inferred type: 'bi:(type, Class1) +// Info 4164: (169-175): Inferred type: 'bi:(type, Class1) +// Info 4164: (176-195): Inferred type: 'bi:(type, Class1) -> () +// Info 4164: (186-192): Inferred type: 'bi:(type, Class1) +// Info 4164: (187-191): Inferred type: 'bi:(type, Class1) +// Info 4164: (190-191): Inferred type: 'bi:(type, Class1) diff --git a/test/libsolidity/syntaxTests/experimental/parsing/forall_type_class.sol b/test/libsolidity/syntaxTests/experimental/parsing/forall_type_class.sol new file mode 100644 index 000000000000..aa0012eac16f --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/parsing/forall_type_class.sol @@ -0,0 +1,8 @@ +pragma experimental solidity; + +forall (A, B) +class Self: C {} +// ==== +// 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 new file mode 100644 index 000000000000..31573447a369 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/parsing/forall_type_class_instantiation.sol @@ -0,0 +1,12 @@ +pragma experimental solidity; + +type T; + +class Self: C {} + +forall (A, B) +instantiation T: C {} +// ==== +// EVMVersion: >=constantinople +// ---- +// ParserError 5709: (72-85): Expected a function definition. From cd52e5aa9c282e75654709cd2890638e52561521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 6 Nov 2023 12:01:25 +0100 Subject: [PATCH 147/189] TypeInference: Require declarations for type variables --- libsolidity/analysis/NameAndTypeResolver.cpp | 31 ++++++-- .../experimental/analysis/TypeInference.cpp | 6 +- .../undeclared_type_variable_in_function.sol | 17 +++++ ...d_type_variable_in_quantified_function.sol | 12 ++++ ...undeclared_type_variable_in_type_class.sol | 17 +++++ .../inference/polymorphic_function_call.sol | 70 ++++++++++--------- .../inference/polymorphic_type.sol | 41 ++++++----- ...ulti_use_function_parameter_and_return.sol | 16 +++++ ...variable_multi_use_function_parameters.sol | 16 +++++ 9 files changed, 169 insertions(+), 57 deletions(-) create mode 100644 test/libsolidity/syntaxTests/experimental/forall/undeclared_type_variable_in_function.sol create mode 100644 test/libsolidity/syntaxTests/experimental/forall/undeclared_type_variable_in_quantified_function.sol create mode 100644 test/libsolidity/syntaxTests/experimental/forall/undeclared_type_variable_in_type_class.sol create mode 100644 test/libsolidity/syntaxTests/experimental/inference/type_variable_multi_use_function_parameter_and_return.sol create mode 100644 test/libsolidity/syntaxTests/experimental/inference/type_variable_multi_use_function_parameters.sol diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 94d0568118b9..642e87a53b3e 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -703,11 +703,34 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio solAssert(m_currentScope && m_scopes.count(m_currentScope), "No current scope."); solAssert(m_currentScope == _declaration.scope(), "Unexpected current scope."); - // Register declaration as inactive if we are in block scope. - bool inactive = - (dynamic_cast(m_currentScope) || dynamic_cast(m_currentScope)); + // Functions defined inside quantifiers should be visible in the scope containing the quantifier + // TODO: Turn it into a more generic mechanism in the same vein as Scopable and ScopeOpener if + // it turns out we need more special-casing here. + auto const* quantifier = dynamic_cast(m_currentScope); + auto const* functionDefinition = dynamic_cast(&_declaration); + if (quantifier && functionDefinition) + { + solAssert(quantifier->scope()); + solAssert( + // forall quantifiers cannot be used in block scope so the declaration is always active. + !dynamic_cast(quantifier->scope()) && + !dynamic_cast(quantifier->scope()) + ); - registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, inactive, m_errorReporter); + // NOTE: We're registering the function outside of its scope(). This will only affect + // name lookups. An more general alternative would be to modify Scoper to simply assign it + // that scope in the first place, but this would complicate the AST traversal here, which + // currently assumes that scopes follow ScopeOpener nesting. + registerDeclaration(*m_scopes.at(quantifier->scope()), _declaration, nullptr, nullptr, false /* inactive */, m_errorReporter); + } + else + { + // Register declaration as inactive if we are in block scope. + bool inactive = + (dynamic_cast(m_currentScope) || dynamic_cast(m_currentScope)); + + registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, inactive, m_errorReporter); + } solAssert(_declaration.annotation().scope == m_currentScope, ""); solAssert(_declaration.annotation().contract == m_currentContract, ""); diff --git a/libsolidity/experimental/analysis/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp index 8625937a35d0..6913dc0bf7cd 100644 --- a/libsolidity/experimental/analysis/TypeInference.cpp +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -580,9 +580,11 @@ bool TypeInference::visit(Identifier const& _identifier) solAssert(false); break; case ExpressionContext::Type: - // TODO: register free type variable name! + m_errorReporter.typeError(5934_error, _identifier.location(), "Undeclared type variable."); + + // Assign it a fresh variable anyway just so that we can continue analysis. identifierAnnotation.type = m_typeSystem.freshTypeVariable({}); - return false; + break; case ExpressionContext::Sort: // TODO: error handling solAssert(false); 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 new file mode 100644 index 000000000000..da1169969143 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/forall/undeclared_type_variable_in_function.sol @@ -0,0 +1,17 @@ +pragma experimental solidity; + +type T(X); + +function f(p: P, q: T(Q)) { + let r: (R, S); + let s: S; +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 5934: (57-58): Undeclared type variable. +// TypeError 5934: (65-66): Undeclared type variable. +// TypeError 5934: (83-84): Undeclared type variable. +// TypeError 5934: (86-87): Undeclared type variable. +// TypeError 5934: (101-102): Undeclared type variable. 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 new file mode 100644 index 000000000000..91b734569372 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/forall/undeclared_type_variable_in_quantified_function.sol @@ -0,0 +1,12 @@ +pragma experimental solidity; + +forall A +function f(b: B) { + let c: C; +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 5934: (54-55): Undeclared type variable. +// TypeError 5934: (70-71): Undeclared type variable. 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 new file mode 100644 index 000000000000..8af0cce82699 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/forall/undeclared_type_variable_in_type_class.sol @@ -0,0 +1,17 @@ +pragma experimental solidity; + +type T(X); + +class Self: C { + function f(self: Self); +} + +instantiation T(Y): C { + function f(self: T(Z)) {} +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 5934: (137-138): Undeclared type variable. +// TypeError 7428: (90-145): Instantiation function 'f' does not match the declaration in the type class (T('bc:type) -> () != T('y:type) -> ()). diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol index c73a857a2fcb..c9c7e01109e7 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_function_call.sol @@ -3,6 +3,7 @@ pragma experimental solidity; type T; type U(A); +forall (X, Y) function f(x, y: X, z: U(Y)) {} function run(a: T, b: U(T), c: U(U(T))) { @@ -18,36 +19,39 @@ function run(a: T, b: U(T), c: U(U(T))) { // Info 4164: (39-49): Inferred type: tfun('t:type, U('t:type)) // Info 4164: (45-48): Inferred type: 't:type // Info 4164: (46-47): Inferred type: 't:type -// Info 4164: (51-82): Inferred type: ('w:type, 'x:type, U('z:type)) -> () -// Info 4164: (61-79): Inferred type: ('w:type, 'x:type, U('z:type)) -// Info 4164: (62-63): Inferred type: 'w:type -// Info 4164: (65-69): Inferred type: 'x:type -// Info 4164: (68-69): Inferred type: 'x:type -// Info 4164: (71-78): Inferred type: U('z:type) -// Info 4164: (74-78): Inferred type: U('z:type) -// Info 4164: (74-75): Inferred type: tfun('z:type, U('z:type)) -// Info 4164: (76-77): Inferred type: 'z:type -// Info 4164: (84-159): Inferred type: (T, U(T), U(U(T))) -> () -// Info 4164: (96-123): Inferred type: (T, U(T), U(U(T))) -// Info 4164: (97-101): Inferred type: T -// Info 4164: (100-101): Inferred type: T -// Info 4164: (103-110): Inferred type: U(T) -// Info 4164: (106-110): Inferred type: U(T) -// Info 4164: (106-107): Inferred type: tfun(T, U(T)) -// Info 4164: (108-109): Inferred type: T -// Info 4164: (112-122): Inferred type: U(U(T)) -// Info 4164: (115-122): Inferred type: U(U(T)) -// Info 4164: (115-116): Inferred type: tfun(U(T), U(U(T))) -// Info 4164: (117-121): Inferred type: U(T) -// Info 4164: (117-118): Inferred type: tfun(T, U(T)) -// Info 4164: (119-120): Inferred type: T -// Info 4164: (130-140): Inferred type: () -// Info 4164: (130-131): Inferred type: (T, T, U(T)) -> () -// Info 4164: (132-133): Inferred type: T -// Info 4164: (135-136): Inferred type: T -// Info 4164: (138-139): Inferred type: U(T) -// Info 4164: (146-156): Inferred type: () -// Info 4164: (146-147): Inferred type: (U(T), U(T), U(U(T))) -> () -// Info 4164: (148-149): Inferred type: U(T) -// Info 4164: (151-152): Inferred type: U(T) -// Info 4164: (154-155): Inferred type: U(U(T)) +// Info 4164: (58-64): Inferred type: ('u:type, 'v:type) +// Info 4164: (59-60): Inferred type: 'u:type +// Info 4164: (62-63): Inferred type: 'v:type +// Info 4164: (65-96): Inferred type: ('y:type, 'u:type, U('v:type)) -> () +// Info 4164: (75-93): Inferred type: ('y:type, 'u:type, U('v:type)) +// Info 4164: (76-77): Inferred type: 'y:type +// Info 4164: (79-83): Inferred type: 'u:type +// Info 4164: (82-83): Inferred type: 'u:type +// Info 4164: (85-92): Inferred type: U('v:type) +// Info 4164: (88-92): Inferred type: U('v:type) +// Info 4164: (88-89): Inferred type: tfun('v:type, U('v:type)) +// Info 4164: (90-91): Inferred type: 'v:type +// Info 4164: (98-173): Inferred type: (T, U(T), U(U(T))) -> () +// Info 4164: (110-137): Inferred type: (T, U(T), U(U(T))) +// Info 4164: (111-115): Inferred type: T +// Info 4164: (114-115): Inferred type: T +// Info 4164: (117-124): Inferred type: U(T) +// Info 4164: (120-124): Inferred type: U(T) +// Info 4164: (120-121): Inferred type: tfun(T, U(T)) +// Info 4164: (122-123): Inferred type: T +// Info 4164: (126-136): Inferred type: U(U(T)) +// Info 4164: (129-136): Inferred type: U(U(T)) +// Info 4164: (129-130): Inferred type: tfun(U(T), U(U(T))) +// Info 4164: (131-135): Inferred type: U(T) +// Info 4164: (131-132): Inferred type: tfun(T, U(T)) +// Info 4164: (133-134): Inferred type: T +// Info 4164: (144-154): Inferred type: () +// Info 4164: (144-145): Inferred type: (T, T, U(T)) -> () +// Info 4164: (146-147): Inferred type: T +// Info 4164: (149-150): Inferred type: T +// Info 4164: (152-153): Inferred type: U(T) +// Info 4164: (160-170): Inferred type: () +// Info 4164: (160-161): Inferred type: (U(T), U(T), U(U(T))) -> () +// Info 4164: (162-163): Inferred type: U(T) +// Info 4164: (165-166): Inferred type: U(T) +// Info 4164: (168-169): Inferred type: U(U(T)) diff --git a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol index ca83ca2ec7fc..85f810a9dc0d 100644 --- a/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol +++ b/test/libsolidity/syntaxTests/experimental/inference/polymorphic_type.sol @@ -7,6 +7,7 @@ type V; class Self: C {} class Self: D {} +forall (X, Y, Z) function run() { let x: T(U, X, Z: C); let y: T(V, Y, Z: D); @@ -27,21 +28,25 @@ function run() { // Info 4164: (71-75): Inferred type: 'k:(type, C) // Info 4164: (82-98): Inferred type: D // Info 4164: (88-92): Inferred type: 'l:(type, D) -// Info 4164: (100-170): Inferred type: () -> () -// Info 4164: (112-114): Inferred type: () -// Info 4164: (125-141): Inferred type: T(U, ?bh:type, ?bj:(type, C)) -// Info 4164: (128-141): Inferred type: T(U, ?bh:type, ?bj:(type, C)) -// Info 4164: (128-129): Inferred type: tfun((U, ?bh:type, ?bj:(type, C)), T(U, ?bh:type, ?bj:(type, C))) -// Info 4164: (130-131): Inferred type: U -// Info 4164: (133-134): Inferred type: ?bh:type -// Info 4164: (136-140): Inferred type: ?bj:(type, C) -// Info 4164: (136-137): Inferred type: ?bj:(type, C) -// Info 4164: (139-140): Inferred type: ?bj:(type, C) -// Info 4164: (151-167): Inferred type: T(V, ?bo:type, ?bq:(type, D)) -// Info 4164: (154-167): Inferred type: T(V, ?bo:type, ?bq:(type, D)) -// Info 4164: (154-155): Inferred type: tfun((V, ?bo:type, ?bq:(type, D)), T(V, ?bo:type, ?bq:(type, D))) -// Info 4164: (156-157): Inferred type: V -// Info 4164: (159-160): Inferred type: ?bo:type -// Info 4164: (162-166): Inferred type: ?bq:(type, D) -// Info 4164: (162-163): Inferred type: ?bq:(type, D) -// Info 4164: (165-166): Inferred type: ?bq:(type, D) +// Info 4164: (107-116): Inferred type: (?bc:type, ?bd:type, ?bq:(type, C, D)) +// Info 4164: (108-109): Inferred type: ?bc:type +// Info 4164: (111-112): Inferred type: ?bd:type +// Info 4164: (114-115): Inferred type: ?bq:(type, C, D) +// Info 4164: (117-187): Inferred type: () -> () +// Info 4164: (129-131): Inferred type: () +// Info 4164: (142-158): Inferred type: T(U, ?bc:type, ?bq:(type, C, D)) +// Info 4164: (145-158): Inferred type: T(U, ?bc:type, ?bq:(type, C, D)) +// Info 4164: (145-146): Inferred type: tfun((U, ?bc:type, ?bq:(type, C, D)), T(U, ?bc:type, ?bq:(type, C, D))) +// Info 4164: (147-148): Inferred type: U +// Info 4164: (150-151): Inferred type: ?bc:type +// Info 4164: (153-157): Inferred type: ?bq:(type, C, D) +// Info 4164: (153-154): Inferred type: ?bq:(type, C, D) +// Info 4164: (156-157): Inferred type: ?bq:(type, C, D) +// Info 4164: (168-184): Inferred type: T(V, ?bd:type, ?bq:(type, C, D)) +// Info 4164: (171-184): Inferred type: T(V, ?bd:type, ?bq:(type, C, D)) +// Info 4164: (171-172): Inferred type: tfun((V, ?bd:type, ?bq:(type, C, D)), T(V, ?bd:type, ?bq:(type, C, D))) +// Info 4164: (173-174): Inferred type: V +// Info 4164: (176-177): Inferred type: ?bd:type +// Info 4164: (179-183): Inferred type: ?bq:(type, C, D) +// Info 4164: (179-180): Inferred type: ?bq:(type, C, D) +// Info 4164: (182-183): Inferred type: ?bq:(type, C, D) 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 new file mode 100644 index 000000000000..00cd7b1207df --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/type_variable_multi_use_function_parameter_and_return.sol @@ -0,0 +1,16 @@ +pragma experimental solidity; + +type T; +type U; + +forall X +function f(x: X) -> X {} + +function test(t: T, u: U) { + t = f(u); +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 8456: (115-123): Cannot unify T and U. 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 new file mode 100644 index 000000000000..7122cdd9efce --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/type_variable_multi_use_function_parameters.sol @@ -0,0 +1,16 @@ +pragma experimental solidity; + +type T; +type U; + +forall X +function f(x: X, y: X) {} + +function test(t: T, u: U) { + f(t, u); +} +// ==== +// EVMVersion: >=constantinople +// ---- +// Warning 2264: (0-29): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError 8456: (116-123): Cannot unify T and U. From 180f744c434dc6a5fbeb09715e194ab05a30bd88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 26 Feb 2024 19:44:15 +0100 Subject: [PATCH 148/189] gas_diff_stats.py: Sort the table by average change --- scripts/gas_diff_stats.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/scripts/gas_diff_stats.py b/scripts/gas_diff_stats.py index 13d8a59e8d76..e593bb9a9746 100755 --- a/scripts/gas_diff_stats.py +++ b/scripts/gas_diff_stats.py @@ -116,10 +116,16 @@ def parse_git_diff(fname): return None return collect_statistics(diff_output) - def stat(old, new): - if old == 0: + def percent(old, new): + return (int(new) - int(old)) / int(old) * 100 if int(old) != 0 else None + + def percent_or_zero(old, new): + result = percent(old, new) + return result if result is not None else 0 + + def format_percent(percentage): + if percentage is None: return '' - percentage = (new - old) / old * 100 prefix = ( # Distinguish actual zero from very small differences '+' if round(percentage) == 0 and percentage > 0 else @@ -128,6 +134,9 @@ def stat(old, new): ) return f'{prefix}{round(percentage)}%' + def stat(old, new): + return format_percent(percent(old, new)) + table = [] if not SEMANTIC_TEST_DIR.is_dir(): @@ -138,16 +147,24 @@ 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]) fname = f"`{fname.split('/', 3)[-1]}`" - table += [[fname, ir_optimized, legacy_optimized, legacy]] + 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]] + + 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"] - print(tabulate(sorted(table), headers=table_header, tablefmt="github")) + print(tabulate(sorted_table, headers=table_header, tablefmt="github")) print("
") else: print("No differences found.") From 125767ed17b9a6ab517aaf6ef910b7eaabe82fe0 Mon Sep 17 00:00:00 2001 From: Martin Blicha Date: Tue, 27 Feb 2024 17:21:34 +0100 Subject: [PATCH 149/189] SMTCommand: Remember to flush query before calling solver On MacOS I often experienced a problem that the solver reported SMT-LIB file as malformed. It appears that the whole query was not written to the file and using std::flush fixed the problem. --- Changelog.md | 1 + libsolidity/interface/SMTSolverCommand.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index b2ac59c65af8..33e2793d5c37 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Compiler Features: Bugfixes: * Assembler: Prevent incorrect calculation of tag sizes. * SMTChecker: Fix internal error caused by not respecting the sign of an integer type when constructing zero-value SMT expressions. + * SMTChecker: Ensure query is properly flushed to a file before calling solver when using SMT-LIB interface. ### 0.8.24 (2024-01-25) diff --git a/libsolidity/interface/SMTSolverCommand.cpp b/libsolidity/interface/SMTSolverCommand.cpp index 92e0485b84a7..d1c58c0c0a69 100644 --- a/libsolidity/interface/SMTSolverCommand.cpp +++ b/libsolidity/interface/SMTSolverCommand.cpp @@ -51,7 +51,7 @@ ReadCallback::Result SMTSolverCommand::solve(std::string const& _kind, std::stri auto queryFileName = tempDir.path() / ("query_" + queryHash.hex() + ".smt2"); auto queryFile = boost::filesystem::ofstream(queryFileName); - queryFile << _query; + queryFile << _query << std::flush; auto eldBin = boost::process::search_path(m_solverCmd); From 51eff63222ae7866ebbda2cc280c3291e74ac719 Mon Sep 17 00:00:00 2001 From: ByeongSu Go <107296751+eclipse1228@users.noreply.github.com> Date: Thu, 29 Feb 2024 18:45:48 +0900 Subject: [PATCH 150/189] Update introduction-to-smart-contracts.rst (#14884) --- docs/introduction-to-smart-contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index c4cc1201b02a..614cb275d4cd 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -156,7 +156,7 @@ creates a public state variable, but it is a more complex datatype. The :ref:`mapping ` type maps addresses to :ref:`unsigned integers `. Mappings can be seen as `hash tables `_ which are -virtually initialised such that every possible key exists from the start and is mapped to a +virtually initialized such that every possible key exists from the start and is mapped to a value whose byte-representation is all zeros. However, it is neither possible to obtain a list of all keys of a mapping, nor a list of all values. Record what you added to the mapping, or use it in a context where this is not needed. Or From e7aaf03d57e560d29a6c3e7ae631aba7c3fbccf7 Mon Sep 17 00:00:00 2001 From: Vojtch Date: Thu, 29 Feb 2024 10:58:53 +0100 Subject: [PATCH 151/189] docs: Replace references to "mining" with "attestation" (#14147) * docs: fix old wording Updated mining to attestation, but maybe it doesn't even make sense to put it like this. This is for the maintainers to decide but maybe it'd make sense to remove "(which is called "attestation")" altogether and just add a link to more information about LMD-GHOST. * Update docs/introduction-to-smart-contracts.rst --------- Co-authored-by: Vishwa Mehta --- docs/introduction-to-smart-contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 614cb275d4cd..3c43e5f193b2 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -304,7 +304,7 @@ These blocks form a linear sequence in time, and that is where the word "blockch Blocks are added to the chain at regular intervals, although these intervals may be subject to change in the future. For the most up-to-date information, it is recommended to monitor the network, for example, on `Etherscan `_. -As part of the "order selection mechanism" (which is called "mining") it may happen that +As part of the "order selection mechanism", which is called `attestation `_, it may happen that blocks are reverted from time to time, but only at the "tip" of the chain. The more blocks are added on top of a particular block, the less likely this block will be reverted. So it might be that your transactions are reverted and even removed from the blockchain, but the longer you wait, the less From 9bd2424cffbd7b21ee37b736c7c339f606bd6667 Mon Sep 17 00:00:00 2001 From: Chomtana Date: Thu, 29 Feb 2024 17:02:31 +0700 Subject: [PATCH 152/189] Fix typos in variable names and comment (#14876) * Fix word determinsm -> determinism * Fix word Optimzation -> Optimization * Fix word affilate -> affiliate * fix: import_base assertion --- libyul/optimiser/StackCompressor.cpp | 6 ++--- test/compilationTests/corion/ico.sol | 22 +++++++++---------- .../smtCheckerTests/imports/import_base.sol | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/libyul/optimiser/StackCompressor.cpp b/libyul/optimiser/StackCompressor.cpp index 87a5e411c5e2..58552147a608 100644 --- a/libyul/optimiser/StackCompressor.cpp +++ b/libyul/optimiser/StackCompressor.cpp @@ -253,7 +253,7 @@ bool StackCompressor::run( _optimizeStackAllocation && evmDialect->evmVersion().canOverchargeGasForCall() && evmDialect->providesObjectAccess(); - bool allowMSizeOptimzation = !MSizeFinder::containsMSize(_dialect, *_object.code); + bool allowMSizeOptimization = !MSizeFinder::containsMSize(_dialect, *_object.code); if (usesOptimizedCodeGenerator) { yul::AsmAnalysisInfo analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(_dialect, _object); @@ -262,7 +262,7 @@ bool StackCompressor::run( _dialect, *_object.code, StackLayoutGenerator::reportStackTooDeep(*cfg), - allowMSizeOptimzation + allowMSizeOptimization ); } else @@ -275,7 +275,7 @@ bool StackCompressor::run( _dialect, *_object.code, stackSurplus, - allowMSizeOptimzation + allowMSizeOptimization ); } return false; diff --git a/test/compilationTests/corion/ico.sol b/test/compilationTests/corion/ico.sol index 36510ea65843..35960fe43a44 100644 --- a/test/compilationTests/corion/ico.sol +++ b/test/compilationTests/corion/ico.sol @@ -284,7 +284,7 @@ contract ico is safeMath { require( buy(payable(msg.sender), address(0x00)) ); } - function buy(address payable beneficiaryAddress, address affilateAddress) public payable returns (bool success) { + function buy(address payable beneficiaryAddress, address affiliateAddress) public payable returns (bool success) { /* Buying a token @@ -297,12 +297,12 @@ contract ico is safeMath { More than 1e10 token: 3% More than 1e9 token: 2% below 1% @beneficiaryAddress The address of the accredited where the token will be sent. - @affilateAddress The address of the person who offered who will get the referral reward. It can not be equal with the beneficiaryAddress. + @affiliateAddress The address of the person who offered who will get the referral reward. It can not be equal with the beneficiaryAddress. */ require( isICO() ); if ( beneficiaryAddress == address(0x00)) { beneficiaryAddress = payable(msg.sender); } - if ( beneficiaryAddress == affilateAddress ) { - affilateAddress = address(0x00); + if ( beneficiaryAddress == affiliateAddress ) { + affiliateAddress = address(0x00); } uint256 _value = msg.value; if ( beneficiaryAddress.balance < 0.2 ether ) { @@ -317,9 +317,9 @@ contract ico is safeMath { totalMint = safeAdd(totalMint, _reward); require( foundationAddress.send(_value * 10 / 100) ); uint256 extra; - if ( affilateAddress != address(0x00) && ( brought[affilateAddress].eth > 0 || interestDB[affilateAddress][0].amount > 0 ) ) { - affiliate[affilateAddress].weight = safeAdd(affiliate[affilateAddress].weight, _reward); - extra = affiliate[affilateAddress].weight; + if ( affiliateAddress != address(0x00) && ( brought[affiliateAddress].eth > 0 || interestDB[affiliateAddress][0].amount > 0 ) ) { + affiliate[affiliateAddress].weight = safeAdd(affiliate[affiliateAddress].weight, _reward); + extra = affiliate[affiliateAddress].weight; uint256 rate; if (extra >= 1e12) { rate = 5; @@ -332,12 +332,12 @@ contract ico is safeMath { } else { rate = 1; } - extra = safeSub(extra * rate / 100, affiliate[affilateAddress].paid); - affiliate[affilateAddress].paid = safeAdd(affiliate[affilateAddress].paid, extra); - token(tokenAddr).mint(affilateAddress, extra); + extra = safeSub(extra * rate / 100, affiliate[affiliateAddress].paid); + affiliate[affiliateAddress].paid = safeAdd(affiliate[affiliateAddress].paid, extra); + token(tokenAddr).mint(affiliateAddress, extra); } checkPremium(beneficiaryAddress); - emit EICO(beneficiaryAddress, _reward, affilateAddress, extra); + emit EICO(beneficiaryAddress, _reward, affiliateAddress, extra); return true; } diff --git a/test/libsolidity/smtCheckerTests/imports/import_base.sol b/test/libsolidity/smtCheckerTests/imports/import_base.sol index 3e8aeefe4845..fb6ed2ee4800 100644 --- a/test/libsolidity/smtCheckerTests/imports/import_base.sol +++ b/test/libsolidity/smtCheckerTests/imports/import_base.sol @@ -12,7 +12,7 @@ contract Base { import "base"; contract Der is Base { function g(uint y) public { - require(x < 10); // added to restrict the search space and avoid non-determinsm in Spacer + require(x < 10); // added to restrict the search space and avoid non-determinism in Spacer x += f(); assert(y > x); } @@ -20,5 +20,5 @@ contract Der is Base { // ==== // SMTEngine: all // ---- -// Warning 6328: (der:173-186): CHC: Assertion violation happens here. +// Warning 6328: (der:174-187): 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 7676ab1014b9fe6bd4314cb648ef790933992e7f Mon Sep 17 00:00:00 2001 From: Aditya Kode <105551807+adityakode@users.noreply.github.com> Date: Thu, 29 Feb 2024 15:38:54 +0530 Subject: [PATCH 153/189] Update natspec-format.rst : Added structs and enums to Tags (#14267) * Update natspec-format.rst * Update docs/natspec-format.rst --------- Co-authored-by: Vishwa Mehta --- docs/natspec-format.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/natspec-format.rst b/docs/natspec-format.rst index 0265fef3e689..f92958b90c02 100644 --- a/docs/natspec-format.rst +++ b/docs/natspec-format.rst @@ -115,10 +115,10 @@ in the same way as if it were tagged with ``@notice``. =============== ====================================================================================== ============================= Tag Context =============== ====================================================================================== ============================= -``@title`` A title that should describe the contract/interface contract, library, interface -``@author`` The name of the author contract, library, interface -``@notice`` Explain to an end user what this does contract, library, interface, function, public state variable, event -``@dev`` Explain to a developer any extra details contract, library, interface, function, state variable, event +``@title`` A title that should describe the contract/interface contract, library, interface, struct, enum +``@author`` The name of the author contract, library, interface, struct, enum +``@notice`` Explain to an end user what this does contract, library, interface, function, public state variable, event, struct, enum +``@dev`` Explain to a developer any extra details contract, library, interface, function, state variable, event, struct, enum ``@param`` Documents a parameter just like in Doxygen (must be followed by parameter name) function, event ``@return`` Documents the return variables of a contract's function function, public state variable ``@inheritdoc`` Copies all missing tags from the base function (must be followed by the contract name) function, public state variable From 6b417ddf521dd8620fc00b7bebb14ede24b3d747 Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Fri, 1 Mar 2024 19:40:14 +0100 Subject: [PATCH 154/189] [evmasm] Fix handling of missing source locations during import. --- Changelog.md | 1 + libevmasm/Assembly.cpp | 6 ++++-- .../asm_json_import_all_valid_flags/output | 20 +++++++++---------- .../output | 1 - .../output | 1 - .../output | 1 - 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Changelog.md b/Changelog.md index 33e2793d5c37..c279531cfe8c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ Compiler Features: Bugfixes: * Assembler: Prevent incorrect calculation of tag sizes. + * EVM Assembly Import: Fix handling of missing source locations during import. * SMTChecker: Fix internal error caused by not respecting the sign of an integer type when constructing zero-value SMT expressions. * SMTChecker: Ensure query is properly flushed to a file before calling solver when using SMT-LIB interface. diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 569ecda26a84..067e230343d0 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -124,8 +124,10 @@ AssemblyItem Assembly::createAssemblyItemFromJSON(Json::Value const& _json, std: solRequire(!name.empty(), AssemblyImportException, "Member 'name' is empty."); SourceLocation location; - location.start = get(_json["begin"]); - location.end = get(_json["end"]); + if (_json.isMember("begin")) + location.start = get(_json["begin"]); + if (_json.isMember("end")) + location.end = get(_json["end"]); int srcIndex = getOrDefault(_json["source"], -1); size_t modifierDepth = static_cast(getOrDefault(_json["modifierDepth"], 0)); std::string value = getOrDefault(_json["value"], ""); diff --git a/test/cmdlineTests/asm_json_import_all_valid_flags/output b/test/cmdlineTests/asm_json_import_all_valid_flags/output index 7b8aa0297982..837187d195ae 100644 --- a/test/cmdlineTests/asm_json_import_all_valid_flags/output +++ b/test/cmdlineTests/asm_json_import_all_valid_flags/output @@ -8,8 +8,8 @@ ".code": [ { - "begin": 0, - "end": 0, + "begin": -1, + "end": -1, "name": "PUSH", "source": -1, "value": "0" @@ -22,8 +22,8 @@ ".code": [ { - "begin": 0, - "end": 0, + "begin": -1, + "end": -1, "name": "PUSH", "source": -1, "value": "1" @@ -40,8 +40,8 @@ "bin": "5ffe", "bin-runtime": "6001", "opcodes": "PUSH0 INVALID ", - "srcmap": "0:0::-:0", - "srcmap-runtime": "0:0::-:0" + "srcmap": ":::-:0", + "srcmap-runtime": ":::-:0" } }, "sourceList": @@ -60,8 +60,8 @@ EVM assembly: ".code": [ { - "begin": 0, - "end": 0, + "begin": -1, + "end": -1, "name": "PUSH", "source": -1, "value": "0" @@ -74,8 +74,8 @@ EVM assembly: ".code": [ { - "begin": 0, - "end": 0, + "begin": -1, + "end": -1, "name": "PUSH", "source": -1, "value": "1" diff --git a/test/cmdlineTests/asm_json_import_difficulty_prevrandao/output b/test/cmdlineTests/asm_json_import_difficulty_prevrandao/output index 3d19f83d668f..1713fb49b6cc 100644 --- a/test/cmdlineTests/asm_json_import_difficulty_prevrandao/output +++ b/test/cmdlineTests/asm_json_import_difficulty_prevrandao/output @@ -1,6 +1,5 @@ Opcodes: PREVRANDAO PREVRANDAO EVM assembly: - /* */ prevrandao prevrandao diff --git a/test/cmdlineTests/asm_json_import_hex_subassembly_indices/output b/test/cmdlineTests/asm_json_import_hex_subassembly_indices/output index 873465aa1f3f..60f114ba68e0 100644 --- a/test/cmdlineTests/asm_json_import_hex_subassembly_indices/output +++ b/test/cmdlineTests/asm_json_import_hex_subassembly_indices/output @@ -1,7 +1,6 @@ Opcodes: PUSH0 INVALID EVM assembly: - /* */ 0x00 stop diff --git a/test/cmdlineTests/asm_json_import_random_order_data_index/output b/test/cmdlineTests/asm_json_import_random_order_data_index/output index b23848e5ddcb..ab2f02dc7c84 100644 --- a/test/cmdlineTests/asm_json_import_random_order_data_index/output +++ b/test/cmdlineTests/asm_json_import_random_order_data_index/output @@ -1,7 +1,6 @@ Opcodes: PUSH0 INVALID EVM assembly: - /* */ 0x00 stop From b630c0a415fd49e6fdc398bc9fef38ea3cef2a0e Mon Sep 17 00:00:00 2001 From: minaminao Date: Thu, 27 Jul 2023 13:38:27 +0900 Subject: [PATCH 155/189] fix: rename RedundantAssignEliminator to UnusedAssignEliminator --- docs/internals/optimizer.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index 70896333c2bc..0a160d61199e 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -298,7 +298,7 @@ The following transformation steps are the main components: - SSA Transform - Common Subexpression Eliminator - Expression Simplifier -- Redundant Assign Eliminator +- Unused Assign Eliminator - Full Inliner .. _optimizer-steps: @@ -335,11 +335,11 @@ Abbreviation Full name ``T`` :ref:`literal-rematerialiser` ``L`` :ref:`load-resolver` ``M`` :ref:`loop-invariant-code-motion` -``r`` :ref:`redundant-assign-eliminator` ``m`` :ref:`rematerialiser` ``V`` :ref:`SSA-reverser` ``a`` :ref:`SSA-transform` ``t`` :ref:`structural-simplifier` +``r`` :ref:`unused-assign-eliminator` ``p`` :ref:`unused-function-parameter-pruner` ``S`` :ref:`unused-store-eliminator` ``u`` :ref:`unused-pruner` @@ -652,7 +652,7 @@ the block, a new SSA variable will be created at the location where control flow this includes the beginning of loop post/body block and the location right after If/Switch/ForLoop/Block statement. -After this stage, the Redundant Assign Eliminator is recommended to remove the unnecessary +After this stage, the Unused Assign Eliminator is recommended to remove the unnecessary intermediate assignments. This stage provides best results if the Expression Splitter and the Common Subexpression Eliminator @@ -660,10 +660,10 @@ are run right before it, because then it does not generate excessive amounts of On the other hand, the Common Subexpression Eliminator could be more efficient if run after the SSA transform. -.. _redundant-assign-eliminator: +.. _unused-assign-eliminator: -RedundantAssignEliminator -^^^^^^^^^^^^^^^^^^^^^^^^^ +UnusedAssignEliminator +^^^^^^^^^^^^^^^^^^^^^^ The SSA transform always generates an assignment of the form ``a := a_i``, even though these might be unnecessary in many cases, like the following example: @@ -691,7 +691,7 @@ The SSA transform converts this snippet to the following: sstore(a_3, 1) } -The Redundant Assign Eliminator removes all the three assignments to ``a``, because +The Unused Assign Eliminator removes all the three assignments to ``a``, because the value of ``a`` is not used and thus turn this snippet into strict SSA form: @@ -704,7 +704,7 @@ snippet into strict SSA form: sstore(a_3, 1) } -Of course the intricate parts of determining whether an assignment is redundant or not +Of course the intricate parts of determining whether an assignment is unused or not are connected to joining control flow. The component works as follows in detail: @@ -829,7 +829,7 @@ current value if the value is an identifier. The combination of the two rules above allow to compute a local value numbering, which means that if two variables have the same value, one of them will always be unused. The Unused Pruner or the -Redundant Assign Eliminator will then be able to fully eliminate such +Unused Assign Eliminator will then be able to fully eliminate such variables. This step is especially efficient if the expression splitter is run From 8709ae9304a58f22053d74c9f08100360696a5c5 Mon Sep 17 00:00:00 2001 From: minaminao Date: Thu, 17 Aug 2023 12:29:00 +0900 Subject: [PATCH 156/189] fix: typos --- docs/bugs.rst | 2 +- docs/cheatsheet.rst | 2 +- docs/contracts/inheritance.rst | 2 +- docs/contributing.rst | 4 ++-- docs/installing-solidity.rst | 16 ++++++++-------- docs/internals/optimizer.rst | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/bugs.rst b/docs/bugs.rst index 350b1e7a48e8..f7f18e9a7f86 100644 --- a/docs/bugs.rst +++ b/docs/bugs.rst @@ -7,7 +7,7 @@ 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 +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. diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index 2b68d7311982..e4f7beabd5f5 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -159,7 +159,7 @@ Modifiers - ``pure`` for functions: Disallows modification or access of state. - ``view`` for functions: Disallows modification of state. - ``payable`` for functions: Allows them to receive Ether together with a call. -- ``constant`` for state variables: Disallows assignment (except initialisation), does not occupy storage slot. +- ``constant`` for state variables: Disallows assignment (except initialization), does not occupy storage slot. - ``immutable`` for state variables: Allows assignment at construction time and is constant when deployed. Is stored in code. - ``anonymous`` for events: Does not store event signature as topic. - ``indexed`` for event parameters: Stores the parameter as topic. diff --git a/docs/contracts/inheritance.rst b/docs/contracts/inheritance.rst index c787c5af6a90..02ab19163ffb 100644 --- a/docs/contracts/inheritance.rst +++ b/docs/contracts/inheritance.rst @@ -398,7 +398,7 @@ Constructors A constructor is an optional function declared with the ``constructor`` keyword which is executed upon contract creation, and where you can run contract -initialisation code. +initialization code. Before the constructor code is executed, state variables are initialised to their specified value if you initialise them inline, or their :ref:`default value` if you do not. diff --git a/docs/contributing.rst b/docs/contributing.rst index ac4e2375ebdc..8ce402680bb1 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -128,7 +128,7 @@ for the ``evmone`` shared object can be specified via the ``ETH_EVMONE`` environ If you do not have it installed, you can skip these tests by passing the ``--no-semantic-tests`` flag to ``scripts/soltest.sh``. -The ``evmone`` library should both end with the file name +The ``evmone`` library should end with the file name extension ``.so`` on Linux, ``.dll`` on Windows systems and ``.dylib`` on macOS. For running SMT tests, the ``libz3`` library must be installed and locatable @@ -552,7 +552,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 or 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 or 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 `_. diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 2f048fe3c104..ab688a8f448c 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -83,14 +83,14 @@ and runs it in a new container, passing the ``--help`` argument. docker run ethereum/solc:stable --help -For example, You can specify release build versions in the tag for the 0.5.4 release. +You can specify release build versions in the tag. For example: .. code-block:: bash docker run ethereum/solc:0.5.4 --help To use the Docker image to compile Solidity files on the host machine, mount a -local folder for input and output, and specify the contract to compile. For example. +local folder for input and output, and specify the contract to compile. For example: .. code-block:: bash @@ -183,7 +183,7 @@ If you need a specific version of Solidity you can install a Homebrew formula directly from Github. View -`solidity.rb commits on Github `_. +`solidity.rb commits on GitHub `_. Copy the commit hash of the version you want and check it out on your machine. @@ -224,8 +224,8 @@ out-of-the-box but it is also meant to be friendly to third-party tools: (via git, HTTPS, IPFS or just have it cached locally) and verify hashes of the binaries after downloading them, you do not have to use HTTPS for the binaries themselves. -The same binaries are in most cases available on the `Solidity release page on Github`_. The -difference is that we do not generally update old releases on the Github release page. This means +The same binaries are in most cases available on the `Solidity release page on GitHub`_. The +difference is that we do not generally update old releases on the GitHub release page. This means that we do not rename them if the naming convention changes and we do not add builds for platforms that were not supported at the time of release. This only happens in ``solc-bin``. @@ -301,7 +301,7 @@ This means that: .. _IPFS: https://ipfs.io .. _Swarm: https://swarm-gateways.net/bzz:/swarm.eth .. _solc-bin: https://github.com/ethereum/solc-bin/ -.. _Solidity release page on github: https://github.com/ethereum/solidity/releases +.. _Solidity release page on GitHub: https://github.com/ethereum/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 @@ -461,11 +461,11 @@ you should fork Solidity and add your personal fork as a second remote: This method will result in a pre-release build leading to e.g. a flag being set in each bytecode produced by such a compiler. If you want to re-build a released Solidity compiler, then - please use the source tarball on the github release page: + 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 - (not the "Source code" provided by github). + (not the "Source code" provided by GitHub). Command-Line Build ------------------ diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index 0a160d61199e..4588cb48358c 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -482,7 +482,7 @@ is transformed to } This eases the rest of the optimization process because we can ignore -the complicated scoping rules of the for loop initialisation block. +the complicated scoping rules of the for loop initialization block. .. _var-decl-initializer: @@ -1217,7 +1217,7 @@ The FullInliner replaces certain calls of certain functions by the function's body. This is not very helpful in most cases, because it just increases the code size but does not have a benefit. Furthermore, code is usually very expensive and we would often rather have shorter -code than more efficient code. In same cases, though, inlining a function +code than more efficient code. In some cases, though, inlining a function can have positive effects on subsequent optimizer steps. This is the case if one of the function arguments is a constant, for example. From f96a8514ad8a4119b793a54227ef7e83e2fbdb85 Mon Sep 17 00:00:00 2001 From: minaminao Date: Thu, 17 Aug 2023 12:31:32 +0900 Subject: [PATCH 157/189] chore: update example version of solidity in docker command --- docs/installing-solidity.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index ab688a8f448c..123f17422d7e 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -87,7 +87,7 @@ You can specify release build versions in the tag. For example: .. code-block:: bash - docker run ethereum/solc:0.5.4 --help + docker run ethereum/solc:0.8.21 --help To use the Docker image to compile Solidity files on the host machine, mount a local folder for input and output, and specify the contract to compile. For example: From 0225d9d336578e33480432b651ed02d2266f024c Mon Sep 17 00:00:00 2001 From: Vishwa Mehta Date: Fri, 1 Mar 2024 22:46:20 +0530 Subject: [PATCH 158/189] Update installing-solidity.rst --- docs/installing-solidity.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 123f17422d7e..4e0948189d01 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -87,7 +87,12 @@ You can specify release build versions in the tag. For example: .. code-block:: bash - docker run ethereum/solc:0.8.21 --help + docker run ethereum/solc:stable --help + +Note: Specific version tags are supported for the Docker tag such as `docker run ethereum/solc:0.8.23 --help`. + +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. To use the Docker image to compile Solidity files on the host machine, mount a local folder for input and output, and specify the contract to compile. For example: From 51b697676c47b775370c727094106939092fea92 Mon Sep 17 00:00:00 2001 From: Vishwa Mehta Date: Fri, 1 Mar 2024 22:53:45 +0530 Subject: [PATCH 159/189] Update installing-solidity.rst --- docs/installing-solidity.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 4e0948189d01..bc20ae346168 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -89,10 +89,11 @@ You can specify release build versions in the tag. For example: docker run ethereum/solc:stable --help -Note: Specific version tags are supported for the Docker tag such as `docker run ethereum/solc:0.8.23 --help`. +Note -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. +Specific version tags are supported for the Docker tag such as `docker run ethereum/solc:0.8.23 --help`. 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. To use the Docker image to compile Solidity files on the host machine, mount a local folder for input and output, and specify the contract to compile. For example: From 8f0cb8a7afb59b653b1dcf334b16243023934309 Mon Sep 17 00:00:00 2001 From: Vishwa Mehta Date: Mon, 4 Mar 2024 12:41:49 +0530 Subject: [PATCH 160/189] Update docs/installing-solidity.rst Co-authored-by: r0qs --- docs/installing-solidity.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index bc20ae346168..8d5b421d3a19 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -91,7 +91,7 @@ You can specify release build versions in the tag. For example: Note -Specific version tags are supported for the Docker tag such as `docker run ethereum/solc:0.8.23 --help`. We will be passing the +Specific compiler versions are supported as the Docker image tag such as `ethereum/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. From cbd84372da1d6e02a5a023b46a1921eefccb2abd Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Mon, 4 Mar 2024 14:53:53 +0100 Subject: [PATCH 161/189] Fix python snippet causing "SyntaxWarning" in Scanner. --- test/cmdlineTests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 3166cd6b5dd0..4f7b88fe974f 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -216,7 +216,7 @@ import re, sys json = open("$stdout_path", "r").read() json = re.sub(r"{[^{}]*Warning: This is a pre-release compiler version[^{}]*},?", "", json) json = re.sub(r"\"errors\":\s*\[\s*\],?","\n" if json[1] == " " else "",json) # Remove "errors" array if it's not empty -json = re.sub("\n\\s*\n", "\n", json) # Remove trailing whitespace +json = re.sub(r"\n\s*\n", "\n", json) # Remove trailing whitespace json = re.sub(r"},(\n{0,1})\n*(\s*(]|}))", r"}\1\2", json) # Remove trailing comma open("$stdout_path", "w").write(json) EOF From a6830fd939e7be9f0406761da4a5c51f398bd5ef Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Wed, 28 Feb 2024 22:52:25 -0300 Subject: [PATCH 162/189] Emit transient storage warning only once --- Changelog.md | 1 + liblangutil/ErrorReporter.cpp | 11 +++++++++ liblangutil/ErrorReporter.h | 3 +++ libyul/AsmAnalysis.cpp | 3 ++- ...e_warning_only_once_multiple_contracts.sol | 23 +++++++++++++++++++ .../tstore_warning_only_once.yul | 10 ++++++++ 6 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/tstore_warning_only_once_multiple_contracts.sol create mode 100644 test/libyul/yulSyntaxTests/tstore_warning_only_once.yul diff --git a/Changelog.md b/Changelog.md index c279531cfe8c..6defb4d72b53 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Language Features: Compiler Features: * Code Generator: Use ``MCOPY`` instead of ``MLOAD``/``MSTORE`` loop when copying byte arrays. + * Yul Analyzer: Emit transient storage warning only for the first occurrence of ``tstore``. Bugfixes: diff --git a/liblangutil/ErrorReporter.cpp b/liblangutil/ErrorReporter.cpp index 298e4f630f10..bf10483aa5a2 100644 --- a/liblangutil/ErrorReporter.cpp +++ b/liblangutil/ErrorReporter.cpp @@ -23,6 +23,7 @@ #include #include +#include #include using namespace solidity; @@ -81,6 +82,16 @@ bool ErrorReporter::hasExcessiveErrors() const return m_errorCount > c_maxErrorsAllowed; } +bool ErrorReporter::hasError(ErrorId _errorId) const +{ + auto errorMatch = [&](std::shared_ptr const& error) -> bool { + solAssert(error.get()); + return error->errorId() == _errorId; + }; + + return ranges::find_if(m_errorList, errorMatch) != ranges::end(m_errorList); +} + bool ErrorReporter::checkForExcessiveErrors(Error::Type _type) { if (_type == Error::Type::Warning) diff --git a/liblangutil/ErrorReporter.h b/liblangutil/ErrorReporter.h index 6901e87b94f8..c78b6f498d2e 100644 --- a/liblangutil/ErrorReporter.h +++ b/liblangutil/ErrorReporter.h @@ -137,6 +137,9 @@ class ErrorReporter // @returns true if the maximum error count has been reached. bool hasExcessiveErrors() const; + /// @returns true if there is at least one occurrence of error + bool hasError(ErrorId _errorId) const; + class ErrorWatcher { public: diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index cc811f75d182..0efaa12c2c8a 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -324,7 +324,8 @@ std::vector AsmAnalyzer::operator()(FunctionCall const& _funCall) ); else if ( m_evmVersion.supportsTransientStorage() && - _funCall.functionName.name == "tstore"_yulstring + _funCall.functionName.name == "tstore"_yulstring && + !m_errorReporter.hasError({2394}) ) m_errorReporter.warning( 2394_error, diff --git a/test/libsolidity/syntaxTests/inlineAssembly/tstore_warning_only_once_multiple_contracts.sol b/test/libsolidity/syntaxTests/inlineAssembly/tstore_warning_only_once_multiple_contracts.sol new file mode 100644 index 000000000000..5b05831f659b --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/tstore_warning_only_once_multiple_contracts.sol @@ -0,0 +1,23 @@ +contract C { + function f() external { + assembly { + tstore(0, 0) + let a := tload(0) + tstore(0, 1) + tstore(1, a) + } + } +} + +contract D { + function g() external { + assembly { + tstore(0, 0) + tstore(0, 1) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// Warning 2394: (72-78): Transient storage as defined by EIP-1153 can break the composability of smart contracts: Since transient storage is cleared only at the end of the transaction and not at the end of the outermost call frame to the contract within a transaction, your contract may unintentionally misbehave when invoked multiple times in a complex transaction. To avoid this, be sure to clear all transient storage at the end of any call to your contract. The use of transient storage for reentrancy guards that are cleared at the end of the call is safe. diff --git a/test/libyul/yulSyntaxTests/tstore_warning_only_once.yul b/test/libyul/yulSyntaxTests/tstore_warning_only_once.yul new file mode 100644 index 000000000000..44a6b59b49ab --- /dev/null +++ b/test/libyul/yulSyntaxTests/tstore_warning_only_once.yul @@ -0,0 +1,10 @@ +{ + tstore(0, 0) + tstore(0, 1) + let x := tload(0) + tstore(1, 0) +} +// ==== +// EVMVersion: >=cancun +// ---- +// Warning 2394: (6-12): Transient storage as defined by EIP-1153 can break the composability of smart contracts: Since transient storage is cleared only at the end of the transaction and not at the end of the outermost call frame to the contract within a transaction, your contract may unintentionally misbehave when invoked multiple times in a complex transaction. To avoid this, be sure to clear all transient storage at the end of any call to your contract. The use of transient storage for reentrancy guards that are cleared at the end of the call is safe. From ad77fd7c67fcff25e5963b6398a909a67220725a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 6 Feb 2024 14:39:21 +0100 Subject: [PATCH 163/189] EVMHost: Rename newlyCreatedAccounts to m_newlyCreatedAccounts --- test/EVMHost.cpp | 6 +++--- test/EVMHost.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index c43d53d6bc43..b4005e16eabb 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -198,12 +198,12 @@ void EVMHost::newTransactionFrame() } // Process selfdestruct list for (auto& [address, _]: recorded_selfdestructs) - if (m_evmVersion < langutil::EVMVersion::cancun() || newlyCreatedAccounts.count(address)) + if (m_evmVersion < langutil::EVMVersion::cancun() || m_newlyCreatedAccounts.count(address)) // EIP-6780: If SELFDESTRUCT is executed in a transaction different from the one // in which it was created, we do NOT record it or clear any data. // Otherwise, the previous behavior (pre-Cancun) is maintained. accounts.erase(address); - newlyCreatedAccounts.clear(); + m_newlyCreatedAccounts.clear(); m_totalCodeDepositGas = 0; recorded_selfdestructs.clear(); } @@ -358,7 +358,7 @@ evmc::Result EVMHost::call(evmc_message const& _message) noexcept auto& destination = accounts[message.recipient]; if (message.kind == EVMC_CREATE || message.kind == EVMC_CREATE2) // Mark account as created if it is a CREATE or CREATE2 call - newlyCreatedAccounts.emplace(message.recipient); + m_newlyCreatedAccounts.emplace(message.recipient); if (value != 0 && message.kind != EVMC_DELEGATECALL && message.kind != EVMC_CALLCODE) { diff --git a/test/EVMHost.h b/test/EVMHost.h index 848314c87974..121adbc15945 100644 --- a/test/EVMHost.h +++ b/test/EVMHost.h @@ -131,15 +131,15 @@ class EVMHost: public evmc::MockedHost static evmc::Result resultWithGas(int64_t gas_limit, int64_t gas_required, bytes const& _data) noexcept; static evmc::Result resultWithFailure() noexcept; - /// Store the accounts that have been created in the current transaction. - std::unordered_set newlyCreatedAccounts; - evmc::VM& m_vm; /// EVM version requested by the testing tool langutil::EVMVersion m_evmVersion; /// EVM version requested from EVMC (matches the above) evmc_revision m_evmRevision; + /// Store the accounts that have been created in the current transaction. + std::unordered_set m_newlyCreatedAccounts; + /// The part of the total cost of the current transaction that paid for the code deposits. /// I.e. GAS_CODE_DEPOSIT times the total size of deployed code of all newly created contracts, /// including the current contract itself if it was a creation transaction. From 41363d723cc530fe7e0d586454d96ccdd41ac7e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 6 Feb 2024 14:39:41 +0100 Subject: [PATCH 164/189] EVMHost: Reset m_newlyCreatedAccounts() in reset() --- test/EVMHost.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index b4005e16eabb..e74491862a2b 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -161,6 +161,7 @@ void EVMHost::reset() recorded_calls.clear(); // Clear EIP-2929 account access indicator recorded_account_accesses.clear(); + m_newlyCreatedAccounts.clear(); m_totalCodeDepositGas = 0; // Mark all precompiled contracts as existing. Existing here means to have a balance (as per EIP-161). From c09d2c091bd98048c7ba86f22c0364ab991e21b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 11 Feb 2024 18:59:16 +0100 Subject: [PATCH 165/189] EVMHost: Note about newly created accounts not being rolled back --- test/EVMHost.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index e74491862a2b..2243758356db 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -359,6 +359,7 @@ evmc::Result EVMHost::call(evmc_message const& _message) noexcept auto& destination = accounts[message.recipient]; if (message.kind == EVMC_CREATE || message.kind == EVMC_CREATE2) // Mark account as created if it is a CREATE or CREATE2 call + // TODO: Should we roll changes back on failure like we do for `accounts`? m_newlyCreatedAccounts.emplace(message.recipient); if (value != 0 && message.kind != EVMC_DELEGATECALL && message.kind != EVMC_CALLCODE) From 2b2c76c214c9c5770856f91601426ab6b79a2d0b Mon Sep 17 00:00:00 2001 From: racerole Date: Wed, 6 Mar 2024 10:52:46 +0800 Subject: [PATCH 166/189] remove repetitive word Signed-off-by: racerole --- test/libyul/yulInterpreterTests/mcopy.yul | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/libyul/yulInterpreterTests/mcopy.yul b/test/libyul/yulInterpreterTests/mcopy.yul index 6483239cb151..3ec6af00213f 100644 --- a/test/libyul/yulInterpreterTests/mcopy.yul +++ b/test/libyul/yulInterpreterTests/mcopy.yul @@ -7,7 +7,7 @@ mcopy(0x60, 0, 32) // Append a duplicate of the first word past msize mcopy(0x90, 0x30, 1) // Copy the 0x44 byte from the middle of second word past msize mcopy(0, 0, 0) // No-op - mcopy(0x2f, 0x90, 2) // Copy the 0x4400 straddling msize back into the the middle of second word + mcopy(0x2f, 0x90, 2) // Copy the 0x4400 straddling msize back into the middle of second word mcopy(0xa0, 0, 160) // Duplicate the whole thing } // ==== From 1affe8e0d7445803402bc643599a5f475cf9a112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 6 Mar 2024 17:13:21 +0100 Subject: [PATCH 167/189] gp2: Remove config section that requires an Etherscan key --- test/externalTests/gp2.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/externalTests/gp2.sh b/test/externalTests/gp2.sh index 1e52134a43de..af77f99aa8b8 100755 --- a/test/externalTests/gp2.sh +++ b/test/externalTests/gp2.sh @@ -67,6 +67,9 @@ function gp2_test force_hardhat_unlimited_contract_size "$config_file" "$config_var" yarn + # Remove the config section that requires an Etherscan key. We don't need it just to run tests. + sed -i '/^ etherscan: {$/,/^ },$/d' hardhat.config.ts + # Some dependencies come with pre-built artifacts. We want to build from scratch. rm -r node_modules/@gnosis.pm/safe-contracts/build/ From 21566ccf82cff1cb72a71e428139db7c7ee7cf0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 6 Mar 2024 17:17:14 +0100 Subject: [PATCH 168/189] bleeps: Disable the ir-optimize-evm+yul preset, which now results in StackTooDeep --- test/externalTests/bleeps.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/externalTests/bleeps.sh b/test/externalTests/bleeps.sh index 5090e6c12e92..5a987e45933e 100755 --- a/test/externalTests/bleeps.sh +++ b/test/externalTests/bleeps.sh @@ -47,7 +47,7 @@ function bleeps_test "${compile_only_presets[@]}" #ir-no-optimize # Compilation fails with: "YulException: Variable expr_15509_mpos is 4 too deep in the stack". No memoryguard was present. #ir-optimize-evm-only # Compilation fails with: "YulException: Variable expr_15260_mpos is 4 too deep in the stack". No memoryguard was present. - ir-optimize-evm+yul + #ir-optimize-evm+yul # Compilation fails with: "YulException: Variable expr_15208_mpos is 1 too deep in the stack". No memoryguard was present. #legacy-no-optimize # Compilation fails with: "CompilerError: Stack too deep, try removing local variables." #legacy-optimize-evm-only # Compilation fails with: "CompilerError: Stack too deep, try removing local variables." legacy-optimize-evm+yul From ce63512f9526b5cf0e5994591114ecb81bde7379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 6 Mar 2024 15:59:35 +0100 Subject: [PATCH 169/189] chainlink: Switch base image back to node 16 --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 30c0229642a0..c2394cdce04c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -819,7 +819,7 @@ defaults: name: t_native_test_ext_chainlink project: chainlink binary_type: native - image: cimg/node:18.16 + image: cimg/node:16.20 resource_class: large # Tests run out of memory on a smaller machine - job_native_test_ext_gp2: &job_native_test_ext_gp2 From 95bcc632cefe3f2c8cdec449290fb0b4dee82c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 6 Mar 2024 19:00:39 +0100 Subject: [PATCH 170/189] Convert all the disabled external tests into compile-only tests This way we can still get bytecode size benchmarks for them --- .circleci/config.yml | 86 ++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c2394cdce04c..f30429fe6112 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -720,11 +720,14 @@ defaults: image: cimg/node:14.20 python2: true - - job_native_test_ext_gnosis: &job_native_test_ext_gnosis + - job_native_compile_ext_gnosis: &job_native_compile_ext_gnosis <<: *requires_b_ubu_static - name: t_native_test_ext_gnosis + name: t_native_compile_ext_gnosis project: gnosis binary_type: native + # NOTE: We are disabling the gnosis test suite due to version discrepancies that are difficult to fix. + # Check again after (and if) https://github.com/safe-global/safe-contracts/pull/644 is merged. + compile_only: 1 image: cimg/node:18.16 - job_native_test_ext_zeppelin: &job_native_test_ext_zeppelin @@ -742,18 +745,24 @@ defaults: binary_type: native image: cimg/node:18.16 - - job_native_test_ext_trident: &job_native_test_ext_trident + - job_native_compile_ext_trident: &job_native_compile_ext_trident <<: *requires_b_ubu_static - name: t_native_test_ext_trident + name: t_native_compile_ext_trident project: trident binary_type: native + # NOTE: test suite disabled due to dependence on a specific version of Hardhat + # which does not support shanghai EVM. + compile_only: 1 image: cimg/node:18.16 - - job_native_test_ext_euler: &job_native_test_ext_euler + - job_native_compile_ext_euler: &job_native_compile_ext_euler <<: *requires_b_ubu_static - name: t_native_test_ext_euler + name: t_native_compile_ext_euler project: euler binary_type: native + # NOTE: test suite disabled due to dependence on a specific version of Hardhat + # which does not support shanghai EVM. + compile_only: 1 resource_class: medium - job_native_test_ext_yield_liquidator: &job_native_test_ext_yield_liquidator @@ -763,18 +772,24 @@ defaults: binary_type: native image: cimg/node:18.16 - - job_native_test_ext_bleeps: &job_native_test_ext_bleeps + - job_native_compile_ext_bleeps: &job_native_compile_ext_bleeps <<: *requires_b_ubu_static - name: t_native_test_ext_bleeps + name: t_native_compile_ext_bleeps project: bleeps binary_type: native + # NOTE: test suite disabled due to dependence on a specific version of Hardhat + # which does not support shanghai EVM. + compile_only: 1 resource_class: medium - - job_native_test_ext_pool_together: &job_native_test_ext_pool_together + - job_native_compile_ext_pool_together: &job_native_compile_ext_pool_together <<: *requires_b_ubu_static - name: t_native_test_ext_pool_together + name: t_native_compile_ext_pool_together project: pool-together binary_type: native + # NOTE: test suite disabled due to dependence on a specific version of Hardhat + # which does not support shanghai EVM. + compile_only: 1 image: cimg/node:18.16 - job_native_test_ext_perpetual_pools: &job_native_test_ext_perpetual_pools @@ -814,19 +829,24 @@ defaults: binary_type: native image: cimg/node:18.16 - - job_native_test_ext_chainlink: &job_native_test_ext_chainlink + - job_native_compile_ext_chainlink: &job_native_compile_ext_chainlink <<: *requires_b_ubu_static - name: t_native_test_ext_chainlink + name: t_native_compile_ext_chainlink project: chainlink binary_type: native + # NOTE: test suite disabled due to dependence on a specific version of Hardhat + # which does not support shanghai EVM. + compile_only: 1 image: cimg/node:16.20 resource_class: large # Tests run out of memory on a smaller machine - - job_native_test_ext_gp2: &job_native_test_ext_gp2 + - job_native_compile_ext_gp2: &job_native_compile_ext_gp2 <<: *requires_b_ubu_static - name: t_native_test_ext_gp2 + name: t_native_compile_ext_gp2 project: gp2 binary_type: native + # NOTE: test suite disabled due to constant failures. + compile_only: 1 image: cimg/node:18.16 - job_b_ubu_asan_clang: &job_b_ubu_asan_clang @@ -1868,9 +1888,7 @@ workflows: - t_ext: *job_ems_compile_ext_colony - # NOTE: We are disabling the gnosis test suite due to version discrepancies that are difficult to fix. - # Check again after (and if) https://github.com/safe-global/safe-contracts/pull/644 is merged. - #- t_ext: *job_native_test_ext_gnosis + - t_ext: *job_native_compile_ext_gnosis - t_ext: *job_native_test_ext_zeppelin - t_ext: *job_native_test_ext_ens - t_ext: *job_native_test_ext_yield_liquidator @@ -1879,23 +1897,18 @@ workflows: - t_ext: *job_native_test_ext_prb_math - t_ext: *job_native_test_ext_elementfi - t_ext: *job_native_test_ext_brink - # NOTE: We are disabling gp2 tests due to constant failures. - # - t_ext: *job_native_test_ext_gp2 - # NOTE: The external tests below were commented because they - # depend on a specific version of hardhat which does not support shanghai EVM. - #- t_ext: *job_native_test_ext_trident - #- t_ext: *job_native_test_ext_euler - #- t_ext: *job_native_test_ext_bleeps - #- t_ext: *job_native_test_ext_pool_together - #- t_ext: *job_native_test_ext_chainlink + - t_ext: *job_native_compile_ext_gp2 + - t_ext: *job_native_compile_ext_trident + - t_ext: *job_native_compile_ext_euler + - t_ext: *job_native_compile_ext_bleeps + - t_ext: *job_native_compile_ext_pool_together + - t_ext: *job_native_compile_ext_chainlink - c_ext_benchmarks: <<: *requires_nothing requires: - t_ems_compile_ext_colony - # NOTE: We are disabling the gnosis test suite due to version discrepancies that are difficult to fix. - # Check again after (and if) https://github.com/safe-global/safe-contracts/pull/644 is merged. - #- t_native_test_ext_gnosis + - t_native_compile_ext_gnosis - t_native_test_ext_zeppelin - t_native_test_ext_ens - t_native_test_ext_yield_liquidator @@ -1903,18 +1916,15 @@ workflows: - t_native_test_ext_uniswap - t_native_test_ext_elementfi - t_native_test_ext_brink - # NOTE: We are disabling gp2 tests due to constant failures. - #- t_native_test_ext_gp2 + - t_native_compile_ext_gp2 # TODO: Dropping prb-math from the benchmarks since it is not implemented yet # in the new Foundry external testing infrastructure. # - t_native_test_ext_prb_math - # NOTE: The external tests below were commented because they - # depend on a specific version of hardhat which does not support shanghai EVM. - #- t_native_test_ext_trident - #- t_native_test_ext_euler - #- t_native_test_ext_bleeps - #- t_native_test_ext_pool_together - #- t_native_test_ext_chainlink + - t_native_compile_ext_trident + - t_native_compile_ext_euler + - t_native_compile_ext_bleeps + - t_native_compile_ext_pool_together + - t_native_compile_ext_chainlink # Windows build and tests - b_win: *requires_nothing From 5aed3c4bbb04f0d43c6467d64e9a5a8cfb963dd8 Mon Sep 17 00:00:00 2001 From: r0qs Date: Wed, 6 Mar 2024 15:11:25 +0100 Subject: [PATCH 171/189] Fix hardhat version to 2.20.0 on uniswap and perpetual_pools external tests --- test/externalTests/perpetual-pools.sh | 5 +++++ test/externalTests/uniswap.sh | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/test/externalTests/perpetual-pools.sh b/test/externalTests/perpetual-pools.sh index 31748a40ca2a..1d7bb8f33b3e 100755 --- a/test/externalTests/perpetual-pools.sh +++ b/test/externalTests/perpetual-pools.sh @@ -69,7 +69,12 @@ function perpetual_pools_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" + yarn install + # We set hardhat version to 2.20.0 since version 2.21.0 has issues with solidity-coverage plugin + # that often causes out-of-memory errors. + # See hardhat note about the issue here: https://github.com/NomicFoundation/hardhat/releases/tag/hardhat@2.21.0 + yarn add hardhat@2.20.0 replace_version_pragmas diff --git a/test/externalTests/uniswap.sh b/test/externalTests/uniswap.sh index bf5f828987eb..04f27ab3f77f 100755 --- a/test/externalTests/uniswap.sh +++ b/test/externalTests/uniswap.sh @@ -90,6 +90,11 @@ function uniswap_test # TODO: Remove when https://github.com/ethers-io/ethers.js/discussions/2849 is resolved. yarn add ethers@5.6.1 + # We set hardhat version to 2.20.0 since version 2.21.0 has issues with solidity-coverage plugin + # that often causes out-of-memory errors. + # See hardhat note about the issue here: https://github.com/NomicFoundation/hardhat/releases/tag/hardhat@2.21.0 + yarn add hardhat@2.20.0 + replace_version_pragmas for preset in $SELECTED_PRESETS; do From d29eded2da25e91fc7bb126e67030ea09da44d9c Mon Sep 17 00:00:00 2001 From: Saw-mon & Natalie <3140080+Saw-mon-and-Natalie@users.noreply.github.com> Date: Thu, 7 Mar 2024 16:33:17 +0100 Subject: [PATCH 172/189] reorders assertThrow statements and adds one to the MCOPY case. --- libevmasm/SemanticInformation.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index c5e14d5f4b85..978fb7ce2aef 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -87,8 +87,8 @@ std::vector SemanticInformation::readWriteOperat case Instruction::LOG4: { assertThrow(storage(_instruction) == Effect::None, OptimizerException, ""); - assertThrow(transientStorage(_instruction) == Effect::None, OptimizerException, ""); assertThrow(memory(_instruction) == Effect::Read, OptimizerException, ""); + assertThrow(transientStorage(_instruction) == Effect::None, OptimizerException, ""); Operation op; op.effect = memory(_instruction); op.location = Location::Memory; @@ -126,6 +126,7 @@ std::vector SemanticInformation::readWriteOperat { assertThrow(memory(_instruction) != Effect::None, OptimizerException, ""); assertThrow(storage(_instruction) == Effect::None, OptimizerException, ""); + assertThrow(transientStorage(_instruction) == Effect::None, OptimizerException, ""); Operation readOperation; readOperation.effect = Read; From 6ee38afed8792dd1dd669612a226bf8080a49ae2 Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Thu, 7 Mar 2024 21:19:08 +0100 Subject: [PATCH 173/189] [solutil] Add missing include guard. --- libsolutil/cxx20.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libsolutil/cxx20.h b/libsolutil/cxx20.h index c65b0454b89c..a21a73e681af 100644 --- a/libsolutil/cxx20.h +++ b/libsolutil/cxx20.h @@ -15,6 +15,7 @@ along with solidity. If not, see . */ // SPDX-License-Identifier: GPL-3.0 +#pragma once #include From e084f71f7b28294e38576458d72d32855aa4e0e5 Mon Sep 17 00:00:00 2001 From: Cyrus Date: Fri, 8 Mar 2024 12:11:48 +0800 Subject: [PATCH 174/189] fix typos --- libevmasm/GasMeter.h | 2 +- libevmasm/Inliner.cpp | 2 +- libsolidity/parsing/Parser.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libevmasm/GasMeter.h b/libevmasm/GasMeter.h index e4bb19925796..c91e0f0971c9 100644 --- a/libevmasm/GasMeter.h +++ b/libevmasm/GasMeter.h @@ -216,7 +216,7 @@ class GasMeter /// @returns an upper bound on the gas consumed by the given instruction and updates /// the state. - /// @param _inculdeExternalCosts if true, include costs caused by other contracts in calls. + /// @param _includeExternalCosts if true, include costs caused by other contracts in calls. GasConsumption estimateMax(AssemblyItem const& _item, bool _includeExternalCosts = true); u256 const& largestMemoryAccess() const { return m_largestMemoryAccess; } diff --git a/libevmasm/Inliner.cpp b/libevmasm/Inliner.cpp index ded5172ddc3a..67918e558bc3 100644 --- a/libevmasm/Inliner.cpp +++ b/libevmasm/Inliner.cpp @@ -44,7 +44,7 @@ using namespace solidity::evmasm; namespace { -/// @returns an estimation of the runtime gas cost of the AsssemblyItems in @a _itemRange. +/// @returns an estimation of the runtime gas cost of the AssemblyItems in @a _itemRange. template u256 executionCost(RangeType const& _itemRange, langutil::EVMVersion _evmVersion) { diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index aedfdb7ddd37..8f0868ac1172 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -223,10 +223,10 @@ class Parser: public langutil::ParserBase /// or an expression; IndexAccessedPath parseIndexAccessedPath(); /// @returns a typename parsed in look-ahead fashion from something like "a.b[8][2**70]", - /// or an empty pointer if an empty @a _pathAndIncides has been supplied. + /// or an empty pointer if an empty @a _pathAndIndices has been supplied. ASTPointer typeNameFromIndexAccessStructure(IndexAccessedPath const& _pathAndIndices); /// @returns an expression parsed in look-ahead fashion from something like "a.b[8][2**70]", - /// or an empty pointer if an empty @a _pathAndIncides has been supplied. + /// or an empty pointer if an empty @a _pathAndIndices has been supplied. ASTPointer expressionFromIndexAccessStructure(IndexAccessedPath const& _pathAndIndices); ASTPointer expectIdentifierToken(); From 86444080a45f475a1065522ffd0ee32e28120c72 Mon Sep 17 00:00:00 2001 From: r0qs Date: Wed, 29 Nov 2023 15:46:30 -0300 Subject: [PATCH 175/189] Move deprecated evm versions to nightly CI workflow --- .circleci/config.yml | 27 ++++++++++++++++++++++++++- .circleci/soltest_all.sh | 7 +++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 59d00ff4155e..af5ddef21be1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -220,11 +220,18 @@ commands: command: .circleci/soltest.sh 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. + evm_versions: + description: "List of EVM versions (separated by space)." + type: string + default: "" steps: - run: name: soltest_all no_output_timeout: 30m - command: .circleci/soltest_all.sh + command: .circleci/soltest_all.sh "<< parameters.evm_versions >>" run_cmdline_tests: steps: @@ -1267,6 +1274,20 @@ jobs: steps: - soltest_all + t_ubu_soltest_deprecated_evm_versions: &t_ubu_soltest_deprecated_evm_versions + <<: *base_ubuntu2204_large + parallelism: 50 + steps: + - checkout + - attach_workspace: + at: build + - run_soltest_all: + evm_versions: homestead byzantium + - store_test_results: + path: test_results/ + - store_artifacts_test_results + - matrix_notify_failure_unless_pr + t_ubu_lsp: &t_ubu_lsp <<: *base_ubuntu2204_small steps: @@ -2004,6 +2025,10 @@ workflows: - t_ubu_ubsan_clang_soltest: *requires_b_ubu_ubsan_clang - t_ubu_ubsan_clang_cli: *requires_b_ubu_ubsan_clang + # Deprecated EVM versions tests + - b_ubu: *requires_nothing + - t_ubu_soltest_deprecated_evm_versions: *requires_b_ubu + nightly-ossfuzz: triggers: diff --git a/.circleci/soltest_all.sh b/.circleci/soltest_all.sh index 1e941731dac3..40ee75b270cd 100755 --- a/.circleci/soltest_all.sh +++ b/.circleci/soltest_all.sh @@ -31,9 +31,12 @@ REPODIR="$(realpath "$(dirname "$0")"/..)" # shellcheck source=scripts/common.sh source "${REPODIR}/scripts/common.sh" -EVM_VALUES=(homestead byzantium constantinople petersburg istanbul berlin london paris shanghai cancun) +DEFAULT_EVM_VALUES=(constantinople petersburg istanbul berlin london paris shanghai cancun) +# Deserialize the EVM_VALUES array if it was provided as argument or +# set EVM_VALUES to the default values. +IFS=" " read -ra EVM_VALUES <<< "${1:-${DEFAULT_EVM_VALUES[@]}}" + DEFAULT_EVM=shanghai -[[ " ${EVM_VALUES[*]} " =~ $DEFAULT_EVM ]] OPTIMIZE_VALUES=(0 1) # Run for ABI encoder v1, without SMTChecker tests. From 9762ebb24cdb79e22d94215e0ebccc27937c63e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 8 Mar 2024 18:55:33 +0100 Subject: [PATCH 176/189] Fix unnecessary compilation when --via-ir is used on its own --- Changelog.md | 1 + libsolidity/interface/CompilerStack.cpp | 2 +- test/cmdlineTests/abi_via_ir/args | 1 + test/cmdlineTests/abi_via_ir/input.sol | 28 ++++ test/cmdlineTests/abi_via_ir/output | 167 ++++++++++++++++++++++++ 5 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 test/cmdlineTests/abi_via_ir/args create mode 100644 test/cmdlineTests/abi_via_ir/input.sol create mode 100644 test/cmdlineTests/abi_via_ir/output diff --git a/Changelog.md b/Changelog.md index 6defb4d72b53..ab62f25eb894 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Compiler Features: Bugfixes: * Assembler: Prevent incorrect calculation of tag sizes. + * Commandline Interface: Do not run IR pipeline when ``--via-ir`` is used but no output that depends on the IR is requested. * EVM Assembly Import: Fix handling of missing source locations during import. * SMTChecker: Fix internal error caused by not respecting the sign of an integer type when constructing zero-value SMT expressions. * SMTChecker: Ensure query is properly flushed to a file before calling solver when using SMT-LIB interface. diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 69394417586e..eca1b50be4f0 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -736,7 +736,7 @@ bool CompilerStack::compile(State _stopAfter) { try { - if (m_viaIR || m_generateIR) + if ((m_generateEvmBytecode && m_viaIR) || m_generateIR) generateIR(*contract); if (m_generateEvmBytecode) { diff --git a/test/cmdlineTests/abi_via_ir/args b/test/cmdlineTests/abi_via_ir/args new file mode 100644 index 000000000000..5dc346ecb4bb --- /dev/null +++ b/test/cmdlineTests/abi_via_ir/args @@ -0,0 +1 @@ +--abi --via-ir --pretty-json --json-indent 4 diff --git a/test/cmdlineTests/abi_via_ir/input.sol b/test/cmdlineTests/abi_via_ir/input.sol new file mode 100644 index 000000000000..1f5c758f719d --- /dev/null +++ b/test/cmdlineTests/abi_via_ir/input.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +error fileLevelError(uint z); + +library L { + event libraryEvent(uint r); + error libraryError(uint r); + error libraryErrorUnused(uint u); + event libraryEventUnused(uint u); +} + +contract C { + struct S { uint x; } + + event ev(uint y); + event anon_ev(uint y) anonymous; + + error err(uint z, uint w); + + function f(S memory s) public { + emit L.libraryEvent(3); + if (s.x > 1) + revert fileLevelError(3); + else + revert L.libraryError(4); + } +} diff --git a/test/cmdlineTests/abi_via_ir/output b/test/cmdlineTests/abi_via_ir/output new file mode 100644 index 000000000000..daf18f3497a7 --- /dev/null +++ b/test/cmdlineTests/abi_via_ir/output @@ -0,0 +1,167 @@ + +======= abi_via_ir/input.sol:C ======= +Contract JSON ABI +[ + { + "inputs": + [ + { + "internalType": "uint256", + "name": "z", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "w", + "type": "uint256" + } + ], + "name": "err", + "type": "error" + }, + { + "inputs": + [ + { + "internalType": "uint256", + "name": "z", + "type": "uint256" + } + ], + "name": "fileLevelError", + "type": "error" + }, + { + "inputs": + [ + { + "internalType": "uint256", + "name": "r", + "type": "uint256" + } + ], + "name": "libraryError", + "type": "error" + }, + { + "anonymous": true, + "inputs": + [ + { + "indexed": false, + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "name": "anon_ev", + "type": "event" + }, + { + "anonymous": false, + "inputs": + [ + { + "indexed": false, + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "name": "ev", + "type": "event" + }, + { + "anonymous": false, + "inputs": + [ + { + "indexed": false, + "internalType": "uint256", + "name": "r", + "type": "uint256" + } + ], + "name": "libraryEvent", + "type": "event" + }, + { + "inputs": + [ + { + "components": + [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + } + ], + "internalType": "struct C.S", + "name": "s", + "type": "tuple" + } + ], + "name": "f", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] + +======= abi_via_ir/input.sol:L ======= +Contract JSON ABI +[ + { + "inputs": + [ + { + "internalType": "uint256", + "name": "r", + "type": "uint256" + } + ], + "name": "libraryError", + "type": "error" + }, + { + "inputs": + [ + { + "internalType": "uint256", + "name": "u", + "type": "uint256" + } + ], + "name": "libraryErrorUnused", + "type": "error" + }, + { + "anonymous": false, + "inputs": + [ + { + "indexed": false, + "internalType": "uint256", + "name": "r", + "type": "uint256" + } + ], + "name": "libraryEvent", + "type": "event" + }, + { + "anonymous": false, + "inputs": + [ + { + "indexed": false, + "internalType": "uint256", + "name": "u", + "type": "uint256" + } + ], + "name": "libraryEventUnused", + "type": "event" + } +] From 0c4bca56a176116940740d0de4f371a0b5357484 Mon Sep 17 00:00:00 2001 From: omahs <73983677+omahs@users.noreply.github.com> Date: Fri, 8 Mar 2024 20:46:24 +0100 Subject: [PATCH 177/189] fix typo --- test/compilationTests/corion/ico.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/compilationTests/corion/ico.sol b/test/compilationTests/corion/ico.sol index 35960fe43a44..801b6d402f3a 100644 --- a/test/compilationTests/corion/ico.sol +++ b/test/compilationTests/corion/ico.sol @@ -372,5 +372,5 @@ contract ico is safeMath { return startBlock <= block.number && block.number <= icoDelay && ( ! aborted ) && ( ! closed ); } - event EICO(address indexed Address, uint256 indexed value, address Affiliate, uint256 AffilateValue); + event EICO(address indexed Address, uint256 indexed value, address Affiliate, uint256 AffiliateValue); } From 193a4579aec550f71af7b5355d82ad57a8588d9d Mon Sep 17 00:00:00 2001 From: omahs <73983677+omahs@users.noreply.github.com> Date: Fri, 8 Mar 2024 20:50:22 +0100 Subject: [PATCH 178/189] fix typo --- docs/smtchecker.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/smtchecker.rst b/docs/smtchecker.rst index f8085b63de6f..dfdcf465198b 100644 --- a/docs/smtchecker.rst +++ b/docs/smtchecker.rst @@ -944,7 +944,7 @@ the arguments. |complex) | | +-----------------------------------+--------------------------------------+ |external functions without |BMC: Erase state knowledge and assume | -|implementation |result is nondeterminisc. | +|implementation |result is nondeterministic. | | |CHC: Nondeterministic summary. | | |Try to infer invariants that hold | | |after the call returns. | From 0219d1e0f2df22cac5ba8e944f26f20860f7a56e Mon Sep 17 00:00:00 2001 From: Martin Blicha Date: Tue, 5 Mar 2024 13:14:32 +0100 Subject: [PATCH 179/189] SMTChecker: Fix usage of Eldarica with SMT callback Previously, CHC engine with SMT interface would always call Eldarica if it was present in the system, regardless whether user specified model-checker-solver as smtlib2 or eld. Here we make sure Eldarica is called only when it is specified as the solver of choice. The proposed solution is to make SMTSolverCommand modifiable and set it up properly based on the user settings. This requires changes also in UniversalCallback, because in the compiler we must be able to check if the given callback is UniversalCallback provided by CommandLineInterface. This mechanism can be used to migrate also other solvers to the SMTLIB interface by further extending/adapting SMTSolverCommand. Its advantage is that meaning of the callback stays the same, thus there is not need to change anything on the side of solc-js. --- Changelog.md | 3 ++- libsolidity/formal/ModelCheckerSettings.h | 2 +- libsolidity/interface/CompilerStack.cpp | 6 ++++++ libsolidity/interface/SMTSolverCommand.cpp | 23 +++++++++++++++++--- libsolidity/interface/SMTSolverCommand.h | 7 +++--- libsolidity/interface/UniversalCallback.h | 25 +++++++++++++--------- solc/CommandLineInterface.h | 2 +- 7 files changed, 49 insertions(+), 19 deletions(-) diff --git a/Changelog.md b/Changelog.md index 6defb4d72b53..6564cb9a3e22 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,8 +11,9 @@ Compiler Features: Bugfixes: * Assembler: Prevent incorrect calculation of tag sizes. * EVM Assembly Import: Fix handling of missing source locations during import. - * SMTChecker: Fix internal error caused by not respecting the sign of an integer type when constructing zero-value SMT expressions. * SMTChecker: Ensure query is properly flushed to a file before calling solver when using SMT-LIB interface. + * SMTChecker: Fix internal error caused by not respecting the sign of an integer type when constructing zero-value SMT expressions. + * SMTChecker: Run Eldarica only when explicitly requested with `--model-checker-solvers eld`, even when it is present on the system. ### 0.8.24 (2024-01-25) diff --git a/libsolidity/formal/ModelCheckerSettings.h b/libsolidity/formal/ModelCheckerSettings.h index b645f7c3b6e7..0cfa370c40c8 100644 --- a/libsolidity/formal/ModelCheckerSettings.h +++ b/libsolidity/formal/ModelCheckerSettings.h @@ -175,7 +175,7 @@ struct ModelCheckerSettings bool showUnsupported = false; smtutil::SMTSolverChoice solvers = smtutil::SMTSolverChoice::Z3(); ModelCheckerTargets targets = ModelCheckerTargets::Default(); - std::optional timeout; + std::optional timeout; // in milliseconds bool operator!=(ModelCheckerSettings const& _other) const noexcept { return !(*this == _other); } bool operator==(ModelCheckerSettings const& _other) const noexcept diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 69394417586e..e9df599630fb 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -53,6 +53,7 @@ #include #include #include +#include #include #include @@ -654,7 +655,12 @@ bool CompilerStack::analyzeLegacy(bool _noErrorsSoFar) // m_modelCheckerSettings is spread to engines and solver interfaces, // so we need to check whether the enabled ones are available before building the classes. if (m_modelCheckerSettings.engine.any()) + { m_modelCheckerSettings.solvers = ModelChecker::checkRequestedSolvers(m_modelCheckerSettings.solvers, m_errorReporter); + if (auto* universalCallback = m_readFile.target()) + if (m_modelCheckerSettings.solvers.eld) + universalCallback->smtCommand().setEldarica(m_modelCheckerSettings.timeout); + } ModelChecker modelChecker(m_errorReporter, *this, m_smtlib2Responses, m_modelCheckerSettings, m_readFile); modelChecker.checkRequestedSourcesAndContracts(allSources); diff --git a/libsolidity/interface/SMTSolverCommand.cpp b/libsolidity/interface/SMTSolverCommand.cpp index d1c58c0c0a69..8ef36cb75189 100644 --- a/libsolidity/interface/SMTSolverCommand.cpp +++ b/libsolidity/interface/SMTSolverCommand.cpp @@ -37,7 +37,17 @@ using solidity::util::errinfo_comment; namespace solidity::frontend { -SMTSolverCommand::SMTSolverCommand(std::string _solverCmd) : m_solverCmd(_solverCmd) {} +void SMTSolverCommand::setEldarica(std::optional timeoutInMilliseconds) +{ + m_arguments.clear(); + m_solverCmd = "eld"; + if (timeoutInMilliseconds) + { + unsigned int timeoutInSeconds = timeoutInMilliseconds.value() / 1000u; + timeoutInSeconds = timeoutInSeconds == 0 ? 1 : timeoutInSeconds; + m_arguments.push_back("-t:" + std::to_string(timeoutInSeconds)); + } +} ReadCallback::Result SMTSolverCommand::solve(std::string const& _kind, std::string const& _query) { @@ -46,6 +56,9 @@ ReadCallback::Result SMTSolverCommand::solve(std::string const& _kind, std::stri if (_kind != ReadCallback::kindString(ReadCallback::Kind::SMTQuery)) solAssert(false, "SMTQuery callback used as callback kind " + _kind); + if (m_solverCmd.empty()) + return ReadCallback::Result{false, "No solver set."}; + auto tempDir = solidity::util::TemporaryDirectory("smt"); util::h256 queryHash = util::keccak256(_query); auto queryFileName = tempDir.path() / ("query_" + queryHash.hex() + ".smt2"); @@ -58,11 +71,15 @@ ReadCallback::Result SMTSolverCommand::solve(std::string const& _kind, std::stri if (eldBin.empty()) return ReadCallback::Result{false, m_solverCmd + " binary not found."}; + auto args = m_arguments; + args.push_back(queryFileName.string()); + boost::process::ipstream pipe; boost::process::child eld( eldBin, - queryFileName, - boost::process::std_out > pipe + args, + boost::process::std_out > pipe, + boost::process::std_err >boost::process::null ); std::vector data; diff --git a/libsolidity/interface/SMTSolverCommand.h b/libsolidity/interface/SMTSolverCommand.h index 56a6ba171055..1d1201e49062 100644 --- a/libsolidity/interface/SMTSolverCommand.h +++ b/libsolidity/interface/SMTSolverCommand.h @@ -28,8 +28,6 @@ namespace solidity::frontend class SMTSolverCommand { public: - SMTSolverCommand(std::string _solverCmd); - /// Calls an SMT solver with the given query. frontend::ReadCallback::Result solve(std::string const& _kind, std::string const& _query); @@ -38,9 +36,12 @@ class SMTSolverCommand return [this](std::string const& _kind, std::string const& _query) { return solve(_kind, _query); }; } + void setEldarica(std::optional timeoutInMilliseconds); + private: /// The name of the solver's binary. - std::string const m_solverCmd; + std::string m_solverCmd; + std::vector m_arguments; }; } diff --git a/libsolidity/interface/UniversalCallback.h b/libsolidity/interface/UniversalCallback.h index 3fe74c65e48f..ef5450a1c5e4 100644 --- a/libsolidity/interface/UniversalCallback.h +++ b/libsolidity/interface/UniversalCallback.h @@ -34,22 +34,27 @@ class UniversalCallback m_solver{_solver} {} + ReadCallback::Result operator()(std::string const& _kind, std::string const& _data) + { + if (_kind == ReadCallback::kindString(ReadCallback::Kind::ReadFile)) + if (!m_fileReader) + return ReadCallback::Result{false, "No import callback."}; + else + return m_fileReader->readFile(_kind, _data); + else if (_kind == ReadCallback::kindString(ReadCallback::Kind::SMTQuery)) + return m_solver.solve(_kind, _data); + solAssert(false, "Unknown callback kind."); + } + frontend::ReadCallback::Callback callback() { - return [this](std::string const& _kind, std::string const& _data) -> ReadCallback::Result { - if (_kind == ReadCallback::kindString(ReadCallback::Kind::ReadFile)) - if (!m_fileReader) - return ReadCallback::Result{false, "No import callback."}; - else - return m_fileReader->readFile(_kind, _data); - else if (_kind == ReadCallback::kindString(ReadCallback::Kind::SMTQuery)) - return m_solver.solve(_kind, _data); - solAssert(false, "Unknown callback kind."); - }; + return *this; } void resetImportCallback() { m_fileReader = nullptr; } + SMTSolverCommand& smtCommand() { return m_solver; } + private: FileReader* m_fileReader; SMTSolverCommand& m_solver; diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 906b34d62b53..1fb4ac00bcb1 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -147,7 +147,7 @@ class CommandLineInterface std::ostream& m_serr; bool m_hasOutput = false; FileReader m_fileReader; - SMTSolverCommand m_solverCommand{"eld"}; + SMTSolverCommand m_solverCommand; UniversalCallback m_universalCallback{&m_fileReader, m_solverCommand}; std::optional m_standardJsonInput; std::unique_ptr m_compiler; From b18b23daf6c4d812211ff9ef329ae8c44999fb17 Mon Sep 17 00:00:00 2001 From: r0qs Date: Tue, 12 Mar 2024 15:18:39 +0100 Subject: [PATCH 180/189] Add missing hardhat networks settings to external tests --- scripts/externalTests/common.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/externalTests/common.sh b/scripts/externalTests/common.sh index bab5890aa400..de9b8da96d8c 100644 --- a/scripts/externalTests/common.sh +++ b/scripts/externalTests/common.sh @@ -332,12 +332,16 @@ function force_hardhat_compiler_settings echo "require('hardhat-gas-reporter');" echo "module.exports.gasReporter = ${gas_reporter_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}'" else [[ $config_file == *\.ts ]] || assertFail [[ $config_var_name != "" ]] || assertFail echo 'import "hardhat-gas-reporter";' echo "${config_var_name}.gasReporter = ${gas_reporter_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}'" fi >> "$config_file" } From c82707cae6a085585421f6952eedffccffb7cb56 Mon Sep 17 00:00:00 2001 From: r0qs Date: Tue, 12 Mar 2024 16:50:51 +0100 Subject: [PATCH 181/189] Fix hardhat version to version that supports cancun hardfork --- test/externalTests/brink.sh | 1 + test/externalTests/elementfi.sh | 4 +--- test/externalTests/gnosis.sh | 1 + test/externalTests/pool-together.sh | 3 +++ 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/test/externalTests/brink.sh b/test/externalTests/brink.sh index 48cc4028f3d7..d8ad001ac014 100755 --- a/test/externalTests/brink.sh +++ b/test/externalTests/brink.sh @@ -64,6 +64,7 @@ function brink_test # TODO: Remove this when Brink merges https://github.com/brinktrade/brink-core/pull/52 sed -i "s|\(function isValidSignature(bytes \)calldata\( _data, bytes \)calldata\( _signature)\)|\1memory\2memory\3|g" src/Test/MockEIP1271Validator.sol + neutralize_package_lock neutralize_package_json_hooks force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH" force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" "$config_var" "$CURRENT_EVM_VERSION" "$extra_settings" "$extra_optimizer_settings" diff --git a/test/externalTests/elementfi.sh b/test/externalTests/elementfi.sh index f3ecbb14170f..f34a169e9512 100755 --- a/test/externalTests/elementfi.sh +++ b/test/externalTests/elementfi.sh @@ -99,9 +99,7 @@ function elementfi_test # "ProviderError: Too Many Requests error received from eth-mainnet.alchemyapi.io" rm test/mockERC20YearnVaultTest.ts - # Several tests fail unless we use the exact versions hard-coded in package-lock.json - #neutralize_package_lock - + neutralize_package_lock neutralize_package_json_hooks force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH" force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" "$config_var" diff --git a/test/externalTests/gnosis.sh b/test/externalTests/gnosis.sh index 218a5a7504b5..416d36153623 100755 --- a/test/externalTests/gnosis.sh +++ b/test/externalTests/gnosis.sh @@ -76,6 +76,7 @@ function gnosis_safe_test sed -i "s|\(it\)\((\"can be used only via DELEGATECALL opcode\"\)|\1.skip\2|g" test/libraries/SignMessageLib.spec.ts sed -i "s|it\((\"can only be called from Safe itself\"\)|it.skip\1|g" test/libraries/Migration.spec.ts + neutralize_package_lock neutralize_package_json_hooks force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH" force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" "$config_var" diff --git a/test/externalTests/pool-together.sh b/test/externalTests/pool-together.sh index f841206ef2a9..0fc8aafbd69d 100755 --- a/test/externalTests/pool-together.sh +++ b/test/externalTests/pool-together.sh @@ -66,6 +66,9 @@ function pool_together_test force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH" force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" "$config_var" yarn install + # Hardhat 2.20.0 is the last version that works. Newer versions throw this error: + # Unexpected config HardhatConfig.networks.hardhat.initialBaseFeePerGas found - This field is only valid for networks with EIP-1559. Try a newer hardfork or remove it. + yarn add hardhat@2.20.0 # These come with already compiled artifacts. We want them recompiled with latest compiler. rm -r node_modules/@pooltogether/yield-source-interface/artifacts/ From 6b3ca241105fd9298a3ff7f9dcb62bf1d58f952b Mon Sep 17 00:00:00 2001 From: r0qs Date: Tue, 12 Mar 2024 18:51:24 +0100 Subject: [PATCH 182/189] Disable trident, chainlink and bleeps external tests --- .circleci/config.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index af5ddef21be1..e437e1d38759 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1919,11 +1919,13 @@ workflows: - t_ext: *job_native_test_ext_elementfi - t_ext: *job_native_test_ext_brink - t_ext: *job_native_compile_ext_gp2 - - t_ext: *job_native_compile_ext_trident - t_ext: *job_native_compile_ext_euler - - t_ext: *job_native_compile_ext_bleeps - t_ext: *job_native_compile_ext_pool_together - - t_ext: *job_native_compile_ext_chainlink + # TODO: Dropping the external tests below since they are based on old forks and + # fail after update the default evm version to cancun. + #- t_ext: *job_native_compile_ext_trident + #- t_ext: *job_native_compile_ext_chainlink + #- t_ext: *job_native_compile_ext_bleeps - c_ext_benchmarks: <<: *requires_nothing @@ -1941,11 +1943,13 @@ workflows: # TODO: Dropping prb-math from the benchmarks since it is not implemented yet # in the new Foundry external testing infrastructure. # - t_native_test_ext_prb_math - - t_native_compile_ext_trident - t_native_compile_ext_euler - - t_native_compile_ext_bleeps - t_native_compile_ext_pool_together - - t_native_compile_ext_chainlink + # TODO: Dropping the external tests below since they are based on old forks and + # fail after update the default evm version to cancun. + #- t_native_compile_ext_trident + #- t_native_compile_ext_chainlink + #- t_native_compile_ext_bleeps # Windows build and tests - b_win: *requires_nothing From 7c6f9f1154b757b040b1cbd9e4383caf04ceeaa9 Mon Sep 17 00:00:00 2001 From: Twice Date: Thu, 14 Mar 2024 00:49:02 +0900 Subject: [PATCH 183/189] Use move ctor instead of copy ctor in stringOrDefault --- libsolutil/Assertions.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libsolutil/Assertions.h b/libsolutil/Assertions.h index 6735cda166be..4879299bf011 100644 --- a/libsolutil/Assertions.h +++ b/libsolutil/Assertions.h @@ -28,6 +28,7 @@ #include #include +#include namespace solidity::util { @@ -69,7 +70,7 @@ inline std::string stringOrDefault(std::string _string, std::string _defaultStri { // NOTE: Putting this in a function rather than directly in a macro prevents the string from // being evaluated multiple times if it's not just a literal. - return (!_string.empty() ? _string : _defaultString); + return (!_string.empty() ? std::move(_string) : std::move(_defaultString)); } } From ab55b0a49840491cd6e47e87e519360e21e9c518 Mon Sep 17 00:00:00 2001 From: matheusaaguiar <95899911+matheusaaguiar@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:55:48 -0300 Subject: [PATCH 184/189] Add multiple source test to ensure transient storage is emitted only once during compilation (#14935) --- ...ore_warning_only_once_multiple_sources.sol | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/tstore_warning_only_once_multiple_sources.sol diff --git a/test/libsolidity/syntaxTests/inlineAssembly/tstore_warning_only_once_multiple_sources.sol b/test/libsolidity/syntaxTests/inlineAssembly/tstore_warning_only_once_multiple_sources.sol new file mode 100644 index 000000000000..51e3a19bccc0 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/tstore_warning_only_once_multiple_sources.sol @@ -0,0 +1,34 @@ +==== Source: A ==== +contract C { + function f() external { + assembly { + tstore(0, 0) + let a := tload(0) + tstore(0, 1) + tstore(1, a) + } + } +} +==== Source: B ==== +import {C as C} from "A"; +contract D { + function g() external { + assembly { + tstore(0, 0) + tstore(0, 1) + } + } +} +==== Source: C ==== +contract X { + function h() external { + assembly { + tstore(0, 0) + tstore(0, 0) + } + } +} +// ==== +// EVMVersion: >=cancun +// ---- +// Warning 2394: (A:72-78): Transient storage as defined by EIP-1153 can break the composability of smart contracts: Since transient storage is cleared only at the end of the transaction and not at the end of the outermost call frame to the contract within a transaction, your contract may unintentionally misbehave when invoked multiple times in a complex transaction. To avoid this, be sure to clear all transient storage at the end of any call to your contract. The use of transient storage for reentrancy guards that are cleared at the end of the call is safe. From 7115a63f2d4e159d41a5c517472af92e219d85f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Mati=C4=87?= Date: Wed, 13 Mar 2024 20:09:52 +0100 Subject: [PATCH 185/189] Update release checklist (#14812) --- ReleaseChecklist.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 167417a95d1a..5db731cfc591 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -50,7 +50,7 @@ At least a day before the release: ### Create the Release - [ ] Create a [release on GitHub](https://github.com/ethereum/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 and the binaries may not yet be available from all sources.**``. + 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. - [ ] Thank voluntary contributors in the GitHub release notes. Use ``scripts/list_contributors.sh v`` to get initial list of names. @@ -68,6 +68,7 @@ At least a day before the release: - [ ] 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. 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. + - [ ] 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. From a15da0a70f3a06822f9a07f0d991bf8250acfa53 Mon Sep 17 00:00:00 2001 From: Vishwa Mehta Date: Wed, 24 Jan 2024 18:49:14 +0530 Subject: [PATCH 186/189] Clean up docs to reflect changes in the behaviour of SELFDESTRUCT as per EIP-6780 --- docs/cheatsheet.rst | 2 +- docs/contracts/function-modifiers.rst | 16 +-- docs/contracts/inheritance.rst | 133 +++++++++++++++-------- docs/examples/micropayment.rst | 101 ++++++++++++----- docs/introduction-to-smart-contracts.rst | 20 +++- docs/metadata.rst | 4 +- docs/style-guide.rst | 9 +- docs/units-and-global-variables.rst | 20 +++- docs/using-the-compiler.rst | 4 +- 9 files changed, 207 insertions(+), 102 deletions(-) diff --git a/docs/cheatsheet.rst b/docs/cheatsheet.rst index e4f7beabd5f5..bc678c647d54 100644 --- a/docs/cheatsheet.rst +++ b/docs/cheatsheet.rst @@ -118,7 +118,7 @@ Contract-related - ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` or ``address payable`` - ``super``: a contract one level higher in the inheritance hierarchy -- ``selfdestruct(address payable recipient)``: destroy the current contract, sending its funds to the given address +- ``selfdestruct(address payable recipient)``: send all funds to the given address and (only on EVMs before Cancun or when invoked within the transaction creating the contract) destroy the contract. .. index:: type;name, type;creationCode, type;runtimeCode, type;interfaceId, type;min, type;max diff --git a/docs/contracts/function-modifiers.rst b/docs/contracts/function-modifiers.rst index 454e12ab65cf..2fd0357db054 100644 --- a/docs/contracts/function-modifiers.rst +++ b/docs/contracts/function-modifiers.rst @@ -19,7 +19,6 @@ if they are marked ``virtual``. For details, please see // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.1 <0.9.0; - // This will report a warning due to deprecated selfdestruct contract owned { constructor() { owner = payable(msg.sender); } @@ -41,16 +40,6 @@ if they are marked ``virtual``. For details, please see } } - contract destructible is owned { - // This contract inherits the `onlyOwner` modifier from - // `owned` and applies it to the `destroy` function, which - // causes that calls to `destroy` only have an effect if - // they are made by the stored owner. - function destroy() public onlyOwner { - selfdestruct(owner); - } - } - contract priced { // Modifiers can receive arguments: modifier costs(uint price) { @@ -60,7 +49,7 @@ if they are marked ``virtual``. For details, please see } } - contract Register is priced, destructible { + contract Register is priced, owned { mapping(address => bool) registeredAddresses; uint price; @@ -73,6 +62,9 @@ if they are marked ``virtual``. For details, please see registeredAddresses[msg.sender] = true; } + // This contract inherits the `onlyOwner` modifier from + // the `owned` contract. As a result, calls to `changePrice` will + // only take effect if they are made by the stored owner. function changePrice(uint price_) public onlyOwner { price = price_; } diff --git a/docs/contracts/inheritance.rst b/docs/contracts/inheritance.rst index 02ab19163ffb..fccfee609587 100644 --- a/docs/contracts/inheritance.rst +++ b/docs/contracts/inheritance.rst @@ -40,27 +40,27 @@ Details are given in the following example. // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; - // This will report a warning due to deprecated selfdestruct contract Owned { - constructor() { owner = payable(msg.sender); } address payable owner; + constructor() { owner = payable(msg.sender); } } - // Use `is` to derive from another contract. Derived // contracts can access all non-private members including // internal functions and state variables. These cannot be // accessed externally via `this`, though. - contract Destructible is Owned { + contract Emittable is Owned { + event Emitted(); + // The keyword `virtual` means that the function can change // its behavior in derived classes ("overriding"). - function destroy() virtual public { - if (msg.sender == owner) selfdestruct(owner); + function emitEvent() virtual public { + if (msg.sender == owner) + emit Emitted(); } } - // These abstract contracts are only provided to make the // interface known to the compiler. Note the function // without body. If a contract does not implement all @@ -69,37 +69,35 @@ Details are given in the following example. function lookup(uint id) public virtual returns (address adr); } - abstract contract NameReg { function register(bytes32 name) public virtual; function unregister() public virtual; } - // Multiple inheritance is possible. Note that `Owned` is - // also a base class of `Destructible`, yet there is only a single + // also a base class of `Emittable`, yet there is only a single // instance of `Owned` (as for virtual inheritance in C++). - contract Named is Owned, Destructible { + contract Named is Owned, Emittable { constructor(bytes32 name) { Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970); NameReg(config.lookup(1)).register(name); } // Functions can be overridden by another function with the same name and - // the same number/types of inputs. If the overriding function has different + // the same number/types of inputs. If the overriding function has different // types of output parameters, that causes an error. // Both local and message-based function calls take these overrides // into account. // If you want the function to override, you need to use the // `override` keyword. You need to specify the `virtual` keyword again // if you want this function to be overridden again. - function destroy() public virtual override { + function emitEvent() public virtual override { if (msg.sender == owner) { Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970); NameReg(config.lookup(1)).unregister(); // It is still possible to call a specific // overridden function. - Destructible.destroy(); + Emittable.emitEvent(); } } } @@ -108,93 +106,132 @@ Details are given in the following example. // If a constructor takes an argument, it needs to be // provided in the header or modifier-invocation-style at // the constructor of the derived contract (see below). - contract PriceFeed is Owned, Destructible, Named("GoldFeed") { + contract PriceFeed is Owned, Emittable, Named("GoldFeed") { + uint info; + function updateInfo(uint newInfo) public { if (msg.sender == owner) info = newInfo; } // Here, we only specify `override` and not `virtual`. // This means that contracts deriving from `PriceFeed` - // cannot change the behavior of `destroy` anymore. - function destroy() public override(Destructible, Named) { Named.destroy(); } + // cannot change the behavior of `emitEvent` anymore. + function emitEvent() public override(Emittable, Named) { Named.emitEvent(); } function get() public view returns(uint r) { return info; } - - uint info; } -Note that above, we call ``Destructible.destroy()`` to "forward" the -destruction request. The way this is done is problematic, as +Note that above, we call ``Emittable.emitEvent()`` to "forward" the +emit event request. The way this is done is problematic, as seen in the following example: .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; - // This will report a warning due to deprecated selfdestruct - contract owned { - constructor() { owner = payable(msg.sender); } + contract Owned { address payable owner; + constructor() { owner = payable(msg.sender); } } - contract Destructible is owned { - function destroy() public virtual { - if (msg.sender == owner) selfdestruct(owner); + contract Emittable is Owned { + event Emitted(); + + function emitEvent() virtual public { + if (msg.sender == owner) { + emit Emitted(); + } } } - contract Base1 is Destructible { - function destroy() public virtual override { /* do cleanup 1 */ Destructible.destroy(); } + contract Base1 is Emittable { + event Base1Emitted(); + function emitEvent() public virtual override { + /* Here, we emit an event to simulate some Base1 logic */ + emit Base1Emitted(); + Emittable.emitEvent(); + } } - contract Base2 is Destructible { - function destroy() public virtual override { /* do cleanup 2 */ Destructible.destroy(); } + contract Base2 is Emittable { + event Base2Emitted(); + function emitEvent() public virtual override { + /* Here, we emit an event to simulate some Base2 logic */ + emit Base2Emitted(); + Emittable.emitEvent(); + } } contract Final is Base1, Base2 { - function destroy() public override(Base1, Base2) { Base2.destroy(); } + event FinalEmitted(); + function emitEvent() public override(Base1, Base2) { + /* Here, we emit an event to simulate some Final logic */ + emit FinalEmitted(); + Base2.emitEvent(); + } } -A call to ``Final.destroy()`` will call ``Base2.destroy`` because we specify it +A call to ``Final.emitEvent()`` will call ``Base2.emitEvent`` because we specify it explicitly in the final override, but this function will bypass -``Base1.destroy``. The way around this is to use ``super``: +``Base1.emitEvent``, resulting in the following sequence of events: +``FinalEmitted -> Base2Emitted -> Emitted``, instead of the expected sequence: +``FinalEmitted -> Base2Emitted -> Base1Emitted -> Emitted``. +The way around this is to use ``super``: .. code-block:: solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; - // This will report a warning due to deprecated selfdestruct - contract owned { - constructor() { owner = payable(msg.sender); } + contract Owned { address payable owner; + constructor() { owner = payable(msg.sender); } } - contract Destructible is owned { - function destroy() virtual public { - if (msg.sender == owner) selfdestruct(owner); + contract Emittable is Owned { + event Emitted(); + + function emitEvent() virtual public { + if (msg.sender == owner) { + emit Emitted(); + } } } - contract Base1 is Destructible { - function destroy() public virtual override { /* do cleanup 1 */ super.destroy(); } + contract Base1 is Emittable { + event Base1Emitted(); + function emitEvent() public virtual override { + /* Here, we emit an event to simulate some Base1 logic */ + emit Base1Emitted(); + super.emitEvent(); + } } - contract Base2 is Destructible { - function destroy() public virtual override { /* do cleanup 2 */ super.destroy(); } + contract Base2 is Emittable { + event Base2Emitted(); + function emitEvent() public virtual override { + /* Here, we emit an event to simulate some Base2 logic */ + emit Base2Emitted(); + super.emitEvent(); + } } contract Final is Base1, Base2 { - function destroy() public override(Base1, Base2) { super.destroy(); } + event FinalEmitted(); + function emitEvent() public override(Base1, Base2) { + /* Here, we emit an event to simulate some Final logic */ + emit FinalEmitted(); + super.emitEvent(); + } } -If ``Base2`` calls a function of ``super``, it does not simply +If ``Final`` calls a function of ``super``, it does not simply call this function on one of its base contracts. Rather, it calls this function on the next base contract in the final -inheritance graph, so it will call ``Base1.destroy()`` (note that +inheritance graph, so it will call ``Base1.emitEvent()`` (note that the final inheritance sequence is -- starting with the most -derived contract: Final, Base2, Base1, Destructible, owned). +derived contract: Final, Base2, Base1, Emittable, Owned). The actual function that is called when using super is not known in the context of the class where it is used, although its type is known. This is similar for ordinary diff --git a/docs/examples/micropayment.rst b/docs/examples/micropayment.rst index 50f28005bfd6..c3aef0d653da 100644 --- a/docs/examples/micropayment.rst +++ b/docs/examples/micropayment.rst @@ -55,7 +55,7 @@ as it provides a number of other security benefits. What to Sign ------------ -For a contract that fulfils payments, the signed message must include: +For a contract that fulfills payments, the signed message must include: 1. The recipient's address. 2. The amount to be transferred. @@ -80,6 +80,10 @@ the contract's address itself will be accepted. You can find an example of this in the first two lines of the ``claimPayment()`` function of the full contract at the end of this section. +Furthermore, instead of destroying the contract by calling ``selfdestruct``, +which is currently deprecated, we will disable the contract's functionalities by freezing it, +resulting in the reversion of any call after it being frozen. + Packing arguments ----------------- @@ -144,30 +148,54 @@ The full contract // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; - // This will report a warning due to deprecated selfdestruct - contract ReceiverPays { - address owner = msg.sender; + contract Owned { + address payable owner; + constructor() { + owner = payable(msg.sender); + } + } + + contract Freezable is Owned { + bool private _frozen = false; + + modifier notFrozen() { + require(!_frozen, "Inactive Contract."); + _; + } + + function freeze() internal { + if (msg.sender == owner) + _frozen = true; + } + } + + contract ReceiverPays is Freezable { mapping(uint256 => bool) usedNonces; constructor() payable {} - function claimPayment(uint256 amount, uint256 nonce, bytes memory signature) external { + function claimPayment(uint256 amount, uint256 nonce, bytes memory signature) + external + notFrozen + { require(!usedNonces[nonce]); usedNonces[nonce] = true; // 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); } - /// destroy the contract and reclaim the leftover funds. - function shutdown() external { + /// freeze the contract and reclaim the leftover funds. + function shutdown() + external + notFrozen + { require(msg.sender == owner); - selfdestruct(payable(msg.sender)); + freeze(); + payable(msg.sender).transfer(address(this).balance); } /// signature methods. @@ -196,7 +224,6 @@ The full contract returns (address) { (uint8 v, bytes32 r, bytes32 s) = splitSignature(sig); - return ecrecover(message, v, r, s); } @@ -305,7 +332,7 @@ Closing the Payment Channel When Bob is ready to receive his funds, it is time to close the payment channel by calling a ``close`` function on the smart contract. Closing the channel pays the recipient the Ether they are owed and -destroys the contract, sending any remaining Ether back to Alice. To +deactivates the contract by freezing it, sending any remaining Ether back to Alice. To close the channel, Bob needs to provide a message signed by Alice. The smart contract must verify that the message contains a valid signature from the sender. @@ -320,7 +347,7 @@ they could provide a message with a lower amount and cheat the recipient out of The function verifies the signed message matches the given parameters. If everything checks out, the recipient is sent their portion of the Ether, -and the sender is sent the rest via a ``selfdestruct``. +and the sender is sent the remaining funds via a ``transfer``. You can see the ``close`` function in the full contract. Channel Expiration @@ -342,11 +369,24 @@ The full contract // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; - // This will report a warning due to deprecated selfdestruct - contract SimplePaymentChannel { - address payable public sender; // The account sending payments. - address payable public recipient; // The account receiving the payments. - uint256 public expiration; // Timeout in case the recipient never closes. + + contract Frozeable { + bool private _frozen = false; + + modifier notFrozen() { + require(!_frozen, "Inactive Contract."); + _; + } + + function freeze() internal { + _frozen = true; + } + } + + contract SimplePaymentChannel is Frozeable { + address payable public sender; // The account sending payments. + address payable public recipient; // The account receiving the payments. + uint256 public expiration; // Timeout in case the recipient never closes. constructor (address payable recipientAddress, uint256 duration) payable @@ -359,16 +399,23 @@ The full contract /// the recipient can close the channel at any time by presenting a /// signed amount from the sender. the recipient will be sent that amount, /// and the remainder will go back to the sender - function close(uint256 amount, bytes memory signature) external { + function close(uint256 amount, bytes memory signature) + external + notFrozen + { require(msg.sender == recipient); require(isValidSignature(amount, signature)); recipient.transfer(amount); - selfdestruct(sender); + freeze(); + sender.transfer(address(this).balance); } /// the sender can extend the expiration at any time - function extend(uint256 newExpiration) external { + function extend(uint256 newExpiration) + external + notFrozen + { require(msg.sender == sender); require(newExpiration > expiration); @@ -377,9 +424,13 @@ The full contract /// if the timeout is reached without the recipient closing the channel, /// then the Ether is released back to the sender. - function claimTimeout() external { + function claimTimeout() + external + notFrozen + { require(block.timestamp >= expiration); - selfdestruct(sender); + freeze(); + sender.transfer(address(this).balance); } function isValidSignature(uint256 amount, bytes memory signature) @@ -388,14 +439,12 @@ The full contract returns (bool) { bytes32 message = prefixed(keccak256(abi.encodePacked(this, amount))); - // check that the signature is from the payment sender return recoverSigner(message, signature) == sender; } /// All functions below this are just taken from the chapter /// 'creating and verifying signatures' chapter. - function splitSignature(bytes memory sig) internal pure @@ -411,7 +460,6 @@ The full contract // final byte (first byte of the next 32 bytes) v := byte(0, mload(add(sig, 96))) } - return (v, r, s); } @@ -421,7 +469,6 @@ The full contract returns (address) { (uint8 v, bytes32 r, bytes32 s) = splitSignature(sig); - return ecrecover(message, v, r, s); } diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 3c43e5f193b2..a31b06616aee 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -563,9 +563,23 @@ idea, but it is potentially dangerous, as if someone sends Ether to removed contracts, the Ether is forever lost. .. warning:: - From version 0.8.18 and up, the use of ``selfdestruct`` in both Solidity and Yul will trigger a - deprecation warning, since the ``SELFDESTRUCT`` opcode will eventually undergo breaking changes in behavior - as stated in `EIP-6049 `_. + From ``EVM >= Cancun`` onwards, ``selfdestruct`` will **only** send all Ether in the account to the given recipient and not destroy the contract. + However, when ``selfdestruct`` is called in the same transaction that creates the contract calling it, + the behaviour of ``selfdestruct`` before Cancun hardfork (i.e., ``EVM <= Shanghai``) is preserved and will destroy the current contract, + deleting any data, including storage keys, code and the account itself. + See `EIP-6780 `_ for more details. + + The new behaviour is the result of a network-wide change that affects all contracts present on + the Ethereum mainnet and testnets. + It is important to note that this change is dependent on the EVM version of the chain on which + the contract is deployed. + The ``--evm-version`` setting used when compiling the contract has no bearing on it. + + Also, note that the ``selfdestruct`` opcode has been deprecated in Solidity version 0.8.18, + as recommended by `EIP-6049 `_. + The deprecation is still in effect and the compiler will still emit warnings on its use. + 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. .. warning:: Even if a contract is removed by ``selfdestruct``, it is still part of the diff --git a/docs/metadata.rst b/docs/metadata.rst index ede16a73fd8e..a44d9875388a 100644 --- a/docs/metadata.rst +++ b/docs/metadata.rst @@ -175,9 +175,9 @@ explanatory purposes. }, // Required: Compilation source files/source units, keys are file paths "sources": { - "destructible": { + "settable": { // Required (unless "url" is used): literal contents of the source file - "content": "contract destructible is owned { function destroy() { if (msg.sender == owner) selfdestruct(owner); } }", + "content": "contract settable is owned { uint256 private x = 0; function set(uint256 _x) public { if (msg.sender == owner) x = _x; } }", // Required: keccak256 hash of the source file "keccak256": "0x234..." }, diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 427ede5ee3da..0ac890cb3478 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -662,10 +662,11 @@ Yes: return balanceOf[from]; } - function shutdown() public onlyOwner { - selfdestruct(owner); + function increment(uint x) public onlyOwner pure returns (uint) { + return x + 1; } + No: .. code-block:: solidity @@ -674,8 +675,8 @@ No: return balanceOf[from]; } - function shutdown() onlyOwner public { - selfdestruct(owner); + function increment(uint x) onlyOwner public pure returns (uint) { + return x + 1; } For long function declarations, it is recommended to drop each argument onto diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index a0422c32dfa9..a743441d564d 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -339,9 +339,23 @@ Contract-related Furthermore, all functions of the current contract are callable directly including the current function. .. warning:: - From version 0.8.18 and up, the use of ``selfdestruct`` in both Solidity and Yul will trigger a - deprecation warning, since the ``SELFDESTRUCT`` opcode will eventually undergo breaking changes in behavior - as stated in `EIP-6049 `_. + From ``EVM >= Cancun`` onwards, ``selfdestruct`` will **only** send all Ether in the account to the given recipient and not destroy the contract. + However, when ``selfdestruct`` is called in the same transaction that creates the contract calling it, + the behaviour of ``selfdestruct`` before Cancun hardfork (i.e., ``EVM <= Shanghai``) is preserved and will destroy the current contract, + deleting any data, including storage keys, code and the account itself. + See `EIP-6780 `_ for more details. + + The new behaviour is the result of a network-wide change that affects all contracts present on + the Ethereum mainnet and testnets. + It is important to note that this change is dependent on the EVM version of the chain on which + the contract is deployed. + The ``--evm-version`` setting used when compiling the contract has no bearing on it. + + Also, note that the ``selfdestruct`` opcode has been deprecated in Solidity version 0.8.18, + as recommended by `EIP-6049 `_. + The deprecation is still in effect and the compiler will still emit warnings on its use. + 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. .. note:: Prior to version 0.5.0, there was a function called ``suicide`` with the same diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 97abbdba9df6..219c2f42ed0b 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -236,12 +236,12 @@ Input Description // `--allow-paths `. ] }, - "destructible": + "settable": { // Optional: keccak256 hash of the source file "keccak256": "0x234...", // Required (unless "urls" is used): literal contents of the source file - "content": "contract destructible is owned { function shutdown() { if (msg.sender == owner) selfdestruct(owner); } }" + "content": "contract settable is owned { uint256 private x = 0; function set(uint256 _x) public { if (msg.sender == owner) x = _x; } }" }, "myFile.sol_json.ast": { From a547f615c271dc357e753abb5c95c24b9d3d0e0b Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 13 Mar 2024 22:31:14 +0100 Subject: [PATCH 187/189] Disable gp2 and zeppelin ext tests --- .circleci/config.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e437e1d38759..7fe8967b4435 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1910,7 +1910,8 @@ workflows: - t_ext: *job_ems_compile_ext_colony - t_ext: *job_native_compile_ext_gnosis - - t_ext: *job_native_test_ext_zeppelin + # TODO: Re-enable once bumped to use Cancun supported hardhat version + # - t_ext: *job_native_test_ext_zeppelin - t_ext: *job_native_test_ext_ens - t_ext: *job_native_test_ext_yield_liquidator - t_ext: *job_native_test_ext_perpetual_pools @@ -1918,7 +1919,8 @@ workflows: - t_ext: *job_native_test_ext_prb_math - t_ext: *job_native_test_ext_elementfi - t_ext: *job_native_test_ext_brink - - t_ext: *job_native_compile_ext_gp2 + # TODO: Re-enable once bumped to use Cancun supported hardhat version + # - t_ext: *job_native_compile_ext_gp2 - t_ext: *job_native_compile_ext_euler - t_ext: *job_native_compile_ext_pool_together # TODO: Dropping the external tests below since they are based on old forks and @@ -1932,14 +1934,16 @@ workflows: requires: - t_ems_compile_ext_colony - t_native_compile_ext_gnosis - - t_native_test_ext_zeppelin + # TODO: Re-enable once bumped to use Cancun supported hardhat version + # - t_native_test_ext_zeppelin - t_native_test_ext_ens - t_native_test_ext_yield_liquidator - t_native_test_ext_perpetual_pools - t_native_test_ext_uniswap - t_native_test_ext_elementfi - t_native_test_ext_brink - - t_native_compile_ext_gp2 + # TODO: Re-enable once bumped to use Cancun supported hardhat version + # - t_native_compile_ext_gp2 # TODO: Dropping prb-math from the benchmarks since it is not implemented yet # in the new Foundry external testing infrastructure. # - t_native_test_ext_prb_math From fbeef890ea61c1d07b716a90c5e7548cf791fba7 Mon Sep 17 00:00:00 2001 From: r0qs Date: Wed, 13 Mar 2024 02:01:35 +0100 Subject: [PATCH 188/189] Set cancun hardfork in hardhat stack traces tests --- .circleci/config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7fe8967b4435..dc4cec201981 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1469,6 +1469,9 @@ jobs: # NOTE: This is expected to work without running `pnpm build` first. cd hardhat/packages/hardhat-core + # TODO: temporarily set hardhat stack traces tests to use cancun hardfork + # Remove this when hardhat switch to cancun by default: https://github.com/NomicFoundation/hardhat/issues/4851 + sed -i 's/hardfork: "shanghai",/hardfork: "cancun",/' test/internal/hardhat-network/stack-traces/execution.ts pnpm test - matrix_notify_failure_unless_pr From a388ccb2c464493ecb62797462df6e9d9e6cf047 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Mon, 4 Mar 2024 15:25:05 -0300 Subject: [PATCH 189/189] Make cancun the default EVM version --- .circleci/soltest_all.sh | 2 +- Changelog.md | 1 + docs/using-the-compiler.rst | 8 +- liblangutil/EVMVersion.h | 2 +- libyul/AsmAnalysis.cpp | 9 +- scripts/error_codes.py | 1 - scripts/externalTests/common.sh | 2 +- scripts/externalTests/runners/base.py | 2 +- scripts/tests.sh | 2 +- .../debug_info_in_yul_snippet_escaping/output | 22 ++--- test/cmdlineTests/metadata/output | 2 +- test/cmdlineTests/name_simplifier/output | 10 +-- .../standard_metadata/output.json | 2 +- .../output.json | 2 +- .../viair_subobject_optimization/output | 82 +++++++------------ .../output | 2 +- .../yul_string_format_ascii/output.json | 6 +- .../yul_string_format_ascii_long/output.json | 6 +- test/libsolidity/StandardCompiler.cpp | 4 +- test/libsolidity/gasTests/abiv2.sol | 6 +- test/libsolidity/gasTests/abiv2_optimised.sol | 4 +- .../abi_encode_calldata_slice.sol | 12 +-- .../abi_encode_calldata_slice.sol | 12 +-- .../abiEncoderV2/abi_encode_v2.sol | 6 +- .../abiEncoderV2/calldata_array.sol | 2 +- .../calldata_overlapped_dynamic_arrays.sol | 6 +- .../abiEncoderV2/storage_array_encoding.sol | 12 +-- .../abi_decode_simple_storage.sol | 6 +- .../copying/bytes_storage_to_storage.sol | 30 +++---- .../copy_byte_array_in_struct_to_storage.sol | 12 +-- .../function_type_array_to_storage.sol | 12 +-- .../copying/storage_memory_nested_bytes.sol | 6 +- .../array/pop/byte_array_pop_masking_long.sol | 6 +- .../byte_array_to_storage_cleanup.sol | 12 +-- .../bytes_in_constructors_packer.sol | 12 +-- .../bytes_in_constructors_unpacker.sol | 12 +-- .../externalContracts/FixedFeeRegistrar.sol | 12 +-- .../externalContracts/base64.sol | 12 +-- .../externalContracts/deposit_contract.sol | 36 ++++---- .../externalContracts/strings.sol | 12 +-- .../external_call_to_nonexisting.sol | 12 +-- ...ernal_call_to_nonexisting_debugstrings.sol | 12 +-- .../inlineAssembly/blobhash_pre_cancun.sol | 2 +- .../transient_storage_low_level_calls.sol | 6 ++ .../transient_storage_selfdestruct.sol | 10 +++ .../tstore_hidden_staticcall.sol | 3 + .../salted_create/prediction_example.sol | 2 +- .../structs/copy_from_mapping.sol | 6 +- .../copy_substructures_from_mapping.sol | 6 +- .../structs/copy_substructures_to_mapping.sol | 18 ++-- .../semanticTests/structs/copy_to_mapping.sol | 24 +++--- .../semanticTests/various/address_code.sol | 12 +-- .../semanticTests/various/create_calldata.sol | 12 +-- .../various/selfdestruct_post_cancun.sol | 9 +- ...uct_post_cancun_multiple_beneficiaries.sol | 14 ++++ .../selfdestruct_post_cancun_redeploy.sol | 20 +++-- .../skip_dynamic_types_for_structs.sol | 6 +- .../blobhash_pre_cancun_not_declared.sol | 4 +- .../inlineAssembly/mcopy_pre_cancun.sol | 4 +- .../transient_storage_invalid_pre_cancun.sol | 6 +- .../types/magic_block_blobbasefee_error.sol | 2 +- 61 files changed, 298 insertions(+), 299 deletions(-) diff --git a/.circleci/soltest_all.sh b/.circleci/soltest_all.sh index 40ee75b270cd..009852710e51 100755 --- a/.circleci/soltest_all.sh +++ b/.circleci/soltest_all.sh @@ -36,7 +36,7 @@ DEFAULT_EVM_VALUES=(constantinople petersburg istanbul berlin london paris shang # set EVM_VALUES to the default values. IFS=" " read -ra EVM_VALUES <<< "${1:-${DEFAULT_EVM_VALUES[@]}}" -DEFAULT_EVM=shanghai +DEFAULT_EVM=cancun OPTIMIZE_VALUES=(0 1) # Run for ABI encoder v1, without SMTChecker tests. diff --git a/Changelog.md b/Changelog.md index 8c6df568e9ce..ba5dff678755 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Language Features: Compiler Features: * Code Generator: Use ``MCOPY`` instead of ``MLOAD``/``MSTORE`` loop when copying byte arrays. * Yul Analyzer: Emit transient storage warning only for the first occurrence of ``tstore``. + * EVM: Set default EVM version to ``cancun``. Bugfixes: diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 219c2f42ed0b..875c15b09d6d 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -174,9 +174,9 @@ at each version. Backward compatibility is not guaranteed between each version. - The block's base fee (`EIP-3198 `_ and `EIP-1559 `_) can be accessed via the global ``block.basefee`` or ``basefee()`` in inline assembly. - ``paris`` - Introduces ``prevrandao()`` and ``block.prevrandao``, and changes the semantics of the now deprecated ``block.difficulty``, disallowing ``difficulty()`` in inline assembly (see `EIP-4399 `_). -- ``shanghai`` (**default**) +- ``shanghai`` - Smaller code size and gas savings due to the introduction of ``push0`` (see `EIP-3855 `_). -- ``cancun`` +- ``cancun`` (**default**) - The block's blob base fee (`EIP-7516 `_ and `EIP-4844 `_) can be accessed via the global ``block.blobbasefee`` or ``blobbasefee()`` in inline assembly. - 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 `_). @@ -344,8 +344,8 @@ Input Description // Version of the EVM to compile for. // Affects type checking and code generation. Can be homestead, // tangerineWhistle, spuriousDragon, byzantium, constantinople, - // petersburg, istanbul, berlin, london, paris or shanghai (default) - "evmVersion": "shanghai", + // petersburg, istanbul, berlin, london, paris, shanghai or cancun (default) + "evmVersion": "cancun", // Optional: Change compilation pipeline to go through the Yul intermediate representation. // This is false by default. "viaIR": true, diff --git a/liblangutil/EVMVersion.h b/liblangutil/EVMVersion.h index 992933a898bf..58b54f3dcaa7 100644 --- a/liblangutil/EVMVersion.h +++ b/liblangutil/EVMVersion.h @@ -119,7 +119,7 @@ class EVMVersion: EVMVersion(Version _version): m_version(_version) {} - Version m_version = Version::Shanghai; + Version m_version = Version::Cancun; }; } diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 0efaa12c2c8a..1972d2674516 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -752,14 +752,11 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio else if (_instr == evmasm::Instruction::BLOBBASEFEE && !m_evmVersion.hasBlobBaseFee()) errorForVM(6679_error, "only available for Cancun-compatible"); else if (_instr == evmasm::Instruction::BLOBHASH && !m_evmVersion.hasBlobHash()) - // TODO: Change this assertion to an error, similar to the ones above, when Cancun becomes the default EVM version. - yulAssert(false); + errorForVM(8314_error, "only available for Cancun-compatible"); else if (_instr == evmasm::Instruction::MCOPY && !m_evmVersion.hasMcopy()) - // TODO: Change this assertion to an error, similar to the ones above, when Cancun becomes the default EVM version. - yulAssert(false); + errorForVM(7755_error, "only available for Cancun-compatible"); else if ((_instr == evmasm::Instruction::TSTORE || _instr == evmasm::Instruction::TLOAD) && !m_evmVersion.supportsTransientStorage()) - // TODO: Change this assertion to an error, similar to the ones above, when Cancun becomes the default EVM version. - yulAssert(false); + errorForVM(6243_error, "only available for Cancun-compatible"); else if (_instr == evmasm::Instruction::PC) m_errorReporter.error( 2450_error, diff --git a/scripts/error_codes.py b/scripts/error_codes.py index 8ffb7f2160f4..27e7b1102bbe 100755 --- a/scripts/error_codes.py +++ b/scripts/error_codes.py @@ -202,7 +202,6 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): "4591", # "There are more than 256 warnings. Ignoring the rest." # Due to 3805, the warning lists look different for different compiler builds. "1834", # Unimplemented feature error, as we do not test it anymore via cmdLineTests - "6679", # blobbasefee being used in inline assembly for EVMVersion < cancun "1180", # SMTChecker, covered by CL tests "2339", # SMTChecker, covered by CL tests "2961", # SMTChecker, covered by CL tests diff --git a/scripts/externalTests/common.sh b/scripts/externalTests/common.sh index de9b8da96d8c..8bc9bdf7bd40 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=shanghai +CURRENT_EVM_VERSION=cancun AVAILABLE_PRESETS=( legacy-no-optimize diff --git a/scripts/externalTests/runners/base.py b/scripts/externalTests/runners/base.py index c3807c528c38..cd94fab6b80b 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 = "shanghai" +CURRENT_EVM_VERSION: str = "cancun" @dataclass class TestConfig: diff --git a/scripts/tests.sh b/scripts/tests.sh index 9df40e385c1a..26eaf4b4b15b 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -115,7 +115,7 @@ do for vm in $EVM_VERSIONS do FORCE_ABIV1_RUNS="no" - if [[ "$vm" == "shanghai" ]] + if [[ "$vm" == "cancun" ]] then FORCE_ABIV1_RUNS="no yes" # run both in paris fi diff --git a/test/cmdlineTests/debug_info_in_yul_snippet_escaping/output b/test/cmdlineTests/debug_info_in_yul_snippet_escaping/output index 4c90d9054d2a..0bd6285b785e 100644 --- a/test/cmdlineTests/debug_info_in_yul_snippet_escaping/output +++ b/test/cmdlineTests/debug_info_in_yul_snippet_escaping/output @@ -181,11 +181,7 @@ object "D_27" { function copy_memory_to_memory_with_cleanup(src, dst, length) { - let i := 0 - for { } lt(i, length) { i := add(i, 32) } - { - mstore(add(dst, i), mload(add(src, i))) - } + mcopy(dst, src, length) mstore(add(dst, length), 0) } @@ -470,8 +466,7 @@ object "D_27" { } mstore(_2, newFreePtr) mstore(memPtr, 2) - let _6 := 32 - mstore(add(memPtr, _6), "/*") + mstore(add(memPtr, 32), "/*") let memPtr_1 := mload(_2) let newFreePtr_1 := add(memPtr_1, 96) if or(gt(newFreePtr_1, /** @src 0:446:491 "new /// @src 0:149:156 \"new C()\"..." */ _5), /** @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." */ lt(newFreePtr_1, memPtr_1)) @@ -482,17 +477,14 @@ object "D_27" { } mstore(_2, newFreePtr_1) mstore(memPtr_1, 39) - mstore(add(memPtr_1, _6), 0x2f2a2a204073726320303a39363a313635202022636f6e74726163742044207b) + let _6 := add(memPtr_1, 32) + mstore(_6, 0x2f2a2a204073726320303a39363a313635202022636f6e74726163742044207b) mstore(add(memPtr_1, _2), shl(200, 0x2e2e2e22202a2f)) let memPos := mload(_2) - mstore(memPos, _6) + mstore(memPos, 32) let length := mload(memPtr_1) - mstore(add(memPos, _6), length) - let i := 0 - for { } lt(i, length) { i := add(i, _6) } - { - mstore(add(add(memPos, i), _2), mload(add(add(memPtr_1, i), _6))) - } + mstore(add(memPos, 32), length) + mcopy(add(memPos, _2), _6, length) mstore(add(add(memPos, length), _2), 0) return(memPos, add(sub(add(memPos, and(add(length, 31), not(31))), memPos), _2)) } diff --git a/test/cmdlineTests/metadata/output b/test/cmdlineTests/metadata/output index bf1e750c95f0..b6a684b844f3 100644 --- a/test/cmdlineTests/metadata/output +++ b/test/cmdlineTests/metadata/output @@ -1,4 +1,4 @@ ======= metadata/input.sol:C ======= Metadata: -{"compiler":{"version": ""},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"metadata/input.sol":"C"},"evmVersion":"shanghai","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"metadata/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":{"metadata/input.sol":"C"},"evmVersion":"cancun","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"metadata/input.sol":{"keccak256":"0x5cf617b1707a484e3c4bd59643013dec76ab7d75900b46855214729ae3e0ceb0","license":"GPL-3.0","urls":["bzz-raw://ac418a02dfadf87234150d3568f33269e3f49460345cb39300e017a6d755eff2","dweb:/ipfs/QmQq3owBu25x2WV46HB1WyKzJpxiAPecU7eMKqtXCF7eeS"]}},"version":1} diff --git a/test/cmdlineTests/name_simplifier/output b/test/cmdlineTests/name_simplifier/output index ed4c6e5c02e9..0a461e3e4af4 100644 --- a/test/cmdlineTests/name_simplifier/output +++ b/test/cmdlineTests/name_simplifier/output @@ -98,9 +98,9 @@ object "C_59" { } mstore(_9, newFreePtr_2) mstore(memPtr_1, 100) - mstore(add(memPtr_1, _3), "longstringlongstringlongstringlo") + let _11 := add(memPtr_1, _3) + mstore(_11, "longstringlongstringlongstringlo") mstore(add(memPtr_1, _9), "ngstringlongstringlongstringlong") - let _11 := 96 mstore(add(memPtr_1, 96), "stringlongstringlongstringlongst") mstore(add(memPtr_1, 128), "ring") let memPos := mload(_9) @@ -108,11 +108,7 @@ object "C_59" { mstore(add(memPos, _3), _9) let length := mload(memPtr_1) mstore(add(memPos, _9), length) - let i := 0 - for { } lt(i, length) { i := add(i, _3) } - { - mstore(add(add(memPos, i), _11), mload(add(add(memPtr_1, i), _3))) - } + mcopy(add(memPos, 96), _11, length) mstore(add(add(memPos, length), 96), 0) return(memPos, add(sub(add(memPos, and(add(length, 31), _8)), memPos), 96)) } diff --git a/test/cmdlineTests/standard_metadata/output.json b/test/cmdlineTests/standard_metadata/output.json index 4429466676d1..955d30c34475 100644 --- a/test/cmdlineTests/standard_metadata/output.json +++ b/test/cmdlineTests/standard_metadata/output.json @@ -5,7 +5,7 @@ { "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\":\"shanghai\",\"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\":\"cancun\",\"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_outputs_on_compilation_error/output.json b/test/cmdlineTests/standard_outputs_on_compilation_error/output.json index ac01fb1eba9c..aae6d02f6b41 100644 --- a/test/cmdlineTests/standard_outputs_on_compilation_error/output.json +++ b/test/cmdlineTests/standard_outputs_on_compilation_error/output.json @@ -40,7 +40,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\":\"shanghai\",\"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\":\"cancun\",\"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": [], diff --git a/test/cmdlineTests/viair_subobject_optimization/output b/test/cmdlineTests/viair_subobject_optimization/output index 4804d138434c..5090c282bb24 100644 --- a/test/cmdlineTests/viair_subobject_optimization/output +++ b/test/cmdlineTests/viair_subobject_optimization/output @@ -117,77 +117,70 @@ sub_0: assembly { tag_3: jumpi(tag_7, callvalue) jumpi(tag_7, slt(add(not(0x03), calldatasize), 0x00)) - not(0x1f) /* "viair_subobject_optimization/input.sol":745:765 type(C).creationCode */ dataSize(sub_0) /* "viair_subobject_optimization/input.sol":669:772 contract D {... */ - 0x3f + swap1 + not(0x1f) + swap1 dup2 + 0x3f + dup5 add - dup3 and - dup4 + dup2 add - swap1 + swap2 + dup2 dup4 - dup3 lt 0xffffffffffffffff - dup4 + dup5 gt or tag_9 jumpi - swap3 - swap1 - 0x40 - swap1 0x40 + swap3 + dup4 mstore /* "viair_subobject_optimization/input.sol":745:765 type(C).creationCode */ dup4 dup3 mstore 0x20 + dup3 + add swap4 dataOffset(sub_0) - 0x20 - dup5 - add + dup6 codecopy /* "viair_subobject_optimization/input.sol":669:772 contract D {... */ - mload(0x40) + 0x1f + dup4 + mload + swap5 + dup6 swap4 - dup5 - swap3 /* "viair_subobject_optimization/input.sol":745:765 type(C).creationCode */ 0x20 /* "viair_subobject_optimization/input.sol":669:772 contract D {... */ - dup5 + dup6 mstore - dup1 mload - swap3 - dup4 + dup1 + swap2 + dup2 /* "viair_subobject_optimization/input.sol":745:765 type(C).creationCode */ 0x20 /* "viair_subobject_optimization/input.sol":669:772 contract D {... */ - dup7 + dup8 add mstore - 0x00 - tag_11: - dup5 - dup2 - lt - tag_12 - jumpi - dup6 - 0x40 - dup2 - dup10 - 0x1f - dup10 + dup7 + dup7 + add + mcopy 0x00 dup6 dup3 @@ -203,25 +196,6 @@ sub_0: assembly { add swap1 return - tag_12: - dup3 - dup2 - add - dup5 - add - mload - dup9 - dup3 - add - dup4 - add - mstore - dup8 - swap6 - pop - dup4 - add - jump(tag_11) tag_9: mstore(0x00, shl(0xe0, 0x4e487b71)) mstore(0x04, 0x41) 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 9ee68107929a..6873abe60e47 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":{"yul_optimizer_steps_without_optimize_empty_sequence/input.sol":"C"},"evmVersion":"shanghai","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":{"yul_optimizer_steps_without_optimize_empty_sequence/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":{"yul_optimizer_steps_without_optimize_empty_sequence/input.sol":"C"},"evmVersion":"cancun","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":{"yul_optimizer_steps_without_optimize_empty_sequence/input.sol":{"keccak256":"0x3fc910e345ce1ee62bfa6b0f66931ee632c08265b25b6139cfbbfe4d2f8d5dd8","license":"GPL-3.0","urls":["bzz-raw://e557e9ad2c2e420a669c06ae456b0b790d77d2d6d492cd8540e6b244388a5140","dweb:/ipfs/QmaNiZmC2Mo3YxGiehs1n3dVTjZwD7FguX7EUtpeshMVuR"]}},"version":1} diff --git a/test/cmdlineTests/yul_string_format_ascii/output.json b/test/cmdlineTests/yul_string_format_ascii/output.json index 1a9612c777b8..edb76c0cd405 100644 --- a/test/cmdlineTests/yul_string_format_ascii/output.json +++ b/test/cmdlineTests/yul_string_format_ascii/output.json @@ -97,11 +97,7 @@ object \"C_11\" { function copy_memory_to_memory_with_cleanup(src, dst, length) { - let i := 0 - for { } lt(i, length) { i := add(i, 32) } - { - mstore(add(dst, i), mload(add(src, i))) - } + mcopy(dst, src, length) mstore(add(dst, length), 0) } diff --git a/test/cmdlineTests/yul_string_format_ascii_long/output.json b/test/cmdlineTests/yul_string_format_ascii_long/output.json index 5adeabfdffcf..36d6cfd954e9 100644 --- a/test/cmdlineTests/yul_string_format_ascii_long/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_long/output.json @@ -97,11 +97,7 @@ object \"C_11\" { function copy_memory_to_memory_with_cleanup(src, dst, length) { - let i := 0 - for { } lt(i, length) { i := add(i, 32) } - { - mstore(add(dst, i), mload(add(src, i))) - } + mcopy(dst, src, length) mstore(add(dst, length), 0) } diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index ab013692d5ea..7fafa2a62e08 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -1050,9 +1050,11 @@ BOOST_AUTO_TEST_CASE(evm_version) BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"paris\"") != std::string::npos); result = compile(inputForVersion("\"evmVersion\": \"shanghai\",")); BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"shanghai\"") != std::string::npos); + result = compile(inputForVersion("\"evmVersion\": \"cancun\",")); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"cancun\"") != std::string::npos); // test default result = compile(inputForVersion("")); - BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"shanghai\"") != std::string::npos); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"cancun\"") != std::string::npos); // test invalid result = compile(inputForVersion("\"evmVersion\": \"invalid\",")); BOOST_CHECK(result["errors"][0]["message"].asString() == "Invalid EVM version requested."); diff --git a/test/libsolidity/gasTests/abiv2.sol b/test/libsolidity/gasTests/abiv2.sol index aacb333bfeca..6fa822fade47 100644 --- a/test/libsolidity/gasTests/abiv2.sol +++ b/test/libsolidity/gasTests/abiv2.sol @@ -14,9 +14,9 @@ contract C { } // ---- // creation: -// codeDepositCost: 1213200 -// executionCost: 1259 -// totalCost: 1214459 +// codeDepositCost: 1208000 +// executionCost: 1252 +// totalCost: 1209252 // external: // a(): 2425 // b(uint256): infinite diff --git a/test/libsolidity/gasTests/abiv2_optimised.sol b/test/libsolidity/gasTests/abiv2_optimised.sol index 1ab8803d28b2..8f407e6ede24 100644 --- a/test/libsolidity/gasTests/abiv2_optimised.sol +++ b/test/libsolidity/gasTests/abiv2_optimised.sol @@ -17,9 +17,9 @@ contract C { // optimize-yul: true // ---- // creation: -// codeDepositCost: 639000 +// codeDepositCost: 634600 // executionCost: 668 -// totalCost: 639668 +// totalCost: 635268 // external: // a(): 2283 // b(uint256): 4649 diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_calldata_slice.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_calldata_slice.sol index f548c50bcf52..3dd5dd269340 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_calldata_slice.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_encode_calldata_slice.sol @@ -59,10 +59,10 @@ contract C { // EVMVersion: >homestead // ---- // test_bytes() -> -// gas irOptimized: 321332 -// gas legacy: 319111 -// gas legacyOptimized: 269317 +// gas irOptimized: 306908 +// gas legacy: 305827 +// gas legacyOptimized: 255013 // test_uint256() -> -// gas irOptimized: 447646 -// gas legacy: 433627 -// gas legacyOptimized: 365410 +// gas irOptimized: 435054 +// gas legacy: 421315 +// gas legacyOptimized: 352838 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_calldata_slice.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_calldata_slice.sol index 686164de856a..5457e7f6743e 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_calldata_slice.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_calldata_slice.sol @@ -60,10 +60,10 @@ contract C { // EVMVersion: >homestead // ---- // test_bytes() -> -// gas irOptimized: 321332 -// gas legacy: 319111 -// gas legacyOptimized: 269317 +// gas irOptimized: 306908 +// gas legacy: 305827 +// gas legacyOptimized: 255013 // test_uint256() -> -// gas irOptimized: 447646 -// gas legacy: 433627 -// gas legacyOptimized: 365410 +// gas irOptimized: 435054 +// gas legacy: 421315 +// gas legacyOptimized: 352838 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2.sol index 5e50d5e9a807..c0d5f5d3fb9c 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2.sol @@ -50,6 +50,6 @@ contract C { // f2() -> 0x20, 0xa0, 0x1, 0x60, 0x2, 0x3, "abc" // f3() -> 0x20, 0xa0, 0x1, 0x60, 0x2, 0x3, "abc" // f4() -> 0x20, 0x160, 0x1, 0x80, 0xc0, 0x2, 0x3, "abc", 0x7, 0x40, 0x2, 0x2, 0x3 -// gas irOptimized: 112630 -// gas legacy: 114794 -// gas legacyOptimized: 112572 +// gas irOptimized: 111708 +// gas legacy: 113892 +// gas legacyOptimized: 111646 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array.sol index 22aad67492e8..f749e403b567 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/calldata_array.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/calldata_array.sol @@ -20,6 +20,6 @@ contract C { // f(uint256[][1]): 32, 32, 0 -> true // 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: 120043 +// gas irOptimized: 117157 // gas legacy: 101568 // gas legacyOptimized: 119092 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/calldata_overlapped_dynamic_arrays.sol b/test/libsolidity/semanticTests/abiEncoderV2/calldata_overlapped_dynamic_arrays.sol index a92336097172..46df313331e4 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: 111642 -// gas legacy: 112944 -// gas legacyOptimized: 112092 +// gas irOptimized: 111395 +// gas legacy: 112709 +// gas legacyOptimized: 111852 // 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 33075675358e..14699df57219 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: 180720 -// gas legacy: 184830 -// gas legacyOptimized: 181493 +// gas irOptimized: 180103 +// gas legacy: 184235 +// gas legacyOptimized: 180873 // i(uint256[2][2]): 123, 124, 223, 224 -> 32, 128, 123, 124, 223, 224 -// gas irOptimized: 112425 -// gas legacy: 115398 -// gas legacyOptimized: 112982 +// gas irOptimized: 112104 +// gas legacy: 115091 +// gas legacyOptimized: 112666 diff --git a/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple_storage.sol b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple_storage.sol index c06d2b24c457..9f59c43155ee 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: 135661 -// gas legacy: 137278 -// gas legacyOptimized: 136048 +// gas irOptimized: 135452 +// gas legacy: 137096 +// gas legacyOptimized: 135834 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 38dbe52bccde..24565b4c62a6 100644 --- a/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol @@ -17,25 +17,25 @@ contract c { // ---- // f(uint256): 0 -> 0x20, 0x00 // f(uint256): 31 -> 0x20, 0x1f, 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e00 -// gas irOptimized: 104476 -// gas legacy: 112974 -// gas legacyOptimized: 112717 +// gas irOptimized: 103283 +// gas legacy: 112883 +// gas legacyOptimized: 112629 // f(uint256): 32 -> 0x20, 0x20, 1780731860627700044960722568376592200742329637303199754547598369979440671 -// gas irOptimized: 118962 -// gas legacy: 129034 -// gas legacyOptimized: 128952 +// gas irOptimized: 117740 +// gas legacy: 128943 +// gas legacyOptimized: 128864 // f(uint256): 33 -> 0x20, 33, 1780731860627700044960722568376592200742329637303199754547598369979440671, 0x2000000000000000000000000000000000000000000000000000000000000000 -// gas irOptimized: 125419 -// gas legacy: 136234 -// gas legacyOptimized: 135643 +// gas irOptimized: 124080 +// gas legacy: 136071 +// gas legacyOptimized: 135479 // f(uint256): 63 -> 0x20, 0x3f, 1780731860627700044960722568376592200742329637303199754547598369979440671, 14532552714582660066924456880521368950258152170031413196862950297402215316992 -// gas irOptimized: 129529 -// gas legacy: 148834 -// gas legacyOptimized: 148873 +// gas irOptimized: 127140 +// gas legacy: 148671 +// gas legacyOptimized: 148709 // f(uint256): 12 -> 0x20, 0x0c, 0x0102030405060708090a0b0000000000000000000000000000000000000000 // gas legacy: 59345 // gas legacyOptimized: 57279 // f(uint256): 129 -> 0x20, 0x81, 1780731860627700044960722568376592200742329637303199754547598369979440671, 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f, 29063324697304692433803953038474361308315562010425523193971352996434451193439, 0x606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f, -57896044618658097711785492504343953926634992332820282019728792003956564819968 -// gas irOptimized: 421911 -// gas legacy: 459355 -// gas legacyOptimized: 461066 +// gas irOptimized: 416966 +// gas legacy: 458976 +// gas legacyOptimized: 460674 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 5482f615f241..33a4c7045af5 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 @@ -35,12 +35,12 @@ contract C { } // ---- // f() -> 0x40, 0x80, 6, 0x6162636465660000000000000000000000000000000000000000000000000000, 0x49, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738390000000000000000000000000000000000000000000000 -// gas irOptimized: 179750 -// gas legacy: 181001 -// gas legacyOptimized: 180018 +// gas irOptimized: 179420 +// gas legacy: 180675 +// gas legacyOptimized: 179700 // g() -> 0x40, 0xc0, 0x49, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738390000000000000000000000000000000000000000000000, 0x11, 0x3132333435363738393233343536373839000000000000000000000000000000 -// gas irOptimized: 106679 -// gas legacy: 109720 -// gas legacyOptimized: 106932 +// gas irOptimized: 106349 +// gas legacy: 109394 +// gas legacyOptimized: 106614 // h() -> 0x40, 0x60, 0x00, 0x00 // storageEmpty -> 1 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 275a0fca1b75..56a415079084 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: 116645 -// gas legacy: 118936 -// gas legacyOptimized: 116975 +// gas irOptimized: 116546 +// gas legacy: 118845 +// gas legacyOptimized: 116887 // test2() -> 0x20, 0x14, "[b called][a called]" // test3() -> 0x20, 0x14, "[b called][a called]" -// gas irOptimized: 103246 -// gas legacy: 102745 -// gas legacyOptimized: 101669 +// gas irOptimized: 103147 +// gas legacy: 102654 +// gas legacyOptimized: 101581 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 211477c553c2..0e8cff97fd57 100644 --- a/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol +++ b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol @@ -11,6 +11,6 @@ contract C { } // ---- // f() -> 0x20, 0x02, 0x40, 0x80, 3, 0x6162630000000000000000000000000000000000000000000000000000000000, 0x99, 44048183304486788312148433451363384677562265908331949128489393215789685032262, 32241931068525137014058842823026578386641954854143559838526554899205067598957, 49951309422467613961193228765530489307475214998374779756599339590522149884499, 0x54555658595a6162636465666768696a6b6c6d6e6f707172737475767778797a, 0x4142434445464748494a4b4c4d4e4f5051525354555658595a00000000000000 -// gas irOptimized: 202731 -// gas legacy: 204798 -// gas legacyOptimized: 203357 +// gas irOptimized: 202162 +// gas legacy: 204328 +// gas legacyOptimized: 202887 diff --git a/test/libsolidity/semanticTests/array/pop/byte_array_pop_masking_long.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_masking_long.sol index 5b1a61839183..674b0841e6b4 100644 --- a/test/libsolidity/semanticTests/array/pop/byte_array_pop_masking_long.sol +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop_masking_long.sol @@ -9,6 +9,6 @@ contract c { } // ---- // test() -> 0x20, 33, 0x303030303030303030303030303030303030303030303030303030303030303, 0x0300000000000000000000000000000000000000000000000000000000000000 -// gas irOptimized: 107343 -// gas legacy: 121408 -// gas legacyOptimized: 120534 +// gas irOptimized: 107183 +// gas legacy: 121245 +// gas legacyOptimized: 120370 diff --git a/test/libsolidity/semanticTests/byte_array_to_storage_cleanup.sol b/test/libsolidity/semanticTests/byte_array_to_storage_cleanup.sol index 14130926e7fb..8d0cb0a01294 100644 --- a/test/libsolidity/semanticTests/byte_array_to_storage_cleanup.sol +++ b/test/libsolidity/semanticTests/byte_array_to_storage_cleanup.sol @@ -28,12 +28,12 @@ contract C { // compileViaYul: also // ---- // constructor() -> -// gas irOptimized: 82402 -// gas irOptimized code: 361000 -// gas legacy: 101895 -// gas legacy code: 609400 -// gas legacyOptimized: 85378 -// gas legacyOptimized code: 397000 +// gas irOptimized: 81932 +// gas irOptimized code: 355200 +// gas legacy: 101472 +// gas legacy code: 604200 +// gas legacyOptimized: 85004 +// gas legacyOptimized code: 392400 // h() -> 0x20, 0x40, 0x00, 0 // ~ emit ev(uint256[],uint256): 0x40, 0x21, 0x02, 0x00, 0x00 // g() -> 0x20, 0x40, 0, 0x00 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol index 7885d3ceecf0..c0b9df16c34d 100644 --- a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol @@ -24,9 +24,9 @@ contract Creator { } // ---- // f(uint256,bytes): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> 7, "h" -// gas irOptimized: 169902 -// gas irOptimized code: 105200 -// gas legacy: 173433 -// gas legacy code: 245000 -// gas legacyOptimized: 170360 -// gas legacyOptimized code: 121600 +// gas irOptimized: 169361 +// gas irOptimized code: 99200 +// gas legacy: 172944 +// gas legacy code: 239800 +// gas legacyOptimized: 169858 +// gas legacyOptimized code: 117000 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol index 1d6b01127c5a..0918a970e272 100644 --- a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_unpacker.sol @@ -8,11 +8,11 @@ contract Test { } // ---- // constructor(): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> -// gas irOptimized: 182025 -// gas irOptimized code: 87200 -// gas legacy: 196220 -// gas legacy code: 114600 -// gas legacyOptimized: 182604 -// gas legacyOptimized code: 76000 +// gas irOptimized: 180756 +// gas irOptimized code: 79200 +// gas legacy: 195165 +// gas legacy code: 109400 +// gas legacyOptimized: 181600 +// gas legacyOptimized code: 71400 // m_x() -> 7 // m_s() -> 0x20, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" diff --git a/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol b/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol index d308d4597e53..925ed54e6dc3 100644 --- a/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol +++ b/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol @@ -74,12 +74,12 @@ contract FixedFeeRegistrar is Registrar { } // ---- // constructor() -// gas irOptimized: 78006 -// gas irOptimized code: 306600 -// gas legacy: 115817 -// gas legacy code: 797600 -// gas legacyOptimized: 84924 -// gas legacyOptimized code: 392000 +// gas irOptimized: 77654 +// gas irOptimized code: 302200 +// gas legacy: 115395 +// gas legacy code: 792400 +// gas legacyOptimized: 84566 +// gas legacyOptimized code: 387600 // reserve(string), 69 ether: 0x20, 3, "abc" -> // ~ emit Changed(string): #0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 // gas irOptimized: 45967 diff --git a/test/libsolidity/semanticTests/externalContracts/base64.sol b/test/libsolidity/semanticTests/externalContracts/base64.sol index b712092ed022..aed2a11df22c 100644 --- a/test/libsolidity/semanticTests/externalContracts/base64.sol +++ b/test/libsolidity/semanticTests/externalContracts/base64.sol @@ -33,12 +33,12 @@ contract test { // EVMVersion: >=constantinople // ---- // constructor() -// gas irOptimized: 79428 -// gas irOptimized code: 326400 -// gas legacy: 102450 -// gas legacy code: 632600 -// gas legacyOptimized: 88318 -// gas legacyOptimized code: 434400 +// gas irOptimized: 78952 +// gas irOptimized code: 320600 +// gas legacy: 102034 +// gas legacy code: 627400 +// gas legacyOptimized: 87950 +// gas legacyOptimized code: 429800 // encode_inline_asm(bytes): 0x20, 0 -> 0x20, 0 // encode_inline_asm(bytes): 0x20, 1, "f" -> 0x20, 4, "Zg==" // encode_inline_asm(bytes): 0x20, 2, "fo" -> 0x20, 4, "Zm8=" diff --git a/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol b/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol index 0ef87edd4f08..ce77683aab73 100644 --- a/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol +++ b/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol @@ -176,38 +176,38 @@ contract DepositContract is IDepositContract, ERC165 { } // ---- // constructor() -// gas irOptimized: 815686 -// gas irOptimized code: 571200 -// gas legacy: 925933 -// gas legacy code: 1442800 -// gas legacyOptimized: 854404 -// gas legacyOptimized code: 885600 +// gas irOptimized: 809248 +// gas irOptimized code: 558000 +// gas legacy: 919945 +// gas legacy code: 1437600 +// gas legacyOptimized: 848464 +// gas legacyOptimized code: 875200 // supportsInterface(bytes4): 0x0 -> 0 // supportsInterface(bytes4): 0xffffffff00000000000000000000000000000000000000000000000000000000 -> false # defined to be false by ERC-165 # // supportsInterface(bytes4): 0x01ffc9a700000000000000000000000000000000000000000000000000000000 -> true # ERC-165 id # // supportsInterface(bytes4): 0x8564090700000000000000000000000000000000000000000000000000000000 -> true # the deposit interface id # // get_deposit_root() -> 0xd70a234731285c6804c2a4f56711ddb8c82c99740f207854891028af34e27e5e -// gas irOptimized: 115231 -// gas legacy: 148205 -// gas legacyOptimized: 122303 +// gas irOptimized: 109009 +// gas legacy: 142735 +// gas legacyOptimized: 117558 // 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: 115231 -// gas legacy: 148205 -// gas legacyOptimized: 122303 +// gas irOptimized: 109009 +// gas legacy: 142735 +// gas legacyOptimized: 117558 // 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 // get_deposit_root() -> 0x2089653123d9c721215120b6db6738ba273bbc5228ac093b1f983badcdc8a438 -// gas irOptimized: 115216 -// gas legacy: 148214 -// gas legacyOptimized: 122315 +// gas irOptimized: 108994 +// gas legacy: 142744 +// gas legacyOptimized: 117570 // 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 // get_deposit_root() -> 0x40255975859377d912c53aa853245ebd939bdd2b33a28e084babdcc1ed8238ee -// gas irOptimized: 115216 -// gas legacy: 148214 -// gas legacyOptimized: 122315 +// gas irOptimized: 108994 +// gas legacy: 142744 +// gas legacyOptimized: 117570 // get_deposit_count() -> 0x20, 8, 0x0200000000000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/externalContracts/strings.sol b/test/libsolidity/semanticTests/externalContracts/strings.sol index a4b0f9fd353d..102c85ae0fa8 100644 --- a/test/libsolidity/semanticTests/externalContracts/strings.sol +++ b/test/libsolidity/semanticTests/externalContracts/strings.sol @@ -49,12 +49,12 @@ contract test { } // ---- // constructor() -// gas irOptimized: 96420 -// gas irOptimized code: 533800 -// gas legacy: 126553 -// gas legacy code: 935400 -// gas legacyOptimized: 102933 -// gas legacyOptimized code: 616000 +// gas irOptimized: 95949 +// gas irOptimized code: 528000 +// gas legacy: 126106 +// gas legacy code: 930200 +// gas legacyOptimized: 102559 +// gas legacyOptimized code: 611400 // toSlice(string): 0x20, 11, "hello world" -> 11, 0xa0 // gas irOptimized: 22660 // gas legacy: 23190 diff --git a/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting.sol b/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting.sol index 9d521f7777f9..5b254a9f6c2d 100644 --- a/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting.sol +++ b/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting.sol @@ -22,12 +22,12 @@ contract C { } // ---- // constructor(), 1 ether -> -// gas irOptimized: 89555 -// gas irOptimized code: 172800 -// gas legacy: 103042 -// gas legacy code: 338400 -// gas legacyOptimized: 91862 -// gas legacyOptimized code: 201000 +// gas irOptimized: 89155 +// gas irOptimized code: 167800 +// gas legacy: 102626 +// gas legacy code: 333200 +// gas legacyOptimized: 91471 +// gas legacyOptimized code: 196200 // f(uint256): 0 -> FAILURE // f(uint256): 1 -> FAILURE // f(uint256): 2 -> FAILURE diff --git a/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting_debugstrings.sol b/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting_debugstrings.sol index 9baacd812773..8bf182a89ee8 100644 --- a/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting_debugstrings.sol +++ b/test/libsolidity/semanticTests/functionCall/external_call_to_nonexisting_debugstrings.sol @@ -25,12 +25,12 @@ contract C { // revertStrings: debug // ---- // constructor(), 1 ether -> -// gas irOptimized: 99264 -// gas irOptimized code: 291200 -// gas legacy: 123585 -// gas legacy code: 686400 -// gas legacyOptimized: 107331 -// gas legacyOptimized code: 391000 +// gas irOptimized: 98842 +// gas irOptimized code: 286000 +// gas legacy: 123163 +// gas legacy code: 681200 +// gas legacyOptimized: 106957 +// gas legacyOptimized code: 386400 // f(uint256): 0 -> FAILURE, hex"08c379a0", 0x20, 37, "Target contract does not contain", " code" // f(uint256): 1 -> FAILURE, hex"08c379a0", 0x20, 37, "Target contract does not contain", " code" // f(uint256): 2 -> FAILURE, hex"08c379a0", 0x20, 37, "Target contract does not contain", " code" diff --git a/test/libsolidity/semanticTests/inlineAssembly/blobhash_pre_cancun.sol b/test/libsolidity/semanticTests/inlineAssembly/blobhash_pre_cancun.sol index 8972958e1f80..d3a41fc07bf3 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/blobhash_pre_cancun.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/blobhash_pre_cancun.sol @@ -15,7 +15,7 @@ contract C { } } // ==== -// EVMVersion: <=shanghai +// EVMVersion: 1 // g() -> 1000 diff --git a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_low_level_calls.sol b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_low_level_calls.sol index f3fe0384472d..e43fc475740a 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_low_level_calls.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_low_level_calls.sol @@ -68,3 +68,9 @@ contract C { // testCall() -> true // tloadAllowedStaticCall() -> true // tstoreNotAllowedStaticCall() -> true +// gas irOptimized: 98419720 +// gas irOptimized code: 19000 +// gas legacy: 98409086 +// gas legacy code: 30000 +// gas legacyOptimized: 98420962 +// gas legacyOptimized code: 17800 diff --git a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_selfdestruct.sol b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_selfdestruct.sol index 935435420e44..f4975cd4c790 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/transient_storage_selfdestruct.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/transient_storage_selfdestruct.sol @@ -40,5 +40,15 @@ contract D { // EVMVersion: >=cancun // ---- // constructor() -> +// gas irOptimized: 127944 +// gas irOptimized code: 225200 +// gas legacy: 149357 +// gas legacy code: 499800 +// gas legacyOptimized: 125830 +// gas legacyOptimized code: 203200 // destroy() -> // createAndDestroy() -> +// gas legacy: 67044 +// gas legacy code: 92600 +// gas legacyOptimized: 65675 +// gas legacyOptimized code: 39400 diff --git a/test/libsolidity/semanticTests/inlineAssembly/tstore_hidden_staticcall.sol b/test/libsolidity/semanticTests/inlineAssembly/tstore_hidden_staticcall.sol index bdd9401ee5a6..d64c7dfb5fce 100644 --- a/test/libsolidity/semanticTests/inlineAssembly/tstore_hidden_staticcall.sol +++ b/test/libsolidity/semanticTests/inlineAssembly/tstore_hidden_staticcall.sol @@ -18,3 +18,6 @@ contract C { // EVMVersion: >=cancun // ---- // test() -> FAILURE +// gas irOptimized: 98437877 +// gas legacy: 98437872 +// gas legacyOptimized: 98437873 diff --git a/test/libsolidity/semanticTests/salted_create/prediction_example.sol b/test/libsolidity/semanticTests/salted_create/prediction_example.sol index 6fa865440bd4..75174729326f 100644 --- a/test/libsolidity/semanticTests/salted_create/prediction_example.sol +++ b/test/libsolidity/semanticTests/salted_create/prediction_example.sol @@ -26,5 +26,5 @@ contract C { // compileViaYul: also // ---- // createDSalted(bytes32,uint256): 42, 64 -> -// gas legacy: 79241 +// gas legacy: 78574 // gas legacy code: 23600 diff --git a/test/libsolidity/semanticTests/structs/copy_from_mapping.sol b/test/libsolidity/semanticTests/structs/copy_from_mapping.sol index 1d1783a16291..8ee19a278a23 100644 --- a/test/libsolidity/semanticTests/structs/copy_from_mapping.sol +++ b/test/libsolidity/semanticTests/structs/copy_from_mapping.sol @@ -36,8 +36,8 @@ contract C { } // ---- // to_state() -> 0x20, 0x60, 0xa0, 7, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 -// gas irOptimized: 121481 -// gas legacy: 123069 -// gas legacyOptimized: 121756 +// gas irOptimized: 121394 +// gas legacy: 122978 +// gas legacyOptimized: 121648 // 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_from_mapping.sol b/test/libsolidity/semanticTests/structs/copy_substructures_from_mapping.sol index a1b0999ff5b1..9b0fa9207a8c 100644 --- a/test/libsolidity/semanticTests/structs/copy_substructures_from_mapping.sol +++ b/test/libsolidity/semanticTests/structs/copy_substructures_from_mapping.sol @@ -44,8 +44,8 @@ contract C { } // ---- // to_state() -> 0x20, 0x60, 0xa0, 7, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 -// gas irOptimized: 121598 -// gas legacy: 123208 -// gas legacyOptimized: 121763 +// gas irOptimized: 121511 +// gas legacy: 123117 +// gas legacyOptimized: 121655 // 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 b88466113408..a0834ca5bf5b 100644 --- a/test/libsolidity/semanticTests/structs/copy_substructures_to_mapping.sol +++ b/test/libsolidity/semanticTests/structs/copy_substructures_to_mapping.sol @@ -52,14 +52,14 @@ contract C { } // ---- // from_memory() -> 0x20, 0x60, 0xa0, 0x15, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 -// gas irOptimized: 123039 -// gas legacy: 130227 -// gas legacyOptimized: 128758 +// gas irOptimized: 122952 +// gas legacy: 130136 +// gas legacyOptimized: 128650 // from_state() -> 0x20, 0x60, 0xa0, 21, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 -// gas irOptimized: 121709 -// gas legacy: 123282 -// gas legacyOptimized: 121868 +// gas irOptimized: 121622 +// gas legacy: 123191 +// gas legacyOptimized: 121760 // 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: 115130 -// gas legacy: 122516 -// gas legacyOptimized: 120804 +// gas irOptimized: 115043 +// gas legacy: 122425 +// gas legacyOptimized: 120696 diff --git a/test/libsolidity/semanticTests/structs/copy_to_mapping.sol b/test/libsolidity/semanticTests/structs/copy_to_mapping.sol index e6b569922c5f..033f0f518c12 100644 --- a/test/libsolidity/semanticTests/structs/copy_to_mapping.sol +++ b/test/libsolidity/semanticTests/structs/copy_to_mapping.sol @@ -45,18 +45,18 @@ contract C { } // ---- // from_state() -> 0x20, 0x60, 0xa0, 21, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 -// gas irOptimized: 121686 -// gas legacy: 123144 -// gas legacyOptimized: 121808 +// gas irOptimized: 121599 +// gas legacy: 123053 +// gas legacyOptimized: 121700 // from_storage() -> 0x20, 0x60, 0xa0, 21, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 -// gas irOptimized: 121731 -// gas legacy: 123193 -// gas legacyOptimized: 121860 +// gas irOptimized: 121644 +// gas legacy: 123102 +// gas legacyOptimized: 121752 // from_memory() -> 0x20, 0x60, 0xa0, 21, 3, 0x666F6F0000000000000000000000000000000000000000000000000000000000, 2, 13, 14 -// gas irOptimized: 122947 -// gas legacy: 130088 -// gas legacyOptimized: 128754 +// gas irOptimized: 122860 +// gas legacy: 129997 +// gas legacyOptimized: 128646 // 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: 115045 -// gas legacy: 118301 -// gas legacyOptimized: 115432 +// gas irOptimized: 114958 +// gas legacy: 118210 +// gas legacyOptimized: 115324 diff --git a/test/libsolidity/semanticTests/various/address_code.sol b/test/libsolidity/semanticTests/various/address_code.sol index 6a97c0bf7ee2..f283aaeaa3de 100644 --- a/test/libsolidity/semanticTests/various/address_code.sol +++ b/test/libsolidity/semanticTests/various/address_code.sol @@ -14,12 +14,12 @@ contract C { } // ---- // constructor() -> -// gas irOptimized: 70873 -// gas irOptimized code: 102400 -// gas legacy: 82796 -// gas legacy code: 159000 -// gas legacyOptimized: 69870 -// gas legacyOptimized code: 83800 +// gas irOptimized: 70351 +// gas irOptimized code: 95800 +// gas legacy: 82380 +// gas legacy code: 153800 +// gas legacyOptimized: 69496 +// gas legacyOptimized code: 79200 // initCode() -> 0x20, 0 // f() -> true // g() -> 0 diff --git a/test/libsolidity/semanticTests/various/create_calldata.sol b/test/libsolidity/semanticTests/various/create_calldata.sol index 2db42adebb86..b8ec252095c8 100644 --- a/test/libsolidity/semanticTests/various/create_calldata.sol +++ b/test/libsolidity/semanticTests/various/create_calldata.sol @@ -8,10 +8,10 @@ contract C { } // ---- // constructor(): 42 -> -// gas irOptimized: 68378 -// gas irOptimized code: 77200 -// gas legacy: 78445 -// gas legacy code: 95400 -// gas legacyOptimized: 68677 -// gas legacyOptimized code: 69200 +// gas irOptimized: 67834 +// gas irOptimized code: 70400 +// gas legacy: 78029 +// gas legacy code: 90200 +// gas legacyOptimized: 68321 +// gas legacyOptimized code: 64600 // s() -> 0x20, 0 diff --git a/test/libsolidity/semanticTests/various/selfdestruct_post_cancun.sol b/test/libsolidity/semanticTests/various/selfdestruct_post_cancun.sol index 85c95be44795..263da7133fa9 100644 --- a/test/libsolidity/semanticTests/various/selfdestruct_post_cancun.sol +++ b/test/libsolidity/semanticTests/various/selfdestruct_post_cancun.sol @@ -64,9 +64,12 @@ contract D { // EVMVersion: >=cancun // ---- // constructor(), 1 ether -> -// gas irOptimized: 455954 -// gas legacy: 375495 -// gas legacyOptimized: 507630 +// gas irOptimized: 67028 +// gas irOptimized code: 175400 +// gas legacy: 76163 +// gas legacy code: 297400 +// gas legacyOptimized: 66516 +// gas legacyOptimized code: 168000 // exists() -> false // test_create_and_terminate() -> // exists() -> false diff --git a/test/libsolidity/semanticTests/various/selfdestruct_post_cancun_multiple_beneficiaries.sol b/test/libsolidity/semanticTests/various/selfdestruct_post_cancun_multiple_beneficiaries.sol index 4bc65b760811..16797ee4c0fd 100644 --- a/test/libsolidity/semanticTests/various/selfdestruct_post_cancun_multiple_beneficiaries.sol +++ b/test/libsolidity/semanticTests/various/selfdestruct_post_cancun_multiple_beneficiaries.sol @@ -35,16 +35,30 @@ contract D { // EVMVersion: >=cancun // ---- // constructor(), 2 ether -> +// gas irOptimized: 107718 +// gas irOptimized code: 116200 +// gas legacy: 120424 +// gas legacy code: 253600 +// gas legacyOptimized: 109015 +// gas legacyOptimized code: 130800 // balance: 0x1111111111111111111111111111111111111111 -> 0 // balance: 0x2222222222222222222222222222222222222222 -> 0 // balance -> 2000000000000000000 // exists() -> false // test_deploy_and_terminate_twice() -> +// gas irOptimized: 121350 +// gas irOptimized code: 14000 +// gas legacy: 122384 +// gas legacy code: 43200 +// gas legacyOptimized: 121596 +// gas legacyOptimized code: 22800 // exists() -> false // balance: 0x1111111111111111111111111111111111111111 -> 1000000000000000000 // balance: 0x2222222222222222222222222222222222222222 -> 0 // balance -> 1000000000000000000 // deploy() -> +// gas legacy: 58491 +// gas legacy code: 43200 // exists() -> true // balance: 0x1111111111111111111111111111111111111111 -> 1000000000000000000 // balance: 0x2222222222222222222222222222222222222222 -> 0 diff --git a/test/libsolidity/semanticTests/various/selfdestruct_post_cancun_redeploy.sol b/test/libsolidity/semanticTests/various/selfdestruct_post_cancun_redeploy.sol index 473571bf1391..1b3022efedf0 100644 --- a/test/libsolidity/semanticTests/various/selfdestruct_post_cancun_redeploy.sol +++ b/test/libsolidity/semanticTests/various/selfdestruct_post_cancun_redeploy.sol @@ -82,14 +82,21 @@ contract D { // EVMVersion: >=cancun // ---- // constructor(), 1 ether -> -// gas irOptimized: 562058 -// gas legacy: 633310 -// gas legacyOptimized: 590051 +// gas irOptimized: 132874 +// gas irOptimized code: 292400 +// gas legacy: 151217 +// gas legacy code: 533400 +// gas legacyOptimized: 131436 +// gas legacyOptimized code: 276600 // exists() -> false // test_deploy_and_terminate() -> // ~ emit Deployed(address,bytes32) from 0x137aa4dfc0911524504fcd4d98501f179bc13b4a: 0x7e6580007e709ac52945fae182c61131d42634e8, 0x1234000000000000000000000000000000000000000000000000000000000000 -// gas irOptimized: 118541 -// gas legacyOptimized: 118305 +// gas irOptimized: 96394 +// gas irOptimized code: 20800 +// gas legacy: 97788 +// gas legacy code: 20800 +// gas legacyOptimized: 96044 +// gas legacyOptimized code: 20800 // exists() -> false // deploy_create2() -> // ~ emit Deployed(address,bytes32) from 0x137aa4dfc0911524504fcd4d98501f179bc13b4a: 0x7e6580007e709ac52945fae182c61131d42634e8, 0x1234000000000000000000000000000000000000000000000000000000000000 @@ -99,3 +106,6 @@ contract D { // test_balance_after_selfdestruct() -> // exists() -> true // deploy_create2() -> FAILURE +// gas irOptimized: 96903654 +// gas legacy: 96903660 +// gas legacyOptimized: 96903641 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 ce8946c55c4e..ef53a4e47848 100644 --- a/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol +++ b/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol @@ -19,6 +19,6 @@ contract C { } // ---- // g() -> 2, 6 -// gas irOptimized: 178475 -// gas legacy: 180839 -// gas legacyOptimized: 179374 +// gas irOptimized: 178263 +// gas legacy: 180657 +// gas legacyOptimized: 179156 diff --git a/test/libsolidity/syntaxTests/inlineAssembly/blobhash_pre_cancun_not_declared.sol b/test/libsolidity/syntaxTests/inlineAssembly/blobhash_pre_cancun_not_declared.sol index c0284b330e25..8f58c77ce6f3 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/blobhash_pre_cancun_not_declared.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/blobhash_pre_cancun_not_declared.sol @@ -6,7 +6,7 @@ contract C { } } // ==== -// EVMVersion: <=shanghai +// EVMVersion: =shanghai // ---- -// DeclarationError 4619: (106-114): Function "blobhash" not found. +// TypeError 8314: (106-114): The "blobhash" instruction is only available for Cancun-compatible VMs (you are currently compiling for "shanghai"). // DeclarationError 8678: (99-116): Variable count for assignment to "ret" does not match number of values (1 vs. 0) diff --git a/test/libsolidity/syntaxTests/inlineAssembly/mcopy_pre_cancun.sol b/test/libsolidity/syntaxTests/inlineAssembly/mcopy_pre_cancun.sol index 090e2f62862c..96be578948c9 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/mcopy_pre_cancun.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/mcopy_pre_cancun.sol @@ -6,6 +6,6 @@ contract C { } } // ==== -// EVMVersion: