diff --git a/.circleci/config.yml b/.circleci/config.yml
index 47c6518ee..3e9cebb06 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -104,17 +104,6 @@ defaults:
name: command line tests
command: ./test/cmdlineTests.sh
- - test_ubuntu1904: &test_ubuntu1904
- docker:
- - image: ethereum/solidity-buildpack-deps:ubuntu1904
- steps:
- - checkout
- - attach_workspace:
- at: build
- - run: *run_soltest
- - store_test_results: *store_test_results
- - store_artifacts: *artifacts_test_results
-
- test_ubuntu1904_clang: &test_ubuntu1904_clang
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904-clang
@@ -126,7 +115,7 @@ defaults:
- store_test_results: *store_test_results
- store_artifacts: *artifacts_test_results
- - test_ubuntu1904_all: &test_ubuntu1904
+ - test_ubuntu1904: &test_ubuntu1904
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904
steps:
@@ -423,40 +412,61 @@ jobs:
b_osx:
macos:
- xcode: "10.0.0"
+ xcode: "11.0.0"
environment:
TERM: xterm
CMAKE_BUILD_TYPE: Debug
- CMAKE_OPTIONS: -DLLL=ON
steps:
- checkout
+ - restore_cache:
+ keys:
+ - dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
- run:
name: Install build dependencies
- command: |
- brew unlink python
- brew install z3
- brew install boost
- brew install cmake
- brew install wget
- ./scripts/install_obsolete_jsoncpp_1_7_4.sh
+ command: ./.circleci/osx_install_dependencies.sh
+ - save_cache:
+ key: dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
+ paths:
+ - /usr/local/bin
+ - /usr/local/sbin
+ - /usr/local/lib
+ - /usr/local/include
+ - /usr/local/Cellar
+ - /usr/local/Homebrew
- run: *run_build
- store_artifacts: *artifacts_solc
- - persist_to_workspace: *artifacts_executables
+ - persist_to_workspace: *artifacts_build_dir
+
+ t_osx_soltest:
+ macos:
+ xcode: "11.0.0"
+ environment:
+ EVM: constantinople
+ OPTIMIZE: 0
+ TERM: xterm
+ steps:
+ - checkout
+ - restore_cache:
+ keys:
+ - dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
+ - attach_workspace:
+ at: build
+ - run: *run_soltest
+ - store_test_results: *store_test_results
+ - store_artifacts: *artifacts_test_results
t_osx_cli:
macos:
- xcode: "10.0.0"
+ xcode: "11.0.0"
environment:
TERM: xterm
steps:
- checkout
+ - restore_cache:
+ keys:
+ - dependencies-osx-{{ .Branch }}-{{ checksum ".circleci/config.yml" }}-{{ checksum ".circleci/osx_install_dependencies.sh" }}
- attach_workspace:
at: build
- - run:
- name: Install dependencies
- command: |
- brew unlink python
- brew install z3
- run: *run_cmdline_tests
- store_artifacts: *artifacts_test_results
@@ -568,7 +578,7 @@ jobs:
environment:
EVM: constantinople
OPTIMIZE: 0
- flags: --no-smt
+ SOLTEST_FLAGS: --no-smt
ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2
t_ems_solcjs:
@@ -660,6 +670,7 @@ workflows:
# OS/X build and tests
- b_osx: *workflow_trigger_on_tags
- t_osx_cli: *workflow_osx
+ - t_osx_soltest: *workflow_osx
# Ubuntu build and tests
- b_ubu: *workflow_trigger_on_tags
diff --git a/.circleci/docker/Dockerfile.clang.ubuntu1904 b/.circleci/docker/Dockerfile.clang.ubuntu1904
index d21d26bcb..4fb9b1132 100644
--- a/.circleci/docker/Dockerfile.clang.ubuntu1904
+++ b/.circleci/docker/Dockerfile.clang.ubuntu1904
@@ -57,12 +57,12 @@ RUN git clone --recursive -b boost-1.69.0 https://github.com/boostorg/boost.git
./bootstrap.sh --with-toolset=clang --prefix=/usr; \
./b2 toolset=clang headers; \
./b2 toolset=clang variant=release \
- system regex filesystem unit_test_framework program_options \
+ system filesystem unit_test_framework program_options \
install -j $(($(nproc)/2)); \
rm -rf /usr/src/boost
# Z3
-RUN git clone --depth 1 -b Z3-4.8.5 https://github.com/Z3Prover/z3.git \
+RUN git clone --depth 1 -b z3-4.8.6 https://github.com/Z3Prover/z3.git \
/usr/src/z3; \
cd /usr/src/z3; \
python scripts/mk_make.py --prefix=/usr ; \
diff --git a/.circleci/docker/Dockerfile.ubuntu1804 b/.circleci/docker/Dockerfile.ubuntu1804
index 8d6e25289..0242661e7 100644
--- a/.circleci/docker/Dockerfile.ubuntu1804
+++ b/.circleci/docker/Dockerfile.ubuntu1804
@@ -34,7 +34,7 @@ RUN set -ex; \
build-essential \
software-properties-common \
cmake ninja-build clang++-8 \
- libboost-regex-dev libboost-filesystem-dev libboost-test-dev libboost-system-dev \
+ libboost-filesystem-dev libboost-test-dev libboost-system-dev \
libboost-program-options-dev \
libjsoncpp-dev \
llvm-8-dev libz3-static-dev \
diff --git a/.circleci/docker/Dockerfile.ubuntu1904 b/.circleci/docker/Dockerfile.ubuntu1904
index f356d0bde..504e13d1f 100644
--- a/.circleci/docker/Dockerfile.ubuntu1904
+++ b/.circleci/docker/Dockerfile.ubuntu1904
@@ -34,7 +34,7 @@ RUN set -ex; \
build-essential \
software-properties-common \
cmake ninja-build clang++-8 libc++-8-dev libc++abi-8-dev \
- libboost-regex-dev libboost-filesystem-dev libboost-test-dev libboost-system-dev \
+ libboost-filesystem-dev libboost-test-dev libboost-system-dev \
libboost-program-options-dev \
libjsoncpp-dev \
llvm-8-dev libcvc4-dev libz3-static-dev libleveldb1d \
diff --git a/.circleci/osx_install_dependencies.sh b/.circleci/osx_install_dependencies.sh
new file mode 100755
index 000000000..22e14af7e
--- /dev/null
+++ b/.circleci/osx_install_dependencies.sh
@@ -0,0 +1,59 @@
+#! /bin/bash
+#------------------------------------------------------------------------------
+# Bash script to install osx dependencies
+#
+# The documentation for solidity is hosted at:
+#
+# https://solidity.readthedocs.org
+#
+# ------------------------------------------------------------------------------
+# This file is part of solidity.
+#
+# solidity is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# solidity is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with solidity. If not, see
+#
+# (c) 2016-2019 solidity contributors.
+# ------------------------------------------------------------------------------
+
+# 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
+
+if [ ! -f /usr/local/lib/libz3.a ] # if this file does not exists (cache was not restored), rebuild dependencies
+then
+ brew unlink python
+ brew install boost
+ brew install cmake
+ brew install wget
+ brew install coreutils
+ ./scripts/install_obsolete_jsoncpp_1_7_4.sh
+
+ # z3
+ wget https://github.com/Z3Prover/z3/releases/download/z3-4.8.6/z3-4.8.6-x64-osx-10.14.6.zip
+ unzip z3-4.8.6-x64-osx-10.14.6.zip
+ rm -f z3-4.8.6-x64-osx-10.14.6.zip
+ cp z3-4.8.6-x64-osx-10.14.6/bin/libz3.a /usr/local/lib
+ cp z3-4.8.6-x64-osx-10.14.6/bin/z3 /usr/local/bin
+ cp z3-4.8.6-x64-osx-10.14.6/include/* /usr/local/include
+ rm -rf z3-4.8.6-x64-osx-10.14.6
+
+ # evmone
+ wget https://github.com/ethereum/evmone/releases/download/v0.1.0/evmone-0.1.0-darwin-x86_64.tar.gz
+ tar xzpf evmone-0.1.0-darwin-x86_64.tar.gz -C /usr/local
+ rm -f evmone-0.1.0-darwin-x86_64.tar.gz
+fi
+
diff --git a/.circleci/soltest.sh b/.circleci/soltest.sh
index 41f08db77..948c79dee 100755
--- a/.circleci/soltest.sh
+++ b/.circleci/soltest.sh
@@ -12,6 +12,7 @@
# EVM=version_string Specifies EVM version to compile for (such as homestead, etc)
# OPTIMIZE=1 Enables backend optimizer
# ABI_ENCODER_V2=1 Enables ABI encoder version 2
+# SOLTEST_FLAGS= Appends to default SOLTEST_ARGS
#
# ------------------------------------------------------------------------------
# This file is part of solidity.
@@ -54,7 +55,7 @@ get_logfile_basename() {
}
BOOST_TEST_ARGS="--color_output=no --show_progress=yes --logger=JUNIT,error,test_results/`get_logfile_basename`.xml"
-SOLTEST_ARGS="--evm-version=$EVM --evmonepath /usr/lib/libevmone.so $flags"
+SOLTEST_ARGS="--evm-version=$EVM $SOLTEST_FLAGS"
test "${OPTIMIZE}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --optimize"
test "${ABI_ENCODER_V2}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --abiencoderv2 --optimize-yul"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a1927bf33..07f26e291 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,7 +10,7 @@ include(EthPolicy)
eth_policy()
# project name and version should be set after cmake_policy CMP0048
-set(PROJECT_VERSION "0.5.12")
+set(PROJECT_VERSION "0.5.13")
project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX)
include(TestBigEndian)
diff --git a/CODING_STYLE.md b/CODING_STYLE.md
index 3f44cd1f9..0d17fad18 100644
--- a/CODING_STYLE.md
+++ b/CODING_STYLE.md
@@ -119,7 +119,7 @@ Use `solAssert` and `solUnimplementedAssert` generously to check assumptions tha
4. Favour declarations close to use; don't habitually declare at top of scope ala C.
5. Pass non-trivial parameters as const reference, unless the data is to be copied into the function, then either pass by const reference or by value and use std::move.
6. If a function returns multiple values, use std::tuple (std::pair acceptable) or better introduce a struct type. Do not use */& arguments.
-7. Use parameters of pointer type only if ``nullptr`` is a valid argument, use references otherwise. Often, ``boost::optional`` is better suited than a raw pointer.
+7. Use parameters of pointer type only if ``nullptr`` is a valid argument, use references otherwise. Often, ``std::optional`` is better suited than a raw pointer.
8. Never use a macro where adequate non-preprocessor C++ can be written.
9. Only use ``auto`` if the type is very long and rather irrelevant.
10. Do not pass bools: prefer enumerations instead.
@@ -135,12 +135,12 @@ enum class Accuracy
};
struct MeanSigma
{
- float mean;
- float standardDeviation;
+ float mean = 0.0f;
+ float standardDeviation = 1.0f;
};
double const d = 0;
-int i;
-int j;
+int i = 0;
+int j = 0;
char* s;
MeanAndSigma ms meanAndSigma(std::vector const& _v, Accuracy _a);
Derived* x = dynamic_cast(base);
diff --git a/Changelog.md b/Changelog.md
index f184c0568..3ffe69997 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,3 +1,30 @@
+### 0.5.13 (2019-11-14)
+
+Language Features:
+ * Allow to obtain the address of a linked library with ``address(LibraryName)``.
+
+
+Compiler Features:
+ * Code Generator: Use SELFBALANCE opcode for ``address(this).balance`` if using Istanbul EVM.
+ * EWasm: Experimental EWasm binary output via ``--ewasm`` and as documented in standard-json.
+ * SMTChecker: Add break/continue support to the CHC engine.
+ * SMTChecker: Support assignments to multi-dimensional arrays and mappings.
+ * SMTChecker: Support inheritance and function overriding.
+ * Standard JSON Interface: Output the storage layout of a contract when artifact ``storageLayout`` is requested.
+ * TypeChecker: List possible candidates when overload resolution fails.
+ * TypeChecker: Disallow variables of library types.
+
+
+Bugfixes:
+ * Code Generator: Fixed a faulty assert that would wrongly trigger for array sizes exceeding unsigned integer.
+ * SMTChecker: Fix internal error when accessing indices of fixed bytes.
+ * SMTChecker: Fix internal error when using function pointers as arguments.
+ * SMTChecker: Fix internal error when implicitly converting string literals to fixed bytes.
+ * Type Checker: Disallow constructor of the same class to be used as modifier.
+ * Type Checker: Treat magic variables as unknown identifiers in inline assembly.
+
+
+
### 0.5.12 (2019-10-01)
Language Features:
@@ -10,6 +37,7 @@ Compiler Features:
* SMTChecker: Add loop support to the CHC engine.
* Yul Optimizer: Take side-effect-freeness of user-defined functions into account.
* Yul Optimizer: Remove redundant mload/sload operations.
+ * Yul Optimizer: Use the fact that branch conditions have certain value inside the branch.
Bugfixes:
diff --git a/README.md b/README.md
index 80a20a902..a8c04da2a 100644
--- a/README.md
+++ b/README.md
@@ -60,6 +60,8 @@ Please follow the
[Developers Guide](https://solidity.readthedocs.io/en/latest/contributing.html)
if you want to help.
+You can find our current feature and bug priorities for forthcoming releases [in the projects section](https://github.com/ethereum/solidity/projects).
+
## Maintainers
* [@axic](https://github.com/axic)
* [@chriseth](https://github.com/chriseth)
diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake
index 7ffb45964..eb55feef1 100644
--- a/cmake/EthCompilerSettings.cmake
+++ b/cmake/EthCompilerSettings.cmake
@@ -23,6 +23,9 @@ endif()
eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough)
+# Prevent the path of the source directory from ending up in the binary via __FILE__ macros.
+eth_add_cxx_compiler_flag_if_supported("-fmacro-prefix-map=${CMAKE_SOURCE_DIR}=/solidity")
+
if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
# Enables all the warnings about constructions that some users consider questionable,
# and that are easy to avoid. Also enable some extra warning flags that are not
@@ -130,6 +133,8 @@ elseif (DEFINED MSVC)
add_compile_options(-D_WIN32_WINNT=0x0600) # declare Windows Vista API requirement
add_compile_options(-DNOMINMAX) # undefine windows.h MAX && MIN macros cause it cause conflicts with std::min && std::max functions
add_compile_options(/utf-8) # enable utf-8 encoding (solves warning 4819)
+ add_compile_options(-DBOOST_REGEX_NO_LIB) # disable automatic boost::regex library selection
+ add_compile_options(-D_REGEX_MAX_STACK_COUNT=200000L) # increase std::regex recursion depth limit
# disable empty object file warning
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221")
diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake
index 53319e6c8..d8bca8a31 100644
--- a/cmake/EthDependencies.cmake
+++ b/cmake/EthDependencies.cmake
@@ -26,7 +26,7 @@ set(ETH_SCRIPTS_DIR ${ETH_CMAKE_DIR}/scripts)
set(Boost_USE_MULTITHREADED ON)
option(Boost_USE_STATIC_LIBS "Link Boost statically" ON)
-set(BOOST_COMPONENTS "regex;filesystem;unit_test_framework;program_options;system")
+set(BOOST_COMPONENTS "filesystem;unit_test_framework;program_options;system")
find_package(Boost 1.65.0 QUIET REQUIRED COMPONENTS ${BOOST_COMPONENTS})
diff --git a/cmake/EthUtils.cmake b/cmake/EthUtils.cmake
index a473abcb7..fca41107e 100644
--- a/cmake/EthUtils.cmake
+++ b/cmake/EthUtils.cmake
@@ -34,3 +34,17 @@ macro(eth_default_option O DEF)
endif()
endmacro()
+function(detect_stray_source_files FILELIST DIRECTORY)
+ if(CMAKE_VERSION VERSION_LESS 3.12)
+ file(GLOB sources RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${DIRECTORY}/*.cpp" "${DIRECTORY}/*.h")
+ else()
+ file(GLOB sources RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" CONFIGURE_DEPENDS "${DIRECTORY}/*.cpp" "${DIRECTORY}/*.h")
+ endif()
+ foreach(path IN LISTS FILELIST)
+ list(REMOVE_ITEM sources ${path})
+ endforeach()
+ list(LENGTH sources leftover_sources)
+ if (leftover_sources)
+ message(SEND_ERROR "The following source files are present but are not compiled: ${sources}")
+ endif()
+endfunction(detect_stray_source_files)
diff --git a/docs/050-breaking-changes.rst b/docs/050-breaking-changes.rst
index 46ddc9abe..fd201d77c 100644
--- a/docs/050-breaking-changes.rst
+++ b/docs/050-breaking-changes.rst
@@ -292,27 +292,27 @@ Consider you have the following pre-0.5.0 contract already deployed:
::
- // This will not compile with the current version of the compiler
- pragma solidity ^0.4.25;
- contract OldContract {
- function someOldFunction(uint8 a) {
- //...
- }
- function anotherOldFunction() constant returns (bool) {
- //...
- }
- // ...
- }
+ // This will not compile with the current version of the compiler
+ pragma solidity ^0.4.25;
+ contract OldContract {
+ function someOldFunction(uint8 a) {
+ //...
+ }
+ function anotherOldFunction() constant returns (bool) {
+ //...
+ }
+ // ...
+ }
This will no longer compile with Solidity v0.5.0. However, you can define a compatible interface for it:
::
- pragma solidity >=0.5.0 <0.7.0;
- interface OldContract {
- function someOldFunction(uint8 a) external;
- function anotherOldFunction() external returns (bool);
- }
+ pragma solidity >=0.5.0 <0.7.0;
+ interface OldContract {
+ function someOldFunction(uint8 a) external;
+ function anotherOldFunction() external returns (bool);
+ }
Note that we did not declare ``anotherOldFunction`` to be ``view``, despite it being declared ``constant`` in the original
contract. This is due to the fact that starting with Solidity v0.5.0 ``staticcall`` is used to call ``view`` functions.
@@ -325,19 +325,19 @@ Given the interface defined above, you can now easily use the already deployed p
::
- pragma solidity >=0.5.0 <0.7.0;
+ pragma solidity >=0.5.0 <0.7.0;
- interface OldContract {
- function someOldFunction(uint8 a) external;
- function anotherOldFunction() external returns (bool);
- }
+ interface OldContract {
+ function someOldFunction(uint8 a) external;
+ function anotherOldFunction() external returns (bool);
+ }
- contract NewContract {
- function doSomething(OldContract a) public returns (bool) {
- a.someOldFunction(0x42);
- return a.anotherOldFunction();
- }
- }
+ contract NewContract {
+ function doSomething(OldContract a) public returns (bool) {
+ a.someOldFunction(0x42);
+ return a.anotherOldFunction();
+ }
+ }
Similarly, pre-0.5.0 libraries can be used by defining the functions of the library without implementation and
supplying the address of the pre-0.5.0 library during linking (see :ref:`commandline-compiler` for how to use the
@@ -345,17 +345,17 @@ commandline compiler for linking):
::
- pragma solidity >=0.5.0 <0.7.0;
+ pragma solidity >=0.5.0 <0.7.0;
- library OldLibrary {
- function someFunction(uint8 a) public returns(bool);
- }
+ library OldLibrary {
+ function someFunction(uint8 a) public returns(bool);
+ }
- contract NewContract {
- function f(uint8 a) public returns (bool) {
- return OldLibrary.someFunction(a);
- }
- }
+ contract NewContract {
+ function f(uint8 a) public returns (bool) {
+ return OldLibrary.someFunction(a);
+ }
+ }
Example
@@ -368,144 +368,144 @@ Old version:
::
- // This will not compile
- pragma solidity ^0.4.25;
+ // This will not compile
+ pragma solidity ^0.4.25;
- contract OtherContract {
- uint x;
- function f(uint y) external {
- x = y;
- }
- function() payable external {}
- }
+ contract OtherContract {
+ uint x;
+ function f(uint y) external {
+ x = y;
+ }
+ function() payable external {}
+ }
- contract Old {
- OtherContract other;
- uint myNumber;
+ contract Old {
+ OtherContract other;
+ uint myNumber;
- // Function mutability not provided, not an error.
- function someInteger() internal returns (uint) { return 2; }
+ // Function mutability not provided, not an error.
+ function someInteger() internal returns (uint) { return 2; }
- // Function visibility not provided, not an error.
- // Function mutability not provided, not an error.
- function f(uint x) returns (bytes) {
- // Var is fine in this version.
- var z = someInteger();
- x += z;
- // Throw is fine in this version.
- if (x > 100)
- throw;
- bytes b = new bytes(x);
- y = -3 >> 1;
- // y == -1 (wrong, should be -2)
- do {
- x += 1;
- if (x > 10) continue;
- // 'Continue' causes an infinite loop.
- } while (x < 11);
- // Call returns only a Bool.
- bool success = address(other).call("f");
- if (!success)
- revert();
- else {
- // Local variables could be declared after their use.
- int y;
- }
- return b;
- }
+ // Function visibility not provided, not an error.
+ // Function mutability not provided, not an error.
+ function f(uint x) returns (bytes) {
+ // Var is fine in this version.
+ var z = someInteger();
+ x += z;
+ // Throw is fine in this version.
+ if (x > 100)
+ throw;
+ bytes b = new bytes(x);
+ y = -3 >> 1;
+ // y == -1 (wrong, should be -2)
+ do {
+ x += 1;
+ if (x > 10) continue;
+ // 'Continue' causes an infinite loop.
+ } while (x < 11);
+ // Call returns only a Bool.
+ bool success = address(other).call("f");
+ if (!success)
+ revert();
+ else {
+ // Local variables could be declared after their use.
+ int y;
+ }
+ return b;
+ }
- // No need for an explicit data location for 'arr'
- function g(uint[] arr, bytes8 x, OtherContract otherContract) public {
- otherContract.transfer(1 ether);
+ // No need for an explicit data location for 'arr'
+ function g(uint[] arr, bytes8 x, OtherContract otherContract) public {
+ otherContract.transfer(1 ether);
- // Since uint32 (4 bytes) is smaller than bytes8 (8 bytes),
- // the first 4 bytes of x will be lost. This might lead to
- // unexpected behavior since bytesX are right padded.
- uint32 y = uint32(x);
- myNumber += y + msg.value;
- }
- }
+ // Since uint32 (4 bytes) is smaller than bytes8 (8 bytes),
+ // the first 4 bytes of x will be lost. This might lead to
+ // unexpected behavior since bytesX are right padded.
+ uint32 y = uint32(x);
+ myNumber += y + msg.value;
+ }
+ }
New version:
::
- pragma solidity >=0.5.0 <0.7.0;
+ pragma solidity >=0.5.0 <0.7.0;
- contract OtherContract {
- uint x;
- function f(uint y) external {
- x = y;
- }
- function() payable external {}
- }
+ contract OtherContract {
+ uint x;
+ function f(uint y) external {
+ x = y;
+ }
+ function() payable external {}
+ }
- contract New {
- OtherContract other;
- uint myNumber;
+ contract New {
+ OtherContract other;
+ uint myNumber;
- // Function mutability must be specified.
- function someInteger() internal pure returns (uint) { return 2; }
+ // Function mutability must be specified.
+ function someInteger() internal pure returns (uint) { return 2; }
- // Function visibility must be specified.
- // Function mutability must be specified.
- function f(uint x) public returns (bytes memory) {
- // The type must now be explicitly given.
- uint z = someInteger();
- x += z;
- // Throw is now disallowed.
- require(x > 100);
- int y = -3 >> 1;
- // y == -2 (correct)
- do {
- x += 1;
- if (x > 10) continue;
- // 'Continue' jumps to the condition below.
- } while (x < 11);
+ // Function visibility must be specified.
+ // Function mutability must be specified.
+ function f(uint x) public returns (bytes memory) {
+ // The type must now be explicitly given.
+ uint z = someInteger();
+ x += z;
+ // Throw is now disallowed.
+ require(x > 100);
+ int y = -3 >> 1;
+ require(y == -2);
+ do {
+ x += 1;
+ if (x > 10) continue;
+ // 'Continue' jumps to the condition below.
+ } while (x < 11);
- // Call returns (bool, bytes).
- // Data location must be specified.
- (bool success, bytes memory data) = address(other).call("f");
- if (!success)
- revert();
- return data;
- }
+ // Call returns (bool, bytes).
+ // Data location must be specified.
+ (bool success, bytes memory data) = address(other).call("f");
+ if (!success)
+ revert();
+ return data;
+ }
- using address_make_payable for address;
- // Data location for 'arr' must be specified
- function g(uint[] memory arr, bytes8 x, OtherContract otherContract, address unknownContract) public payable {
- // 'otherContract.transfer' is not provided.
- // Since the code of 'OtherContract' is known and has the fallback
- // function, address(otherContract) has type 'address payable'.
- address(otherContract).transfer(1 ether);
+ using address_make_payable for address;
+ // Data location for 'arr' must be specified
+ function g(uint[] memory /* arr */, bytes8 x, OtherContract otherContract, address unknownContract) public payable {
+ // 'otherContract.transfer' is not provided.
+ // Since the code of 'OtherContract' is known and has the fallback
+ // function, address(otherContract) has type 'address payable'.
+ address(otherContract).transfer(1 ether);
- // 'unknownContract.transfer' is not provided.
- // 'address(unknownContract).transfer' is not provided
- // since 'address(unknownContract)' is not 'address payable'.
- // If the function takes an 'address' which you want to send
- // funds to, you can convert it to 'address payable' via 'uint160'.
- // Note: This is not recommended and the explicit type
- // 'address payable' should be used whenever possible.
- // To increase clarity, we suggest the use of a library for
- // the conversion (provided after the contract in this example).
- address payable addr = unknownContract.make_payable();
- require(addr.send(1 ether));
+ // 'unknownContract.transfer' is not provided.
+ // 'address(unknownContract).transfer' is not provided
+ // since 'address(unknownContract)' is not 'address payable'.
+ // If the function takes an 'address' which you want to send
+ // funds to, you can convert it to 'address payable' via 'uint160'.
+ // Note: This is not recommended and the explicit type
+ // 'address payable' should be used whenever possible.
+ // To increase clarity, we suggest the use of a library for
+ // the conversion (provided after the contract in this example).
+ address payable addr = unknownContract.make_payable();
+ require(addr.send(1 ether));
- // Since uint32 (4 bytes) is smaller than bytes8 (8 bytes),
- // the conversion is not allowed.
- // We need to convert to a common size first:
- bytes4 x4 = bytes4(x); // Padding happens on the right
- uint32 y = uint32(x4); // Conversion is consistent
- // 'msg.value' cannot be used in a 'non-payable' function.
- // We need to make the function payable
- myNumber += y + msg.value;
- }
- }
+ // Since uint32 (4 bytes) is smaller than bytes8 (8 bytes),
+ // the conversion is not allowed.
+ // We need to convert to a common size first:
+ bytes4 x4 = bytes4(x); // Padding happens on the right
+ uint32 y = uint32(x4); // Conversion is consistent
+ // 'msg.value' cannot be used in a 'non-payable' function.
+ // We need to make the function payable
+ myNumber += y + msg.value;
+ }
+ }
- // We can define a library for explicitly converting ``address``
- // to ``address payable`` as a workaround.
- library address_make_payable {
- function make_payable(address x) internal pure returns (address payable) {
- return address(uint160(x));
- }
- }
+ // We can define a library for explicitly converting ``address``
+ // to ``address payable`` as a workaround.
+ library address_make_payable {
+ function make_payable(address x) internal pure returns (address payable) {
+ return address(uint160(x));
+ }
+ }
diff --git a/docs/assembly.rst b/docs/assembly.rst
index 119242aea..d735b54cd 100644
--- a/docs/assembly.rst
+++ b/docs/assembly.rst
@@ -292,7 +292,7 @@ In the grammar, opcodes are represented as pre-defined identifiers.
| create2(v, p, n, s) | | C | create new contract with code mem[p...(p+n)) at address |
| | | | keccak256(0xff . this . s . keccak256(mem[p...(p+n))) |
| | | | and send v wei and return the new address, where ``0xff`` is a |
-| | | | 8 byte value, ``this`` is the current contract's address |
+| | | | 1 byte value, ``this`` is the current contract's address |
| | | | as a 20 byte value and ``s`` is a big-endian 256-bit value |
+-------------------------+-----+---+-----------------------------------------------------------------+
| call(g, a, v, in, | | F | call contract at address a with input mem[in...(in+insize)) |
diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json
index d79aca112..402997af1 100644
--- a/docs/bugs_by_version.json
+++ b/docs/bugs_by_version.json
@@ -754,6 +754,10 @@
"bugs": [],
"released": "2019-10-01"
},
+ "0.5.13": {
+ "bugs": [],
+ "released": "2019-11-14"
+ },
"0.5.2": {
"bugs": [
"SignedArrayStorageCopy",
diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst
index 494f22dd4..ed87666d9 100644
--- a/docs/common-patterns.rst
+++ b/docs/common-patterns.rst
@@ -22,9 +22,8 @@ a contract where the goal is to send the most money to the
contract in order to become the "richest", inspired by
`King of the Ether `_.
-In the following contract, if you are usurped as the richest,
-you will receive the funds of the person who has gone on to
-become the new richest.
+In the following contract, if you are no longer the richest,
+you receive the funds of the person who is now the richest.
::
diff --git a/docs/contracts/libraries.rst b/docs/contracts/libraries.rst
index 7421e278c..bfeebdb77 100644
--- a/docs/contracts/libraries.rst
+++ b/docs/contracts/libraries.rst
@@ -178,6 +178,9 @@ custom types without the overhead of external function calls:
}
}
+It is possible to obtain the address of a library by converting
+the library type to the ``address`` type, i.e. using ``address(LibraryName)``.
+
As the compiler cannot know where the library will be
deployed at, these addresses have to be filled into the
final bytecode by a linker
diff --git a/docs/contracts/visibility-and-getters.rst b/docs/contracts/visibility-and-getters.rst
index 7bba31e7c..56d5b0e25 100644
--- a/docs/contracts/visibility-and-getters.rst
+++ b/docs/contracts/visibility-and-getters.rst
@@ -150,24 +150,24 @@ to write a function, for example:
::
- pragma solidity >=0.4.0 <0.7.0;
+ pragma solidity >=0.4.0 <0.7.0;
- contract arrayExample {
- // public state variable
- uint[] public myArray;
+ contract arrayExample {
+ // public state variable
+ uint[] public myArray;
- // Getter function generated by the compiler
- /*
- function myArray(uint i) returns (uint) {
- return myArray[i];
+ // Getter function generated by the compiler
+ /*
+ function myArray(uint i) public view returns (uint) {
+ return myArray[i];
+ }
+ */
+
+ // function that returns entire array
+ function getArray() public view returns (uint[] memory) {
+ return myArray;
+ }
}
- */
-
- // function that returns entire array
- function getArray() returns (uint[] memory) {
- return myArray;
- }
- }
Now you can use ``getArray()`` to retrieve the entire array, instead of
``myArray(i)``, which returns a single element per call.
diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst
index 3b680113e..7a811fa4b 100644
--- a/docs/layout-of-source-files.rst
+++ b/docs/layout-of-source-files.rst
@@ -37,7 +37,7 @@ breaking changes. These releases always have versions of the form
The version pragma is used as follows::
- pragma solidity ^0.5.2;
+ pragma solidity ^0.5.2;
A source file with the line above does not compile with a compiler earlier than version 0.5.2,
and it also does not work on a compiler starting from version 0.6.0 (this
diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst
index 137513b83..74c2d0326 100644
--- a/docs/miscellaneous.rst
+++ b/docs/miscellaneous.rst
@@ -8,6 +8,8 @@ Miscellaneous
Layout of State Variables in Storage
************************************
+.. _storage-inplace-encoding:
+
Statically-sized variables (everything except mapping and dynamically-sized array types) are laid out contiguously in storage starting from position ``0``. Multiple, contiguous items that need less than 32 bytes are packed into a single storage slot if possible, according to the following rules:
- The first item in a storage slot is stored lower-order aligned.
@@ -49,6 +51,8 @@ The elements of structs and arrays are stored after each other, just as if they
Mappings and Dynamic Arrays
===========================
+.. _storage-hashed-encoding:
+
Due to their unpredictable size, mapping and dynamically-sized array types use a Keccak-256 hash
computation to find the starting position of the value or the array data. These starting positions are always full stack slots.
@@ -88,6 +92,267 @@ by checking if the lowest bit is set: short (not set) and long (set).
.. note::
Handling invalidly encoded slots is currently not supported but may be added in the future.
+JSON Output
+===========
+
+.. _storage-layout-top-level:
+
+The storage layout of a contract can be requested via the :ref:`standard JSON interface `. The output is a JSON object containing two keys,
+``storage`` and ``types``. The ``storage`` object is an array where each
+element has the following form:
+
+
+.. code::
+
+
+ {
+ "astId": 2,
+ "contract": "fileA:A",
+ "label": "x",
+ "offset": 0,
+ "slot": "0",
+ "type": "t_uint256"
+ }
+
+where the example above is the storage layout of ``contract A { uint x; }`` from source unit ``fileA``
+and
+
+- ``astId`` is the id of the AST node of the state variable's declaration
+- ``contract`` is the name of the contract including its path as prefix
+- ``label`` is the name of the state variable
+- ``offset`` is the offset in bytes within the storage slot according to the encoding
+- ``slot`` is the storage slot where the state variable resides or starts. This
+ number may be very large and therefore its JSON value is represented as a
+ string.
+- ``type`` is an identifier used as key to the variable's type information (described in the following)
+
+The given ``type``, in this case ``t_uint256`` represents an element in
+``types``, which has the form:
+
+
+.. code::
+
+ {
+ "encoding": "inplace",
+ "label": "uint256",
+ "numberOfBytes": "32",
+ }
+
+where
+
+- ``encoding`` how the data is encoded in storage, where the possible values are:
+
+ - ``inplace``: data is laid out contiguously in storage (see :ref:`above `).
+ - ``mapping``: Keccak-256 hash-based method (see :ref:`above `).
+ - ``dynamic_array``: Keccak-256 hash-based method (see :ref:`above `).
+ - ``bytes``: single slot or Keccak-256 hash-based depending on the data size (see :ref:`above `).
+
+- ``label`` is the canonical type name.
+- ``numberOfBytes`` is the number of used bytes (as a decimal string). Note that if ``numberOfBytes > 32`` this means that more than one slot is used.
+
+Some types have extra information besides the four above. Mappings contain
+its ``key`` and ``value`` types (again referencing an entry in this mapping
+of types), arrays have its ``base`` type, and structs list their ``members`` in
+the same format as the top-level ``storage`` (see :ref:`above
+`).
+
+.. note ::
+ The JSON output format of a contract's storage layout is still considered experimental
+ and is subject to change in non-breaking releases of Solidity.
+
+The following example shows a contract and its storage layout, containing
+value and reference types, types that are encoded packed, and nested types.
+
+
+.. code::
+
+ pragma solidity >=0.4.0 <0.7.0;
+ contract A {
+ struct S {
+ uint128 a;
+ uint128 b;
+ uint[2] staticArray;
+ uint[] dynArray;
+ }
+
+ uint x;
+ uint y;
+ S s;
+ address addr;
+ mapping (uint => mapping (address => bool)) map;
+ uint[] array;
+ string s1;
+ bytes b1;
+ }
+
+.. code::
+
+ "storageLayout": {
+ "storage": [
+ {
+ "astId": 14,
+ "contract": "fileA:A",
+ "label": "x",
+ "offset": 0,
+ "slot": "0",
+ "type": "t_uint256"
+ },
+ {
+ "astId": 16,
+ "contract": "fileA:A",
+ "label": "y",
+ "offset": 0,
+ "slot": "1",
+ "type": "t_uint256"
+ },
+ {
+ "astId": 18,
+ "contract": "fileA:A",
+ "label": "s",
+ "offset": 0,
+ "slot": "2",
+ "type": "t_struct(S)12_storage"
+ },
+ {
+ "astId": 20,
+ "contract": "fileA:A",
+ "label": "addr",
+ "offset": 0,
+ "slot": "6",
+ "type": "t_address"
+ },
+ {
+ "astId": 26,
+ "contract": "fileA:A",
+ "label": "map",
+ "offset": 0,
+ "slot": "7",
+ "type": "t_mapping(t_uint256,t_mapping(t_address,t_bool))"
+ },
+ {
+ "astId": 29,
+ "contract": "fileA:A",
+ "label": "array",
+ "offset": 0,
+ "slot": "8",
+ "type": "t_array(t_uint256)dyn_storage"
+ },
+ {
+ "astId": 31,
+ "contract": "fileA:A",
+ "label": "s1",
+ "offset": 0,
+ "slot": "9",
+ "type": "t_string_storage"
+ },
+ {
+ "astId": 33,
+ "contract": "fileA:A",
+ "label": "b1",
+ "offset": 0,
+ "slot": "10",
+ "type": "t_bytes_storage"
+ }
+ ],
+ "types": {
+ "t_address": {
+ "encoding": "inplace",
+ "label": "address",
+ "numberOfBytes": "20"
+ },
+ "t_array(t_uint256)2_storage": {
+ "base": "t_uint256",
+ "encoding": "inplace",
+ "label": "uint256[2]",
+ "numberOfBytes": "64"
+ },
+ "t_array(t_uint256)dyn_storage": {
+ "base": "t_uint256",
+ "encoding": "dynamic_array",
+ "label": "uint256[]",
+ "numberOfBytes": "32"
+ },
+ "t_bool": {
+ "encoding": "inplace",
+ "label": "bool",
+ "numberOfBytes": "1"
+ },
+ "t_bytes_storage": {
+ "encoding": "bytes",
+ "label": "bytes",
+ "numberOfBytes": "32"
+ },
+ "t_mapping(t_address,t_bool)": {
+ "encoding": "mapping",
+ "key": "t_address",
+ "label": "mapping(address => bool)",
+ "numberOfBytes": "32",
+ "value": "t_bool"
+ },
+ "t_mapping(t_uint256,t_mapping(t_address,t_bool))": {
+ "encoding": "mapping",
+ "key": "t_uint256",
+ "label": "mapping(uint256 => mapping(address => bool))",
+ "numberOfBytes": "32",
+ "value": "t_mapping(t_address,t_bool)"
+ },
+ "t_string_storage": {
+ "encoding": "bytes",
+ "label": "string",
+ "numberOfBytes": "32"
+ },
+ "t_struct(S)12_storage": {
+ "encoding": "inplace",
+ "label": "struct A.S",
+ "members": [
+ {
+ "astId": 2,
+ "contract": "fileA:A",
+ "label": "a",
+ "offset": 0,
+ "slot": "0",
+ "type": "t_uint128"
+ },
+ {
+ "astId": 4,
+ "contract": "fileA:A",
+ "label": "b",
+ "offset": 16,
+ "slot": "0",
+ "type": "t_uint128"
+ },
+ {
+ "astId": 8,
+ "contract": "fileA:A",
+ "label": "staticArray",
+ "offset": 0,
+ "slot": "1",
+ "type": "t_array(t_uint256)2_storage"
+ },
+ {
+ "astId": 11,
+ "contract": "fileA:A",
+ "label": "dynArray",
+ "offset": 0,
+ "slot": "3",
+ "type": "t_array(t_uint256)dyn_storage"
+ }
+ ],
+ "numberOfBytes": "128"
+ },
+ "t_uint128": {
+ "encoding": "inplace",
+ "label": "uint128",
+ "numberOfBytes": "16"
+ },
+ "t_uint256": {
+ "encoding": "inplace",
+ "label": "uint256",
+ "numberOfBytes": "32"
+ }
+ }
+ }
+
.. index: memory layout
****************
diff --git a/docs/natspec-format.rst b/docs/natspec-format.rst
index 5c9d98736..cfc94d81b 100644
--- a/docs/natspec-format.rst
+++ b/docs/natspec-format.rst
@@ -49,22 +49,22 @@ The following example shows a contract and a function using all available tags.
.. code:: solidity
- pragma solidity ^0.5.6;
+ pragma solidity ^0.5.6;
- /// @title A simulator for trees
- /// @author Larry A. Gardner
- /// @notice You can use this contract for only the most basic simulation
- /// @dev All function calls are currently implemented without side effects
- contract Tree {
- /// @author Mary A. Botanist
- /// @notice Calculate tree age in years, rounded up, for live trees
- /// @dev The Alexandr N. Tetearing algorithm could increase precision
- /// @param rings The number of rings from dendrochronological sample
- /// @return age in years, rounded up for partial years
- function age(uint256 rings) external pure returns (uint256) {
- return rings + 1;
- }
- }
+ /// @title A simulator for trees
+ /// @author Larry A. Gardner
+ /// @notice You can use this contract for only the most basic simulation
+ /// @dev All function calls are currently implemented without side effects
+ contract Tree {
+ /// @author Mary A. Botanist
+ /// @notice Calculate tree age in years, rounded up, for live trees
+ /// @dev The Alexandr N. Tetearing algorithm could increase precision
+ /// @param rings The number of rings from dendrochronological sample
+ /// @return age in years, rounded up for partial years
+ function age(uint256 rings) external pure returns (uint256) {
+ return rings + 1;
+ }
+ }
.. _header-tags:
diff --git a/docs/resources.rst b/docs/resources.rst
index a3a89113a..4335c3deb 100644
--- a/docs/resources.rst
+++ b/docs/resources.rst
@@ -127,6 +127,9 @@ Solidity Tools
* `Universal Mutator `_
A tool for mutation generation, with configurable rules and support for Solidity and Vyper.
+* `PIET `_
+ A tool to develop, audit and use solidity smart contracts through a simple graphical interface.
+
.. note::
Information like variable names, comments, and source code formatting is lost in the compilation process and it is not possible to completely recover the original source code. Decompiling smart contracts to view the original source code might not be possible, or the end result that useful.
diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst
index 3f18bf3b5..0b37d2fb5 100644
--- a/docs/security-considerations.rst
+++ b/docs/security-considerations.rst
@@ -499,26 +499,26 @@ not mean loss of proving power.
::
- pragma solidity >=0.5.0;
- pragma experimental SMTChecker;
+ pragma solidity >=0.5.0;
+ pragma experimental SMTChecker;
- contract Recover
- {
- function f(
- bytes32 hash,
- uint8 _v1, uint8 _v2,
- bytes32 _r1, bytes32 _r2,
- bytes32 _s1, bytes32 _s2
- ) public pure returns (address) {
- address a1 = ecrecover(hash, _v1, _r1, _s1);
- require(_v1 == _v2);
- require(_r1 == _r2);
- require(_s1 == _s2);
- address a2 = ecrecover(hash, _v2, _r2, _s2);
- assert(a1 == a2);
- return a1;
- }
- }
+ contract Recover
+ {
+ function f(
+ bytes32 hash,
+ uint8 _v1, uint8 _v2,
+ bytes32 _r1, bytes32 _r2,
+ bytes32 _s1, bytes32 _s2
+ ) public pure returns (address) {
+ address a1 = ecrecover(hash, _v1, _r1, _s1);
+ require(_v1 == _v2);
+ require(_r1 == _r2);
+ require(_s1 == _s2);
+ address a2 = ecrecover(hash, _v2, _r2, _s2);
+ assert(a1 == a2);
+ return a1;
+ }
+ }
In the example above, the SMTChecker is not expressive enough to actually
compute ``ecrecover``, but by modelling the function calls as uninterpreted
@@ -552,34 +552,34 @@ types.
::
- pragma solidity >=0.5.0;
- pragma experimental SMTChecker;
- // This will not compile
- contract Aliasing
- {
- uint[] array;
- function f(
- uint[] memory a,
- uint[] memory b,
- uint[][] memory c,
- uint[] storage d
- ) internal view {
- require(array[0] == 42);
- require(a[0] == 2);
- require(c[0][0] == 2);
- require(d[0] == 2);
- b[0] = 1;
- // Erasing knowledge about memory references should not
- // erase knowledge about state variables.
- assert(array[0] == 42);
- // Fails because `a == b` is possible.
- assert(a[0] == 2);
- // Fails because `c[i] == b` is possible.
- assert(c[0][0] == 2);
- assert(d[0] == 2);
- assert(b[0] == 1);
- }
- }
+ pragma solidity >=0.5.0;
+ pragma experimental SMTChecker;
+ // This will report a warning
+ contract Aliasing
+ {
+ uint[] array;
+ function f(
+ uint[] memory a,
+ uint[] memory b,
+ uint[][] memory c,
+ uint[] storage d
+ ) internal view {
+ require(array[0] == 42);
+ require(a[0] == 2);
+ require(c[0][0] == 2);
+ require(d[0] == 2);
+ b[0] = 1;
+ // Erasing knowledge about memory references should not
+ // erase knowledge about state variables.
+ assert(array[0] == 42);
+ // Fails because `a == b` is possible.
+ assert(a[0] == 2);
+ // Fails because `c[i] == b` is possible.
+ assert(c[0][0] == 2);
+ assert(d[0] == 2);
+ assert(b[0] == 1);
+ }
+ }
After the assignment to ``b[0]``, we need to clear knowledge about ``a`` since
it has the same type (``uint[]``) and data location (memory). We also need to
diff --git a/docs/types/mapping-types.rst b/docs/types/mapping-types.rst
index a7d607786..24eb1bc9d 100644
--- a/docs/types/mapping-types.rst
+++ b/docs/types/mapping-types.rst
@@ -4,7 +4,8 @@
Mapping Types
=============
-You declare mapping types with the syntax ``mapping(_KeyType => _ValueType)``.
+Mapping types use the syntax ``mapping(_KeyType => _ValueType)`` and variables
+are declared as a mapping type using the syntax ``mapping (_KeyType => _ValueType) _VariableModifiers _VariableName``.
The ``_KeyType`` can be any elementary type. This means it can be any of
the built-in value types plus ``bytes`` and ``string``. User-defined
or complex types like contract types, enums, mappings, structs and any array type
@@ -27,11 +28,16 @@ They cannot be used as parameters or return parameters
of contract functions that are publicly visible.
You can mark state variables of mapping type as ``public`` and Solidity creates a
-:ref:`getter ` for you. The ``_KeyType`` becomes a
-parameter for the getter. If ``_ValueType`` is a value type or a struct,
-the getter returns ``_ValueType``.
+:ref:`getter ` for you. The ``_KeyType`` becomes a parameter for the getter.
+If ``_ValueType`` is a value type or a struct, the getter returns ``_ValueType``.
If ``_ValueType`` is an array or a mapping, the getter has one parameter for
-each ``_KeyType``, recursively. For example with a mapping:
+each ``_KeyType``, recursively.
+
+In the example below, the ``MappingExample`` contract defines a public ``balances``
+mapping, with the key type an ``address``, and a value type a ``uint``, mapping
+an Ethereum address to an unsigned integer value. As ``uint`` is a value type, the getter
+returns a value that matches the type, which you can see in the ``MappingUser``
+contract that returns the value at the specified address.
::
@@ -53,7 +59,146 @@ each ``_KeyType``, recursively. For example with a mapping:
}
}
+The example below is a simplified version of an `ERC20 token `_.
+``_allowances`` is an example of a mapping type inside another mapping type.
+The example below uses ``_allowances`` to record the amount someone else is allowed to withdraw from your account.
-.. note::
- Mappings are not iterable, but it is possible to implement a data structure
- on top of them. For an example, see `iterable mapping `_.
+::
+
+ pragma solidity >=0.4.0 <0.7.0;
+
+ contract MappingExample {
+
+ mapping (address => uint256) private _balances;
+ mapping (address => mapping (address => uint256)) private _allowances;
+
+ event Transfer(address indexed from, address indexed to, uint256 value);
+ event Approval(address indexed owner, address indexed spender, uint256 value);
+
+ function allowance(address owner, address spender) public view returns (uint256) {
+ return _allowances[owner][spender];
+ }
+
+ function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
+ _transfer(sender, recipient, amount);
+ approve(sender, msg.sender, amount);
+ return true;
+ }
+
+ function approve(address owner, address spender, uint256 amount) public returns (bool) {
+ require(owner != address(0), "ERC20: approve from the zero address");
+ require(spender != address(0), "ERC20: approve to the zero address");
+
+ _allowances[owner][spender] = amount;
+ emit Approval(owner, spender, amount);
+ return true;
+ }
+
+ function _transfer(address sender, address recipient, uint256 amount) internal {
+ require(sender != address(0), "ERC20: transfer from the zero address");
+ require(recipient != address(0), "ERC20: transfer to the zero address");
+
+ _balances[sender] -= amount;
+ _balances[recipient] += amount;
+ emit Transfer(sender, recipient, amount);
+ }
+ }
+
+
+.. index:: !iterable mappings
+.. _iterable-mappings:
+
+Iterable Mappings
+-----------------
+
+Mappings are not iterable, but it is possible to implement a data structure on
+top of them and iterate over that. For example, the code below implements an
+``IterableMapping`` library that the ``User`` contract then adds data too, and
+the ``sum`` function iterates over to sum all the values.
+
+::
+
+ pragma solidity >=0.4.0 <0.7.0;
+
+ library IterableMapping {
+
+ struct itmap {
+ mapping(uint => IndexValue) data;
+ KeyFlag[] keys;
+ uint size;
+ }
+
+ struct IndexValue { uint keyIndex; uint value; }
+ struct KeyFlag { uint key; bool deleted; }
+
+ function insert(itmap storage self, uint key, uint value) internal returns (bool replaced) {
+ uint keyIndex = self.data[key].keyIndex;
+ self.data[key].value = value;
+ if (keyIndex > 0)
+ return true;
+ else {
+ keyIndex = self.keys.length++;
+ self.data[key].keyIndex = keyIndex + 1;
+ self.keys[keyIndex].key = key;
+ self.size++;
+ return false;
+ }
+ }
+
+ function remove(itmap storage self, uint key) internal returns (bool success) {
+ uint keyIndex = self.data[key].keyIndex;
+ if (keyIndex == 0)
+ return false;
+ delete self.data[key];
+ self.keys[keyIndex - 1].deleted = true;
+ self.size --;
+ }
+
+ function contains(itmap storage self, uint key) internal view returns (bool) {
+ return self.data[key].keyIndex > 0;
+ }
+
+ function iterate_start(itmap storage self) internal view returns (uint keyIndex) {
+ return iterate_next(self, uint(-1));
+ }
+
+ function iterate_valid(itmap storage self, uint keyIndex) internal view returns (bool) {
+ return keyIndex < self.keys.length;
+ }
+
+ function iterate_next(itmap storage self, uint keyIndex) internal view returns (uint r_keyIndex) {
+ keyIndex++;
+ while (keyIndex < self.keys.length && self.keys[keyIndex].deleted)
+ keyIndex++;
+ return keyIndex;
+ }
+
+ function iterate_get(itmap storage self, uint keyIndex) internal view returns (uint key, uint value) {
+ key = self.keys[keyIndex].key;
+ value = self.data[key].value;
+ }
+ }
+
+ // How to use it
+ contract User {
+ // Just a struct holding our data.
+ IterableMapping.itmap data;
+
+ // Insert something
+ function insert(uint k, uint v) public returns (uint size) {
+ // Actually calls itmap_impl.insert, auto-supplying the first parameter for us.
+ IterableMapping.insert(data, k, v);
+ // We can still access members of the struct - but we should take care not to mess with them.
+ return data.size;
+ }
+
+ // Computes the sum of all stored data.
+ function sum() public view returns (uint s) {
+ for (uint i = IterableMapping.iterate_start(data);
+ IterableMapping.iterate_valid(data, i);
+ i = IterableMapping.iterate_next(data, i)) {
+ (, uint value) = IterableMapping.iterate_get(data, i);
+ s += value;
+ }
+ }
+ }
\ No newline at end of file
diff --git a/docs/types/operators.rst b/docs/types/operators.rst
index afbe2a742..f53d6ee57 100644
--- a/docs/types/operators.rst
+++ b/docs/types/operators.rst
@@ -17,7 +17,7 @@ equivalent to ``a = 0``, but it can also be used on arrays, where it assigns a d
array of length zero or a static array of the same length with all elements set to their
initial value. ``delete a[x]`` deletes the item at index ``x`` of the array and leaves
all other elements and the length of the array untouched. This especially means that it leaves
-a gap in the array. If you plan to remove items, a mapping is probably a better choice.
+a gap in the array. If you plan to remove items, a :ref:`mapping ` is probably a better choice.
For structs, it assigns a struct with all members reset. In other words, the value of ``a`` after ``delete a`` is the same as if ``a`` would be declared without assignment, with the following caveat:
diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst
index 12df74e84..db09b2a09 100644
--- a/docs/using-the-compiler.rst
+++ b/docs/using-the-compiler.rst
@@ -276,6 +276,7 @@ Input Description
// metadata - Metadata
// ir - Yul intermediate representation of the code before optimization
// irOptimized - Intermediate representation after optimization
+ // storageLayout - Slots, offsets and types of the contract's state variables.
// evm.assembly - New assembly format
// evm.legacyAssembly - Old-style assembly format in JSON
// evm.bytecode.object - Bytecode object
@@ -376,6 +377,8 @@ Output Description
"devdoc": {},
// Intermediate representation (string)
"ir": "",
+ // See the Storage Layout documentation.
+ "storageLayout": {"storage": [...], "types": {...} },
// EVM-related outputs
"evm": {
// Assembly (string)
diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt
index 8bc3bb525..7b4342f12 100644
--- a/libdevcore/CMakeLists.txt
+++ b/libdevcore/CMakeLists.txt
@@ -34,6 +34,10 @@ set(sources
)
add_library(devcore ${sources})
-target_link_libraries(devcore PUBLIC jsoncpp Boost::boost Boost::filesystem Boost::regex Boost::system)
+target_link_libraries(devcore PUBLIC jsoncpp Boost::boost Boost::filesystem Boost::system)
target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}")
add_dependencies(devcore solidity_BuildInfo.h)
+
+if(SOLC_LINK_STATIC)
+ target_link_libraries(devcore PUBLIC Threads::Threads)
+endif()
diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h
index bc71c88c4..017a632dc 100644
--- a/libdevcore/CommonData.h
+++ b/libdevcore/CommonData.h
@@ -25,11 +25,10 @@
#include
-#include
-
#include
#include
#include
+#include
#include
#include
#include
@@ -277,7 +276,7 @@ void iterateReplacing(std::vector& _vector, F const& _f)
std::vector modifiedVector;
for (size_t i = 0; i < _vector.size(); ++i)
{
- if (boost::optional> r = _f(_vector[i]))
+ if (std::optional> r = _f(_vector[i]))
{
if (!useModified)
{
@@ -305,7 +304,7 @@ void iterateReplacingWindow(std::vector& _vector, F const& _f, std::index_seq
size_t i = 0;
for (; i + sizeof...(I) <= _vector.size(); ++i)
{
- if (boost::optional> r = _f(_vector[i + I]...))
+ if (std::optional> r = _f(_vector[i + I]...))
{
if (!useModified)
{
diff --git a/libdevcore/Whiskers.cpp b/libdevcore/Whiskers.cpp
index c48c8d6f1..09415960d 100644
--- a/libdevcore/Whiskers.cpp
+++ b/libdevcore/Whiskers.cpp
@@ -25,7 +25,7 @@
#include
-#include
+#include
using namespace std;
using namespace dev;
@@ -72,9 +72,9 @@ string Whiskers::render() const
void Whiskers::checkParameterValid(string const& _parameter) const
{
- static boost::regex validParam("^" + paramRegex() + "$");
+ static regex validParam("^" + paramRegex() + "$");
assertThrow(
- boost::regex_match(_parameter, validParam),
+ regex_match(_parameter, validParam),
WhiskersError,
"Parameter" + _parameter + " contains invalid characters."
);
@@ -99,6 +99,32 @@ void Whiskers::checkParameterUnknown(string const& _parameter) const
);
}
+namespace
+{
+template
+string regex_replace(
+ string const& _source,
+ regex const& _pattern,
+ ReplaceCallback _replace,
+ regex_constants::match_flag_type _flags = regex_constants::match_default
+)
+{
+ sregex_iterator curMatch(_source.begin(), _source.end(), _pattern, _flags);
+ sregex_iterator matchEnd;
+ string::const_iterator lastMatchedPos(_source.cbegin());
+ string result;
+ while (curMatch != matchEnd)
+ {
+ result.append(curMatch->prefix().first, curMatch->prefix().second);
+ result.append(_replace(*curMatch));
+ lastMatchedPos = (*curMatch)[0].second;
+ ++curMatch;
+ }
+ result.append(lastMatchedPos, _source.cend());
+ return result;
+}
+}
+
string Whiskers::replace(
string const& _template,
StringMap const& _parameters,
@@ -106,8 +132,7 @@ string Whiskers::replace(
map> const& _listParameters
)
{
- using namespace boost;
- static regex listOrTag("<(" + paramRegex() + ")>|<#(" + paramRegex() + ")>(.*?)\\2>|<\\?(" + paramRegex() + ")>(.*?)((.*?))?\\4>");
+ static regex listOrTag("<(" + paramRegex() + ")>|<#(" + paramRegex() + ")>((?:.|\\r|\\n)*?)\\2>|<\\?(" + paramRegex() + ")>((?:.|\\r|\\n)*?)(((?:.|\\r|\\n)*?))?\\4>");
return regex_replace(_template, listOrTag, [&](match_results _match) -> string
{
string tagName(_match[1]);
diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp
index 183cba930..ae8d001d7 100644
--- a/libevmasm/Assembly.cpp
+++ b/libevmasm/Assembly.cpp
@@ -258,7 +258,7 @@ Json::Value Assembly::createJsonValue(string _name, int _begin, int _end, string
string Assembly::toStringInHex(u256 _value)
{
std::stringstream hexStr;
- hexStr << hex << _value;
+ hexStr << std::uppercase << hex << _value;
return hexStr.str();
}
diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp
index cc5561daf..8c0a45308 100644
--- a/libevmasm/Instruction.cpp
+++ b/libevmasm/Instruction.cpp
@@ -356,13 +356,13 @@ string dev::eth::disassemble(bytes const& _mem)
stringstream ret;
eachInstruction(_mem, [&](Instruction _instr, u256 const& _data) {
if (!isValidInstruction(_instr))
- ret << "0x" << hex << int(_instr) << " ";
+ ret << "0x" << std::uppercase << std::hex << int(_instr) << " ";
else
{
InstructionInfo info = instructionInfo(_instr);
ret << info.name << " ";
if (info.additional)
- ret << "0x" << hex << _data << " ";
+ ret << "0x" << std::uppercase << std::hex << _data << " ";
}
});
return ret.str();
diff --git a/libevmasm/RuleList.h b/libevmasm/RuleList.h
index 58e773f30..e67f614ae 100644
--- a/libevmasm/RuleList.h
+++ b/libevmasm/RuleList.h
@@ -49,7 +49,7 @@ template S modWorkaround(S const& _a, S const& _b)
// This works around a bug fixed with Boost 1.64.
// https://www.boost.org/doc/libs/1_68_0/libs/multiprecision/doc/html/boost_multiprecision/map/hist.html#boost_multiprecision.map.hist.multiprecision_2_3_1_boost_1_64
-inline u256 shlWorkaround(u256 const& _x, unsigned _amount)
+template S shlWorkaround(S const& _x, unsigned _amount)
{
return u256((bigint(_x) << _amount) & u256(-1));
}
@@ -66,44 +66,50 @@ std::vector> simplificationRuleListPart1(
Pattern
)
{
+ using Word = typename Pattern::Word;
return std::vector> {
// arithmetic on constants
- {{Instruction::ADD, {A, B}}, [=]{ return A.d() + B.d(); }, false},
- {{Instruction::MUL, {A, B}}, [=]{ return A.d() * B.d(); }, false},
- {{Instruction::SUB, {A, B}}, [=]{ return A.d() - B.d(); }, false},
- {{Instruction::DIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false},
- {{Instruction::SDIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
- {{Instruction::MOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false},
- {{Instruction::SMOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
- {{Instruction::EXP, {A, B}}, [=]{ return u256(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << 256)); }, false},
- {{Instruction::NOT, {A}}, [=]{ return ~A.d(); }, false},
- {{Instruction::LT, {A, B}}, [=]() -> u256 { return A.d() < B.d() ? 1 : 0; }, false},
- {{Instruction::GT, {A, B}}, [=]() -> u256 { return A.d() > B.d() ? 1 : 0; }, false},
- {{Instruction::SLT, {A, B}}, [=]() -> u256 { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false},
- {{Instruction::SGT, {A, B}}, [=]() -> u256 { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }, false},
- {{Instruction::EQ, {A, B}}, [=]() -> u256 { return A.d() == B.d() ? 1 : 0; }, false},
- {{Instruction::ISZERO, {A}}, [=]() -> u256 { return A.d() == 0 ? 1 : 0; }, false},
- {{Instruction::AND, {A, B}}, [=]{ return A.d() & B.d(); }, false},
- {{Instruction::OR, {A, B}}, [=]{ return A.d() | B.d(); }, false},
- {{Instruction::XOR, {A, B}}, [=]{ return A.d() ^ B.d(); }, false},
- {{Instruction::BYTE, {A, B}}, [=]{ return A.d() >= 32 ? 0 : (B.d() >> unsigned(8 * (31 - A.d()))) & 0xff; }, false},
- {{Instruction::ADDMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : u256((bigint(A.d()) + bigint(B.d())) % C.d()); }, false},
- {{Instruction::MULMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : u256((bigint(A.d()) * bigint(B.d())) % C.d()); }, false},
- {{Instruction::SIGNEXTEND, {A, B}}, [=]() -> u256 {
- if (A.d() >= 31)
+ {{Pattern::Builtins::ADD, {A, B}}, [=]{ return A.d() + B.d(); }, false},
+ {{Pattern::Builtins::MUL, {A, B}}, [=]{ return A.d() * B.d(); }, false},
+ {{Pattern::Builtins::SUB, {A, B}}, [=]{ return A.d() - B.d(); }, false},
+ {{Pattern::Builtins::DIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false},
+ {{Pattern::Builtins::SDIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
+ {{Pattern::Builtins::MOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false},
+ {{Pattern::Builtins::SMOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
+ {{Pattern::Builtins::EXP, {A, B}}, [=]{ return Word(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << Pattern::WordSize)); }, false},
+ {{Pattern::Builtins::NOT, {A}}, [=]{ return ~A.d(); }, false},
+ {{Pattern::Builtins::LT, {A, B}}, [=]() -> Word { return A.d() < B.d() ? 1 : 0; }, false},
+ {{Pattern::Builtins::GT, {A, B}}, [=]() -> Word { return A.d() > B.d() ? 1 : 0; }, false},
+ {{Pattern::Builtins::SLT, {A, B}}, [=]() -> Word { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false},
+ {{Pattern::Builtins::SGT, {A, B}}, [=]() -> Word { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }, false},
+ {{Pattern::Builtins::EQ, {A, B}}, [=]() -> Word { return A.d() == B.d() ? 1 : 0; }, false},
+ {{Pattern::Builtins::ISZERO, {A}}, [=]() -> Word { return A.d() == 0 ? 1 : 0; }, false},
+ {{Pattern::Builtins::AND, {A, B}}, [=]{ return A.d() & B.d(); }, false},
+ {{Pattern::Builtins::OR, {A, B}}, [=]{ return A.d() | B.d(); }, false},
+ {{Pattern::Builtins::XOR, {A, B}}, [=]{ return A.d() ^ B.d(); }, false},
+ {{Pattern::Builtins::BYTE, {A, B}}, [=]{
+ return
+ A.d() >= Pattern::WordSize / 8 ?
+ 0 :
+ (B.d() >> unsigned(8 * (Pattern::WordSize / 8 - 1 - A.d()))) & 0xff;
+ }, false},
+ {{Pattern::Builtins::ADDMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) + bigint(B.d())) % C.d()); }, false},
+ {{Pattern::Builtins::MULMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) * bigint(B.d())) % C.d()); }, false},
+ {{Pattern::Builtins::SIGNEXTEND, {A, B}}, [=]() -> Word {
+ if (A.d() >= Pattern::WordSize / 8 - 1)
return B.d();
unsigned testBit = unsigned(A.d()) * 8 + 7;
- u256 mask = (u256(1) << testBit) - 1;
+ Word mask = (Word(1) << testBit) - 1;
return boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask;
}, false},
- {{Instruction::SHL, {A, B}}, [=]{
- if (A.d() > 255)
- return u256(0);
+ {{Pattern::Builtins::SHL, {A, B}}, [=]{
+ if (A.d() >= Pattern::WordSize)
+ return Word(0);
return shlWorkaround(B.d(), unsigned(A.d()));
}, false},
- {{Instruction::SHR, {A, B}}, [=]{
- if (A.d() > 255)
- return u256(0);
+ {{Pattern::Builtins::SHR, {A, B}}, [=]{
+ if (A.d() >= Pattern::WordSize)
+ return Word(0);
return B.d() >> unsigned(A.d());
}, false}
};
@@ -118,50 +124,51 @@ std::vector> simplificationRuleListPart2(
Pattern Y
)
{
+ using Word = typename Pattern::Word;
return std::vector> {
// invariants involving known constants
- {{Instruction::ADD, {X, 0}}, [=]{ return X; }, false},
- {{Instruction::ADD, {0, X}}, [=]{ return X; }, false},
- {{Instruction::SUB, {X, 0}}, [=]{ return X; }, false},
- {{Instruction::SUB, {~u256(0), X}}, [=]() -> Pattern { return {Instruction::NOT, {X}}; }, false},
- {{Instruction::MUL, {X, 0}}, [=]{ return u256(0); }, true},
- {{Instruction::MUL, {0, X}}, [=]{ return u256(0); }, true},
- {{Instruction::MUL, {X, 1}}, [=]{ return X; }, false},
- {{Instruction::MUL, {1, X}}, [=]{ return X; }, false},
- {{Instruction::MUL, {X, u256(-1)}}, [=]() -> Pattern { return {Instruction::SUB, {0, X}}; }, false},
- {{Instruction::MUL, {u256(-1), X}}, [=]() -> Pattern { return {Instruction::SUB, {0, X}}; }, false},
- {{Instruction::DIV, {X, 0}}, [=]{ return u256(0); }, true},
- {{Instruction::DIV, {0, X}}, [=]{ return u256(0); }, true},
- {{Instruction::DIV, {X, 1}}, [=]{ return X; }, false},
- {{Instruction::SDIV, {X, 0}}, [=]{ return u256(0); }, true},
- {{Instruction::SDIV, {0, X}}, [=]{ return u256(0); }, true},
- {{Instruction::SDIV, {X, 1}}, [=]{ return X; }, false},
- {{Instruction::AND, {X, ~u256(0)}}, [=]{ return X; }, false},
- {{Instruction::AND, {~u256(0), X}}, [=]{ return X; }, false},
- {{Instruction::AND, {X, 0}}, [=]{ return u256(0); }, true},
- {{Instruction::AND, {0, X}}, [=]{ return u256(0); }, true},
- {{Instruction::OR, {X, 0}}, [=]{ return X; }, false},
- {{Instruction::OR, {0, X}}, [=]{ return X; }, false},
- {{Instruction::OR, {X, ~u256(0)}}, [=]{ return ~u256(0); }, true},
- {{Instruction::OR, {~u256(0), X}}, [=]{ return ~u256(0); }, true},
- {{Instruction::XOR, {X, 0}}, [=]{ return X; }, false},
- {{Instruction::XOR, {0, X}}, [=]{ return X; }, false},
- {{Instruction::MOD, {X, 0}}, [=]{ return u256(0); }, true},
- {{Instruction::MOD, {0, X}}, [=]{ return u256(0); }, true},
- {{Instruction::EQ, {X, 0}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false },
- {{Instruction::EQ, {0, X}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false },
- {{Instruction::SHL, {0, X}}, [=]{ return X; }, false},
- {{Instruction::SHR, {0, X}}, [=]{ return X; }, false},
- {{Instruction::SHL, {X, 0}}, [=]{ return u256(0); }, true},
- {{Instruction::SHR, {X, 0}}, [=]{ return u256(0); }, true},
- {{Instruction::GT, {X, 0}}, [=]() -> Pattern { return {Instruction::ISZERO, {{Instruction::ISZERO, {X}}}}; }, false},
- {{Instruction::LT, {0, X}}, [=]() -> Pattern { return {Instruction::ISZERO, {{Instruction::ISZERO, {X}}}}; }, false},
- {{Instruction::GT, {X, ~u256(0)}}, [=]{ return u256(0); }, true},
- {{Instruction::LT, {~u256(0), X}}, [=]{ return u256(0); }, true},
- {{Instruction::GT, {0, X}}, [=]{ return u256(0); }, true},
- {{Instruction::LT, {X, 0}}, [=]{ return u256(0); }, true},
- {{Instruction::AND, {{Instruction::BYTE, {X, Y}}, {u256(0xff)}}}, [=]() -> Pattern { return {Instruction::BYTE, {X, Y}}; }, false},
- {{Instruction::BYTE, {31, X}}, [=]() -> Pattern { return {Instruction::AND, {X, u256(0xff)}}; }, false}
+ {{Pattern::Builtins::ADD, {X, 0}}, [=]{ return X; }, false},
+ {{Pattern::Builtins::ADD, {0, X}}, [=]{ return X; }, false},
+ {{Pattern::Builtins::SUB, {X, 0}}, [=]{ return X; }, false},
+ {{Pattern::Builtins::SUB, {~Word(0), X}}, [=]() -> Pattern { return {Pattern::Builtins::NOT, {X}}; }, false},
+ {{Pattern::Builtins::MUL, {X, 0}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::MUL, {0, X}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::MUL, {X, 1}}, [=]{ return X; }, false},
+ {{Pattern::Builtins::MUL, {1, X}}, [=]{ return X; }, false},
+ {{Pattern::Builtins::MUL, {X, Word(-1)}}, [=]() -> Pattern { return {Pattern::Builtins::SUB, {0, X}}; }, false},
+ {{Pattern::Builtins::MUL, {Word(-1), X}}, [=]() -> Pattern { return {Pattern::Builtins::SUB, {0, X}}; }, false},
+ {{Pattern::Builtins::DIV, {X, 0}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::DIV, {0, X}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::DIV, {X, 1}}, [=]{ return X; }, false},
+ {{Pattern::Builtins::SDIV, {X, 0}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::SDIV, {0, X}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::SDIV, {X, 1}}, [=]{ return X; }, false},
+ {{Pattern::Builtins::AND, {X, ~Word(0)}}, [=]{ return X; }, false},
+ {{Pattern::Builtins::AND, {~Word(0), X}}, [=]{ return X; }, false},
+ {{Pattern::Builtins::AND, {X, 0}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::AND, {0, X}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::OR, {X, 0}}, [=]{ return X; }, false},
+ {{Pattern::Builtins::OR, {0, X}}, [=]{ return X; }, false},
+ {{Pattern::Builtins::OR, {X, ~Word(0)}}, [=]{ return ~Word(0); }, true},
+ {{Pattern::Builtins::OR, {~Word(0), X}}, [=]{ return ~Word(0); }, true},
+ {{Pattern::Builtins::XOR, {X, 0}}, [=]{ return X; }, false},
+ {{Pattern::Builtins::XOR, {0, X}}, [=]{ return X; }, false},
+ {{Pattern::Builtins::MOD, {X, 0}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::MOD, {0, X}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::EQ, {X, 0}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, false },
+ {{Pattern::Builtins::EQ, {0, X}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, false },
+ {{Pattern::Builtins::SHL, {0, X}}, [=]{ return X; }, false},
+ {{Pattern::Builtins::SHR, {0, X}}, [=]{ return X; }, false},
+ {{Pattern::Builtins::SHL, {X, 0}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::SHR, {X, 0}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::GT, {X, 0}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}; }, false},
+ {{Pattern::Builtins::LT, {0, X}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}; }, false},
+ {{Pattern::Builtins::GT, {X, ~Word(0)}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::LT, {~Word(0), X}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::GT, {0, X}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::LT, {X, 0}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::AND, {{Pattern::Builtins::BYTE, {X, Y}}, {Word(0xff)}}}, [=]() -> Pattern { return {Pattern::Builtins::BYTE, {X, Y}}; }, false},
+ {{Pattern::Builtins::BYTE, {Pattern::WordSize / 8 - 1, X}}, [=]() -> Pattern { return {Pattern::Builtins::AND, {X, Word(0xff)}}; }, false}
};
}
@@ -174,18 +181,19 @@ std::vector> simplificationRuleListPart3(
Pattern
)
{
+ using Word = typename Pattern::Word;
return std::vector> {
// operations involving an expression and itself
- {{Instruction::AND, {X, X}}, [=]{ return X; }, true},
- {{Instruction::OR, {X, X}}, [=]{ return X; }, true},
- {{Instruction::XOR, {X, X}}, [=]{ return u256(0); }, true},
- {{Instruction::SUB, {X, X}}, [=]{ return u256(0); }, true},
- {{Instruction::EQ, {X, X}}, [=]{ return u256(1); }, true},
- {{Instruction::LT, {X, X}}, [=]{ return u256(0); }, true},
- {{Instruction::SLT, {X, X}}, [=]{ return u256(0); }, true},
- {{Instruction::GT, {X, X}}, [=]{ return u256(0); }, true},
- {{Instruction::SGT, {X, X}}, [=]{ return u256(0); }, true},
- {{Instruction::MOD, {X, X}}, [=]{ return u256(0); }, true}
+ {{Pattern::Builtins::AND, {X, X}}, [=]{ return X; }, true},
+ {{Pattern::Builtins::OR, {X, X}}, [=]{ return X; }, true},
+ {{Pattern::Builtins::XOR, {X, X}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::SUB, {X, X}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::EQ, {X, X}}, [=]{ return Word(1); }, true},
+ {{Pattern::Builtins::LT, {X, X}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::SLT, {X, X}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::GT, {X, X}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::SGT, {X, X}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::MOD, {X, X}}, [=]{ return Word(0); }, true}
};
}
@@ -198,25 +206,26 @@ std::vector> simplificationRuleListPart4(
Pattern Y
)
{
+ using Word = typename Pattern::Word;
return std::vector> {
// logical instruction combinations
- {{Instruction::NOT, {{Instruction::NOT, {X}}}}, [=]{ return X; }, false},
- {{Instruction::XOR, {X, {Instruction::XOR, {X, Y}}}}, [=]{ return Y; }, true},
- {{Instruction::XOR, {X, {Instruction::XOR, {Y, X}}}}, [=]{ return Y; }, true},
- {{Instruction::XOR, {{Instruction::XOR, {X, Y}}, X}}, [=]{ return Y; }, true},
- {{Instruction::XOR, {{Instruction::XOR, {Y, X}}, X}}, [=]{ return Y; }, true},
- {{Instruction::OR, {X, {Instruction::AND, {X, Y}}}}, [=]{ return X; }, true},
- {{Instruction::OR, {X, {Instruction::AND, {Y, X}}}}, [=]{ return X; }, true},
- {{Instruction::OR, {{Instruction::AND, {X, Y}}, X}}, [=]{ return X; }, true},
- {{Instruction::OR, {{Instruction::AND, {Y, X}}, X}}, [=]{ return X; }, true},
- {{Instruction::AND, {X, {Instruction::OR, {X, Y}}}}, [=]{ return X; }, true},
- {{Instruction::AND, {X, {Instruction::OR, {Y, X}}}}, [=]{ return X; }, true},
- {{Instruction::AND, {{Instruction::OR, {X, Y}}, X}}, [=]{ return X; }, true},
- {{Instruction::AND, {{Instruction::OR, {Y, X}}, X}}, [=]{ return X; }, true},
- {{Instruction::AND, {X, {Instruction::NOT, {X}}}}, [=]{ return u256(0); }, true},
- {{Instruction::AND, {{Instruction::NOT, {X}}, X}}, [=]{ return u256(0); }, true},
- {{Instruction::OR, {X, {Instruction::NOT, {X}}}}, [=]{ return ~u256(0); }, true},
- {{Instruction::OR, {{Instruction::NOT, {X}}, X}}, [=]{ return ~u256(0); }, true},
+ {{Pattern::Builtins::NOT, {{Pattern::Builtins::NOT, {X}}}}, [=]{ return X; }, false},
+ {{Pattern::Builtins::XOR, {X, {Pattern::Builtins::XOR, {X, Y}}}}, [=]{ return Y; }, true},
+ {{Pattern::Builtins::XOR, {X, {Pattern::Builtins::XOR, {Y, X}}}}, [=]{ return Y; }, true},
+ {{Pattern::Builtins::XOR, {{Pattern::Builtins::XOR, {X, Y}}, X}}, [=]{ return Y; }, true},
+ {{Pattern::Builtins::XOR, {{Pattern::Builtins::XOR, {Y, X}}, X}}, [=]{ return Y; }, true},
+ {{Pattern::Builtins::OR, {X, {Pattern::Builtins::AND, {X, Y}}}}, [=]{ return X; }, true},
+ {{Pattern::Builtins::OR, {X, {Pattern::Builtins::AND, {Y, X}}}}, [=]{ return X; }, true},
+ {{Pattern::Builtins::OR, {{Pattern::Builtins::AND, {X, Y}}, X}}, [=]{ return X; }, true},
+ {{Pattern::Builtins::OR, {{Pattern::Builtins::AND, {Y, X}}, X}}, [=]{ return X; }, true},
+ {{Pattern::Builtins::AND, {X, {Pattern::Builtins::OR, {X, Y}}}}, [=]{ return X; }, true},
+ {{Pattern::Builtins::AND, {X, {Pattern::Builtins::OR, {Y, X}}}}, [=]{ return X; }, true},
+ {{Pattern::Builtins::AND, {{Pattern::Builtins::OR, {X, Y}}, X}}, [=]{ return X; }, true},
+ {{Pattern::Builtins::AND, {{Pattern::Builtins::OR, {Y, X}}, X}}, [=]{ return X; }, true},
+ {{Pattern::Builtins::AND, {X, {Pattern::Builtins::NOT, {X}}}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::AND, {{Pattern::Builtins::NOT, {X}}, X}}, [=]{ return Word(0); }, true},
+ {{Pattern::Builtins::OR, {X, {Pattern::Builtins::NOT, {X}}}}, [=]{ return ~Word(0); }, true},
+ {{Pattern::Builtins::OR, {{Pattern::Builtins::NOT, {X}}, X}}, [=]{ return ~Word(0); }, true},
};
}
@@ -230,58 +239,61 @@ std::vector> simplificationRuleListPart5(
Pattern
)
{
+ using Word = typename Pattern::Word;
+
std::vector> rules;
// Replace MOD X, with AND X, - 1
- for (size_t i = 0; i < 256; ++i)
+ for (size_t i = 0; i < Pattern::WordSize; ++i)
{
- u256 value = u256(1) << i;
+ Word value = Word(1) << i;
rules.push_back({
- {Instruction::MOD, {X, value}},
- [=]() -> Pattern { return {Instruction::AND, {X, value - 1}}; },
+ {Pattern::Builtins::MOD, {X, value}},
+ [=]() -> Pattern { return {Pattern::Builtins::AND, {X, value - 1}}; },
false
});
}
// Replace SHL >=256, X with 0
rules.push_back({
- {Instruction::SHL, {A, X}},
- [=]() -> Pattern { return u256(0); },
+ {Pattern::Builtins::SHL, {A, X}},
+ [=]() -> Pattern { return Word(0); },
true,
- [=]() { return A.d() >= 256; }
+ [=]() { return A.d() >= Pattern::WordSize; }
});
// Replace SHR >=256, X with 0
rules.push_back({
- {Instruction::SHR, {A, X}},
- [=]() -> Pattern { return u256(0); },
+ {Pattern::Builtins::SHR, {A, X}},
+ [=]() -> Pattern { return Word(0); },
true,
- [=]() { return A.d() >= 256; }
+ [=]() { return A.d() >= Pattern::WordSize; }
});
// Replace BYTE(A, X), A >= 32 with 0
rules.push_back({
- {Instruction::BYTE, {A, X}},
- [=]() -> Pattern { return u256(0); },
+ {Pattern::Builtins::BYTE, {A, X}},
+ [=]() -> Pattern { return Word(0); },
true,
- [=]() { return A.d() >= 32; }
+ [=]() { return A.d() >= Pattern::WordSize / 8; }
});
for (auto const& op: std::vector{
- Instruction::ADDRESS,
- Instruction::CALLER,
- Instruction::ORIGIN,
- Instruction::COINBASE
+ Pattern::Builtins::ADDRESS,
+ Pattern::Builtins::CALLER,
+ Pattern::Builtins::ORIGIN,
+ Pattern::Builtins::COINBASE
})
{
- u256 const mask = (u256(1) << 160) - 1;
+ assertThrow(Pattern::WordSize > 160, OptimizerException, "");
+ Word const mask = (Word(1) << 160) - 1;
rules.push_back({
- {Instruction::AND, {{op, mask}}},
+ {Pattern::Builtins::AND, {{op, mask}}},
[=]() -> Pattern { return op; },
false
});
rules.push_back({
- {Instruction::AND, {{mask, op}}},
+ {Pattern::Builtins::AND, {{mask, op}}},
[=]() -> Pattern { return op; },
false
});
@@ -302,27 +314,27 @@ std::vector> simplificationRuleListPart6(
std::vector> rules;
// Double negation of opcodes with boolean result
for (auto const& op: std::vector{
- Instruction::EQ,
- Instruction::LT,
- Instruction::SLT,
- Instruction::GT,
- Instruction::SGT
+ Pattern::Builtins::EQ,
+ Pattern::Builtins::LT,
+ Pattern::Builtins::SLT,
+ Pattern::Builtins::GT,
+ Pattern::Builtins::SGT
})
rules.push_back({
- {Instruction::ISZERO, {{Instruction::ISZERO, {{op, {X, Y}}}}}},
+ {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {{op, {X, Y}}}}}},
[=]() -> Pattern { return {op, {X, Y}}; },
false
});
rules.push_back({
- {Instruction::ISZERO, {{Instruction::ISZERO, {{Instruction::ISZERO, {X}}}}}},
- [=]() -> Pattern { return {Instruction::ISZERO, {X}}; },
+ {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}}},
+ [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; },
false
});
rules.push_back({
- {Instruction::ISZERO, {{Instruction::XOR, {X, Y}}}},
- [=]() -> Pattern { return { Instruction::EQ, {X, Y} }; },
+ {Pattern::Builtins::ISZERO, {{Pattern::Builtins::XOR, {X, Y}}}},
+ [=]() -> Pattern { return { Pattern::Builtins::EQ, {X, Y} }; },
false
});
@@ -338,14 +350,15 @@ std::vector> simplificationRuleListPart7(
Pattern Y
)
{
+ using Word = typename Pattern::Word;
std::vector> rules;
// Associative operations
- for (auto const& opFun: std::vector>>{
- {Instruction::ADD, std::plus()},
- {Instruction::MUL, std::multiplies()},
- {Instruction::AND, std::bit_and()},
- {Instruction::OR, std::bit_or()},
- {Instruction::XOR, std::bit_xor()}
+ for (auto const& opFun: std::vector>>{
+ {Pattern::Builtins::ADD, std::plus()},
+ {Pattern::Builtins::MUL, std::multiplies()},
+ {Pattern::Builtins::AND, std::bit_and()},
+ {Pattern::Builtins::OR, std::bit_or()},
+ {Pattern::Builtins::XOR, std::bit_xor()}
})
{
auto op = opFun.first;
@@ -382,13 +395,13 @@ std::vector> simplificationRuleListPart7(
// Combine two SHL by constant
rules.push_back({
// SHL(B, SHL(A, X)) -> SHL(min(A+B, 256), X)
- {Instruction::SHL, {{B}, {Instruction::SHL, {{A}, {X}}}}},
+ {Pattern::Builtins::SHL, {{B}, {Pattern::Builtins::SHL, {{A}, {X}}}}},
[=]() -> Pattern {
bigint sum = bigint(A.d()) + B.d();
- if (sum >= 256)
- return {Instruction::AND, {X, u256(0)}};
+ if (sum >= Pattern::WordSize)
+ return {Pattern::Builtins::AND, {X, Word(0)}};
else
- return {Instruction::SHL, {u256(sum), X}};
+ return {Pattern::Builtins::SHL, {Word(sum), X}};
},
false
});
@@ -396,13 +409,13 @@ std::vector> simplificationRuleListPart7(
// Combine two SHR by constant
rules.push_back({
// SHR(B, SHR(A, X)) -> SHR(min(A+B, 256), X)
- {Instruction::SHR, {{B}, {Instruction::SHR, {{A}, {X}}}}},
+ {Pattern::Builtins::SHR, {{B}, {Pattern::Builtins::SHR, {{A}, {X}}}}},
[=]() -> Pattern {
bigint sum = bigint(A.d()) + B.d();
- if (sum >= 256)
- return {Instruction::AND, {X, u256(0)}};
+ if (sum >= Pattern::WordSize)
+ return {Pattern::Builtins::AND, {X, Word(0)}};
else
- return {Instruction::SHR, {u256(sum), X}};
+ return {Pattern::Builtins::SHR, {Word(sum), X}};
},
false
});
@@ -410,112 +423,112 @@ std::vector> simplificationRuleListPart7(
// Combine SHL-SHR by constant
rules.push_back({
// SHR(B, SHL(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask)
- {Instruction::SHR, {{B}, {Instruction::SHL, {{A}, {X}}}}},
+ {Pattern::Builtins::SHR, {{B}, {Pattern::Builtins::SHL, {{A}, {X}}}}},
[=]() -> Pattern {
- u256 mask = shlWorkaround(u256(-1), unsigned(A.d())) >> unsigned(B.d());
+ Word mask = shlWorkaround(~Word(0), unsigned(A.d())) >> unsigned(B.d());
if (A.d() > B.d())
- return {Instruction::AND, {{Instruction::SHL, {A.d() - B.d(), X}}, mask}};
+ return {Pattern::Builtins::AND, {{Pattern::Builtins::SHL, {A.d() - B.d(), X}}, mask}};
else if (B.d() > A.d())
- return {Instruction::AND, {{Instruction::SHR, {B.d() - A.d(), X}}, mask}};
+ return {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {B.d() - A.d(), X}}, mask}};
else
- return {Instruction::AND, {X, mask}};
+ return {Pattern::Builtins::AND, {X, mask}};
},
false,
- [=] { return A.d() < 256 && B.d() < 256; }
+ [=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; }
});
// Combine SHR-SHL by constant
rules.push_back({
// SHL(B, SHR(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask)
- {Instruction::SHL, {{B}, {Instruction::SHR, {{A}, {X}}}}},
+ {Pattern::Builtins::SHL, {{B}, {Pattern::Builtins::SHR, {{A}, {X}}}}},
[=]() -> Pattern {
- u256 mask = shlWorkaround(u256(-1) >> unsigned(A.d()), unsigned(B.d()));
+ Word mask = shlWorkaround((~Word(0)) >> unsigned(A.d()), unsigned(B.d()));
if (A.d() > B.d())
- return {Instruction::AND, {{Instruction::SHR, {A.d() - B.d(), X}}, mask}};
+ return {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {A.d() - B.d(), X}}, mask}};
else if (B.d() > A.d())
- return {Instruction::AND, {{Instruction::SHL, {B.d() - A.d(), X}}, mask}};
+ return {Pattern::Builtins::AND, {{Pattern::Builtins::SHL, {B.d() - A.d(), X}}, mask}};
else
- return {Instruction::AND, {X, mask}};
+ return {Pattern::Builtins::AND, {X, mask}};
},
false,
- [=] { return A.d() < 256 && B.d() < 256; }
+ [=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; }
});
// Move AND with constant across SHL and SHR by constant
- for (auto shiftOp: {Instruction::SHL, Instruction::SHR})
+ for (auto shiftOp: {Pattern::Builtins::SHL, Pattern::Builtins::SHR})
{
auto replacement = [=]() -> Pattern {
- u256 mask =
- shiftOp == Instruction::SHL ?
+ Word mask =
+ shiftOp == Pattern::Builtins::SHL ?
shlWorkaround(A.d(), unsigned(B.d())) :
A.d() >> unsigned(B.d());
- return {Instruction::AND, {{shiftOp, {B.d(), X}}, std::move(mask)}};
+ return {Pattern::Builtins::AND, {{shiftOp, {B.d(), X}}, std::move(mask)}};
};
rules.push_back({
// SH[L/R](B, AND(X, A)) -> AND(SH[L/R](B, X), [ A << B / A >> B ])
- {shiftOp, {{B}, {Instruction::AND, {{X}, {A}}}}},
+ {shiftOp, {{B}, {Pattern::Builtins::AND, {{X}, {A}}}}},
replacement,
false,
- [=] { return B.d() < 256; }
+ [=] { return B.d() < Pattern::WordSize; }
});
rules.push_back({
// SH[L/R](B, AND(A, X)) -> AND(SH[L/R](B, X), [ A << B / A >> B ])
- {shiftOp, {{B}, {Instruction::AND, {{A}, {X}}}}},
+ {shiftOp, {{B}, {Pattern::Builtins::AND, {{A}, {X}}}}},
replacement,
false,
- [=] { return B.d() < 256; }
+ [=] { return B.d() < Pattern::WordSize; }
});
}
rules.push_back({
// MUL(X, SHL(Y, 1)) -> SHL(Y, X)
- {Instruction::MUL, {X, {Instruction::SHL, {Y, u256(1)}}}},
+ {Pattern::Builtins::MUL, {X, {Pattern::Builtins::SHL, {Y, Word(1)}}}},
[=]() -> Pattern {
- return {Instruction::SHL, {Y, X}};
+ return {Pattern::Builtins::SHL, {Y, X}};
},
// Actually only changes the order, does not remove.
true
});
rules.push_back({
// MUL(SHL(X, 1), Y) -> SHL(X, Y)
- {Instruction::MUL, {{Instruction::SHL, {X, u256(1)}}, Y}},
+ {Pattern::Builtins::MUL, {{Pattern::Builtins::SHL, {X, Word(1)}}, Y}},
[=]() -> Pattern {
- return {Instruction::SHL, {X, Y}};
+ return {Pattern::Builtins::SHL, {X, Y}};
},
false
});
rules.push_back({
// DIV(X, SHL(Y, 1)) -> SHR(Y, X)
- {Instruction::DIV, {X, {Instruction::SHL, {Y, u256(1)}}}},
+ {Pattern::Builtins::DIV, {X, {Pattern::Builtins::SHL, {Y, Word(1)}}}},
[=]() -> Pattern {
- return {Instruction::SHR, {Y, X}};
+ return {Pattern::Builtins::SHR, {Y, X}};
},
// Actually only changes the order, does not remove.
true
});
std::function feasibilityFunction = [=]() {
- if (B.d() > 256)
+ if (B.d() > Pattern::WordSize)
return false;
unsigned bAsUint = static_cast(B.d());
- return (A.d() & (u256(-1) >> bAsUint)) == (u256(-1) >> bAsUint);
+ return (A.d() & ((~Word(0)) >> bAsUint)) == ((~Word(0)) >> bAsUint);
};
rules.push_back({
// AND(A, SHR(B, X)) -> A & ((2^256-1) >> B) == ((2^256-1) >> B)
- {Instruction::AND, {A, {Instruction::SHR, {B, X}}}},
- [=]() -> Pattern { return {Instruction::SHR, {B, X}}; },
+ {Pattern::Builtins::AND, {A, {Pattern::Builtins::SHR, {B, X}}}},
+ [=]() -> Pattern { return {Pattern::Builtins::SHR, {B, X}}; },
false,
feasibilityFunction
});
rules.push_back({
// AND(SHR(B, X), A) -> ((2^256-1) >> B) & A == ((2^256-1) >> B)
- {Instruction::AND, {{Instruction::SHR, {B, X}}, A}},
- [=]() -> Pattern { return {Instruction::SHR, {B, X}}; },
+ {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {B, X}}, A}},
+ [=]() -> Pattern { return {Pattern::Builtins::SHR, {B, X}}; },
false,
feasibilityFunction
});
@@ -538,28 +551,28 @@ std::vector> simplificationRuleListPart8(
rules += std::vector>{
{
// X - A -> X + (-A)
- {Instruction::SUB, {X, A}},
- [=]() -> Pattern { return {Instruction::ADD, {X, 0 - A.d()}}; },
+ {Pattern::Builtins::SUB, {X, A}},
+ [=]() -> Pattern { return {Pattern::Builtins::ADD, {X, 0 - A.d()}}; },
false
}, {
// (X + A) - Y -> (X - Y) + A
- {Instruction::SUB, {{Instruction::ADD, {X, A}}, Y}},
- [=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; },
+ {Pattern::Builtins::SUB, {{Pattern::Builtins::ADD, {X, A}}, Y}},
+ [=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, A}}; },
false
}, {
// (A + X) - Y -> (X - Y) + A
- {Instruction::SUB, {{Instruction::ADD, {A, X}}, Y}},
- [=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; },
+ {Pattern::Builtins::SUB, {{Pattern::Builtins::ADD, {A, X}}, Y}},
+ [=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, A}}; },
false
}, {
// X - (Y + A) -> (X - Y) + (-A)
- {Instruction::SUB, {X, {Instruction::ADD, {Y, A}}}},
- [=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; },
+ {Pattern::Builtins::SUB, {X, {Pattern::Builtins::ADD, {Y, A}}}},
+ [=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, 0 - A.d()}}; },
false
}, {
// X - (A + Y) -> (X - Y) + (-A)
- {Instruction::SUB, {X, {Instruction::ADD, {A, Y}}}},
- [=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; },
+ {Pattern::Builtins::SUB, {X, {Pattern::Builtins::ADD, {A, Y}}}},
+ [=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, 0 - A.d()}}; },
false
}
};
@@ -577,29 +590,31 @@ std::vector> simplificationRuleListPart9(
Pattern Z
)
{
+ using Word = typename Pattern::Word;
std::vector> rules;
- u256 const mask = (u256(1) << 160) - 1;
+ assertThrow(Pattern::WordSize > 160, OptimizerException, "");
+ Word const mask = (Word(1) << 160) - 1;
// CREATE
rules.push_back({
- {Instruction::AND, {{Instruction::CREATE, {W, X, Y}}, mask}},
- [=]() -> Pattern { return {Instruction::CREATE, {W, X, Y}}; },
+ {Pattern::Builtins::AND, {{Pattern::Builtins::CREATE, {W, X, Y}}, mask}},
+ [=]() -> Pattern { return {Pattern::Builtins::CREATE, {W, X, Y}}; },
false
});
rules.push_back({
- {Instruction::AND, {{mask, {Instruction::CREATE, {W, X, Y}}}}},
- [=]() -> Pattern { return {Instruction::CREATE, {W, X, Y}}; },
+ {Pattern::Builtins::AND, {{mask, {Pattern::Builtins::CREATE, {W, X, Y}}}}},
+ [=]() -> Pattern { return {Pattern::Builtins::CREATE, {W, X, Y}}; },
false
});
// CREATE2
rules.push_back({
- {Instruction::AND, {{Instruction::CREATE2, {W, X, Y, Z}}, mask}},
- [=]() -> Pattern { return {Instruction::CREATE2, {W, X, Y, Z}}; },
+ {Pattern::Builtins::AND, {{Pattern::Builtins::CREATE2, {W, X, Y, Z}}, mask}},
+ [=]() -> Pattern { return {Pattern::Builtins::CREATE2, {W, X, Y, Z}}; },
false
});
rules.push_back({
- {Instruction::AND, {{mask, {Instruction::CREATE2, {W, X, Y, Z}}}}},
- [=]() -> Pattern { return {Instruction::CREATE2, {W, X, Y, Z}}; },
+ {Pattern::Builtins::AND, {{mask, {Pattern::Builtins::CREATE2, {W, X, Y, Z}}}}},
+ [=]() -> Pattern { return {Pattern::Builtins::CREATE2, {W, X, Y, Z}}; },
false
});
@@ -621,6 +636,14 @@ std::vector> simplificationRuleList(
Pattern Z
)
{
+ using Word = typename Pattern::Word;
+ // Some sanity checks
+ assertThrow(Pattern::WordSize % 8 == 0, OptimizerException, "");
+ assertThrow(Pattern::WordSize >= 8, OptimizerException, "");
+ assertThrow(Pattern::WordSize <= 256, OptimizerException, "");
+ assertThrow(Word(-1) == ~Word(0), OptimizerException, "");
+ assertThrow(Word(-1) + 1 == Word(0), OptimizerException, "");
+
std::vector> rules;
rules += simplificationRuleListPart1(A, B, C, W, X);
rules += simplificationRuleListPart2(A, B, C, W, X);
diff --git a/libevmasm/SimplificationRule.h b/libevmasm/SimplificationRule.h
index 5c752545f..b35b89551 100644
--- a/libevmasm/SimplificationRule.h
+++ b/libevmasm/SimplificationRule.h
@@ -20,6 +20,7 @@
#pragma once
+#include
#include
namespace dev
@@ -54,5 +55,85 @@ struct SimplificationRule
std::function feasible;
};
+struct EVMBuiltins
+{
+ using InstrType = Instruction;
+ static auto constexpr STOP = Instruction::STOP;
+ static auto constexpr ADD = Instruction::ADD;
+ static auto constexpr SUB = Instruction::SUB;
+ static auto constexpr MUL = Instruction::MUL;
+ static auto constexpr DIV = Instruction::DIV;
+ static auto constexpr SDIV = Instruction::SDIV;
+ static auto constexpr MOD = Instruction::MOD;
+ static auto constexpr SMOD = Instruction::SMOD;
+ static auto constexpr EXP = Instruction::EXP;
+ static auto constexpr NOT = Instruction::NOT;
+ static auto constexpr LT = Instruction::LT;
+ static auto constexpr GT = Instruction::GT;
+ static auto constexpr SLT = Instruction::SLT;
+ static auto constexpr SGT = Instruction::SGT;
+ static auto constexpr EQ = Instruction::EQ;
+ static auto constexpr ISZERO = Instruction::ISZERO;
+ static auto constexpr AND = Instruction::AND;
+ static auto constexpr OR = Instruction::OR;
+ static auto constexpr XOR = Instruction::XOR;
+ static auto constexpr BYTE = Instruction::BYTE;
+ static auto constexpr SHL = Instruction::SHL;
+ static auto constexpr SHR = Instruction::SHR;
+ static auto constexpr SAR = Instruction::SAR;
+ static auto constexpr ADDMOD = Instruction::ADDMOD;
+ static auto constexpr MULMOD = Instruction::MULMOD;
+ static auto constexpr SIGNEXTEND = Instruction::SIGNEXTEND;
+ static auto constexpr KECCAK256 = Instruction::KECCAK256;
+ static auto constexpr ADDRESS = Instruction::ADDRESS;
+ static auto constexpr BALANCE = Instruction::BALANCE;
+ static auto constexpr ORIGIN = Instruction::ORIGIN;
+ static auto constexpr CALLER = Instruction::CALLER;
+ static auto constexpr CALLVALUE = Instruction::CALLVALUE;
+ static auto constexpr CALLDATALOAD = Instruction::CALLDATALOAD;
+ static auto constexpr CALLDATASIZE = Instruction::CALLDATASIZE;
+ static auto constexpr CALLDATACOPY = Instruction::CALLDATACOPY;
+ static auto constexpr CODESIZE = Instruction::CODESIZE;
+ static auto constexpr CODECOPY = Instruction::CODECOPY;
+ static auto constexpr GASPRICE = Instruction::GASPRICE;
+ static auto constexpr EXTCODESIZE = Instruction::EXTCODESIZE;
+ static auto constexpr EXTCODECOPY = Instruction::EXTCODECOPY;
+ static auto constexpr RETURNDATASIZE = Instruction::RETURNDATASIZE;
+ static auto constexpr RETURNDATACOPY = Instruction::RETURNDATACOPY;
+ static auto constexpr EXTCODEHASH = Instruction::EXTCODEHASH;
+ static auto constexpr BLOCKHASH = Instruction::BLOCKHASH;
+ static auto constexpr COINBASE = Instruction::COINBASE;
+ static auto constexpr TIMESTAMP = Instruction::TIMESTAMP;
+ static auto constexpr NUMBER = Instruction::NUMBER;
+ static auto constexpr DIFFICULTY = Instruction::DIFFICULTY;
+ static auto constexpr GASLIMIT = Instruction::GASLIMIT;
+ static auto constexpr CHAINID = Instruction::CHAINID;
+ static auto constexpr SELFBALANCE = Instruction::SELFBALANCE;
+ static auto constexpr POP = Instruction::POP;
+ static auto constexpr MLOAD = Instruction::MLOAD;
+ static auto constexpr MSTORE = Instruction::MSTORE;
+ static auto constexpr MSTORE8 = Instruction::MSTORE8;
+ static auto constexpr SLOAD = Instruction::SLOAD;
+ static auto constexpr SSTORE = Instruction::SSTORE;
+ static auto constexpr PC = Instruction::PC;
+ static auto constexpr MSIZE = Instruction::MSIZE;
+ static auto constexpr GAS = Instruction::GAS;
+ static auto constexpr LOG0 = Instruction::LOG0;
+ static auto constexpr LOG1 = Instruction::LOG1;
+ static auto constexpr LOG2 = Instruction::LOG2;
+ static auto constexpr LOG3 = Instruction::LOG3;
+ static auto constexpr LOG4 = Instruction::LOG4;
+ static auto constexpr CREATE = Instruction::CREATE;
+ static auto constexpr CALL = Instruction::CALL;
+ static auto constexpr CALLCODE = Instruction::CALLCODE;
+ static auto constexpr STATICCALL = Instruction::STATICCALL;
+ static auto constexpr RETURN = Instruction::RETURN;
+ static auto constexpr DELEGATECALL = Instruction::DELEGATECALL;
+ static auto constexpr CREATE2 = Instruction::CREATE2;
+ static auto constexpr REVERT = Instruction::REVERT;
+ static auto constexpr INVALID = Instruction::INVALID;
+ static auto constexpr SELFDESTRUCT = Instruction::SELFDESTRUCT;
+};
+
}
}
diff --git a/libevmasm/SimplificationRules.h b/libevmasm/SimplificationRules.h
index fc45a46c3..a8c782b26 100644
--- a/libevmasm/SimplificationRules.h
+++ b/libevmasm/SimplificationRules.h
@@ -87,6 +87,10 @@ public:
using Expression = ExpressionClasses::Expression;
using Id = ExpressionClasses::Id;
+ using Builtins = dev::eth::EVMBuiltins;
+ static constexpr size_t WordSize = 256;
+ using Word = u256;
+
// Matches a specific constant value.
Pattern(unsigned _value): Pattern(u256(_value)) {}
// Matches a specific constant value.
diff --git a/liblangutil/EVMVersion.h b/liblangutil/EVMVersion.h
index 3c67e304d..95a9eb089 100644
--- a/liblangutil/EVMVersion.h
+++ b/liblangutil/EVMVersion.h
@@ -22,9 +22,9 @@
#include
+#include
#include
-#include
#include
@@ -51,12 +51,12 @@ public:
static EVMVersion istanbul() { return {Version::Istanbul}; }
static EVMVersion berlin() { return {Version::Berlin}; }
- static boost::optional fromString(std::string const& _version)
+ static std::optional fromString(std::string const& _version)
{
for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium(), constantinople(), petersburg(), istanbul(), berlin()})
if (_version == v.name())
return v;
- return {};
+ return std::nullopt;
}
bool operator==(EVMVersion const& _other) const { return m_version == _other.m_version; }
diff --git a/liblangutil/Exceptions.cpp b/liblangutil/Exceptions.cpp
index 4e68dfa5b..4fd411b6e 100644
--- a/liblangutil/Exceptions.cpp
+++ b/liblangutil/Exceptions.cpp
@@ -29,7 +29,7 @@ using namespace langutil;
Error::Error(Type _type, SourceLocation const& _location, string const& _description):
m_type(_type)
{
- switch(m_type)
+ switch (m_type)
{
case Type::DeclarationError:
m_typeName = "DeclarationError";
diff --git a/liblangutil/Scanner.cpp b/liblangutil/Scanner.cpp
index 131462289..0d65334fb 100644
--- a/liblangutil/Scanner.cpp
+++ b/liblangutil/Scanner.cpp
@@ -53,8 +53,9 @@
#include
#include
#include
-#include
+
#include
+#include
#include
#include
@@ -187,7 +188,7 @@ bool Scanner::scanHexByte(char& o_scannedByte)
return true;
}
-boost::optional Scanner::scanUnicode()
+std::optional Scanner::scanUnicode()
{
unsigned x = 0;
for (int i = 0; i < 4; i++)
@@ -718,7 +719,7 @@ bool Scanner::scanEscape()
break;
case 'u':
{
- if (boost::optional codepoint = scanUnicode())
+ if (auto const codepoint = scanUnicode(); codepoint.has_value())
addUnicodeAsUTF8(*codepoint);
else
return false;
diff --git a/liblangutil/Scanner.h b/liblangutil/Scanner.h
index 3914b85ac..f5b535cce 100644
--- a/liblangutil/Scanner.h
+++ b/liblangutil/Scanner.h
@@ -57,6 +57,8 @@
#include
#include
#include
+
+#include
#include
namespace langutil
@@ -207,7 +209,7 @@ private:
inline Token selectToken(char _next, Token _then, Token _else);
bool scanHexByte(char& o_scannedByte);
- boost::optional scanUnicode();
+ std::optional scanUnicode();
/// Scans a single Solidity token.
void scanToken();
diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt
index 81e86e621..ded239559 100644
--- a/libsolidity/CMakeLists.txt
+++ b/libsolidity/CMakeLists.txt
@@ -79,6 +79,8 @@ set(sources
formal/BMC.h
formal/CHC.cpp
formal/CHC.h
+ formal/CHCSmtLib2Interface.cpp
+ formal/CHCSmtLib2Interface.h
formal/CHCSolverInterface.h
formal/EncodingContext.cpp
formal/EncodingContext.h
@@ -111,6 +113,8 @@ set(sources
interface/ReadFile.h
interface/StandardCompiler.cpp
interface/StandardCompiler.h
+ interface/StorageLayout.cpp
+ interface/StorageLayout.h
interface/Version.cpp
interface/Version.h
parsing/DocStringParser.cpp
diff --git a/libsolidity/analysis/ControlFlowBuilder.cpp b/libsolidity/analysis/ControlFlowBuilder.cpp
index fb766eea9..6bafc4caa 100644
--- a/libsolidity/analysis/ControlFlowBuilder.cpp
+++ b/libsolidity/analysis/ControlFlowBuilder.cpp
@@ -49,7 +49,7 @@ bool ControlFlowBuilder::visit(BinaryOperation const& _operation)
{
solAssert(!!m_currentNode, "");
- switch(_operation.getOperator())
+ switch (_operation.getOperator())
{
case Token::Or:
case Token::And:
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp
index e9792ddf2..cc28220ca 100644
--- a/libsolidity/analysis/NameAndTypeResolver.cpp
+++ b/libsolidity/analysis/NameAndTypeResolver.cpp
@@ -38,10 +38,12 @@ namespace solidity
NameAndTypeResolver::NameAndTypeResolver(
GlobalContext& _globalContext,
+ langutil::EVMVersion _evmVersion,
map>& _scopes,
ErrorReporter& _errorReporter
-) :
+):
m_scopes(_scopes),
+ m_evmVersion(_evmVersion),
m_errorReporter(_errorReporter),
m_globalContext(_globalContext)
{
@@ -324,7 +326,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
{
if (m_scopes.count(&_node))
setScope(&_node);
- return ReferencesResolver(m_errorReporter, *this, _resolveInsideCode).resolve(_node);
+ return ReferencesResolver(m_errorReporter, *this, m_evmVersion, _resolveInsideCode).resolve(_node);
}
}
diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h
index e5f5774ab..0c4452cde 100644
--- a/libsolidity/analysis/NameAndTypeResolver.h
+++ b/libsolidity/analysis/NameAndTypeResolver.h
@@ -28,6 +28,8 @@
#include
#include
+#include
+
#include
#include
@@ -55,6 +57,7 @@ public:
/// are filled during the lifetime of this object.
NameAndTypeResolver(
GlobalContext& _globalContext,
+ langutil::EVMVersion _evmVersion,
std::map>& _scopes,
langutil::ErrorReporter& _errorReporter
);
@@ -130,6 +133,7 @@ private:
/// Aliases (for example `import "x" as y;`) create multiple pointers to the same scope.
std::map>& m_scopes;
+ langutil::EVMVersion m_evmVersion;
DeclarationContainer* m_currentScope = nullptr;
langutil::ErrorReporter& m_errorReporter;
GlobalContext& m_globalContext;
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index a1df47abe..d8627d49d 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -122,11 +122,11 @@ bool ReferencesResolver::visit(ElementaryTypeName const& _typeName)
if (!_typeName.annotation().type)
{
_typeName.annotation().type = TypeProvider::fromElementaryTypeName(_typeName.typeName());
- if (_typeName.stateMutability().is_initialized())
+ if (_typeName.stateMutability().has_value())
{
// for non-address types this was already caught by the parser
solAssert(_typeName.annotation().type->category() == Type::Category::Address, "");
- switch(*_typeName.stateMutability())
+ switch (*_typeName.stateMutability())
{
case StateMutability::Payable:
_typeName.annotation().type = TypeProvider::payableAddress();
@@ -323,12 +323,12 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
// Will be re-generated later with correct information
// We use the latest EVM version because we will re-run it anyway.
yul::AsmAnalysisInfo analysisInfo;
- boost::optional errorTypeForLoose = Error::Type::SyntaxError;
+ std::optional errorTypeForLoose = Error::Type::SyntaxError;
yul::AsmAnalyzer(
analysisInfo,
errorsIgnored,
errorTypeForLoose,
- yul::EVMDialect::looseAssemblyForEVM(EVMVersion{}),
+ yul::EVMDialect::looseAssemblyForEVM(m_evmVersion),
resolver
).analyze(_inlineAssembly.operations());
return false;
diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h
index 5631fb801..207eecdda 100644
--- a/libsolidity/analysis/ReferencesResolver.h
+++ b/libsolidity/analysis/ReferencesResolver.h
@@ -24,6 +24,7 @@
#include
#include
+#include
#include
#include
@@ -52,10 +53,12 @@ public:
ReferencesResolver(
langutil::ErrorReporter& _errorReporter,
NameAndTypeResolver& _resolver,
+ langutil::EVMVersion _evmVersion,
bool _resolveInsideCode = false
):
m_errorReporter(_errorReporter),
m_resolver(_resolver),
+ m_evmVersion(_evmVersion),
m_resolveInsideCode(_resolveInsideCode)
{}
@@ -96,6 +99,7 @@ private:
langutil::ErrorReporter& m_errorReporter;
NameAndTypeResolver& m_resolver;
+ langutil::EVMVersion m_evmVersion;
/// Stack of return parameters.
std::vector m_returnParameters;
bool const m_resolveInsideCode;
diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp
index 2a0aaddcb..0d2a978db 100644
--- a/libsolidity/analysis/StaticAnalyzer.cpp
+++ b/libsolidity/analysis/StaticAnalyzer.cpp
@@ -227,7 +227,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
if (m_constructor)
{
auto const* expr = &_memberAccess.expression();
- while(expr)
+ while (expr)
{
if (auto id = dynamic_cast(expr))
{
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index b5abe59f4..262db9d1d 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -390,11 +390,13 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
set modifiers;
for (ASTPointer const& modifier: _function.modifiers())
{
+ auto baseContracts = dynamic_cast(*_function.scope()).annotation().linearizedBaseContracts;
+ // Delete first base which is just the main contract itself
+ baseContracts.erase(baseContracts.begin());
+
visitManually(
*modifier,
- _function.isConstructor() ?
- dynamic_cast(*_function.scope()).annotation().linearizedBaseContracts :
- vector()
+ _function.isConstructor() ? baseContracts : vector()
);
Declaration const* decl = &dereference(*modifier->name());
if (modifiers.count(decl))
@@ -448,6 +450,9 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
TypePointer varType = _variable.annotation().type;
solAssert(!!varType, "Variable type not provided.");
+ if (auto contractType = dynamic_cast(varType))
+ if (contractType->contractDefinition().isLibrary())
+ m_errorReporter.typeError(_variable.location(), "The type of a variable cannot be a library.");
if (_variable.value())
expectType(*_variable.value(), *varType);
if (_variable.isConstant())
@@ -522,7 +527,12 @@ void TypeChecker::visitManually(
_modifier.arguments() ? *_modifier.arguments() : std::vector>();
for (ASTPointer const& argument: arguments)
argument->accept(*this);
- _modifier.name()->accept(*this);
+
+ {
+ m_insideModifierInvocation = true;
+ ScopeGuard resetFlag{[&] () { m_insideModifierInvocation = false; }};
+ _modifier.name()->accept(*this);
+ }
auto const* declaration = &dereference(*_modifier.name());
vector> emptyParameterList;
@@ -695,6 +705,9 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
}
else if (_context == yul::IdentifierContext::LValue)
{
+ if (dynamic_cast(declaration))
+ return size_t(-1);
+
m_errorReporter.typeError(_identifier.location, "Only local variables can be assigned to in inline assembly.");
return size_t(-1);
}
@@ -2342,12 +2355,30 @@ bool TypeChecker::visit(Identifier const& _identifier)
if (functionType->canTakeArguments(*annotation.arguments))
candidates.push_back(declaration);
}
- if (candidates.empty())
- m_errorReporter.fatalTypeError(_identifier.location(), "No matching declaration found after argument-dependent lookup.");
- else if (candidates.size() == 1)
+ if (candidates.size() == 1)
annotation.referencedDeclaration = candidates.front();
else
- m_errorReporter.fatalTypeError(_identifier.location(), "No unique declaration found after argument-dependent lookup.");
+ {
+ SecondarySourceLocation ssl;
+
+ for (Declaration const* declaration: annotation.overloadedDeclarations)
+ if (declaration->location().isEmpty())
+ {
+ // Try to re-construct function definition
+ string description;
+ for (auto const& param: declaration->functionType(true)->parameterTypes())
+ description += (description.empty() ? "" : ", ") + param->toString(false);
+ description = "function " + _identifier.name() + "(" + description + ")";
+
+ ssl.append("Candidate: " + description, declaration->location());
+ }
+ else
+ ssl.append("Candidate:", declaration->location());
+ if (candidates.empty())
+ m_errorReporter.fatalTypeError(_identifier.location(), ssl, "No matching declaration found after argument-dependent lookup.");
+ else
+ m_errorReporter.fatalTypeError(_identifier.location(), ssl, "No unique declaration found after argument-dependent lookup.");
+ }
}
}
solAssert(
@@ -2376,15 +2407,24 @@ bool TypeChecker::visit(Identifier const& _identifier)
if (_identifier.name() == "sha3" && fType->kind() == FunctionType::Kind::KECCAK256)
m_errorReporter.typeError(
_identifier.location(),
- "\"sha3\" has been deprecated in favour of \"keccak256\""
+ "\"sha3\" has been deprecated in favour of \"keccak256\"."
);
else if (_identifier.name() == "suicide" && fType->kind() == FunctionType::Kind::Selfdestruct)
m_errorReporter.typeError(
_identifier.location(),
- "\"suicide\" has been deprecated in favour of \"selfdestruct\""
+ "\"suicide\" has been deprecated in favour of \"selfdestruct\"."
);
}
+ if (!m_insideModifierInvocation)
+ if (ModifierType const* type = dynamic_cast(_identifier.annotation().type))
+ {
+ m_errorReporter.typeError(
+ _identifier.location(),
+ "Modifier can only be referenced in function headers."
+ );
+ }
+
return false;
}
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index 8f532a89c..003915b2e 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -166,6 +166,9 @@ private:
/// Flag indicating whether we are currently inside a StructDefinition.
bool m_insideStruct = false;
+ /// Flag indicating whether we are currently inside the invocation of a modifier
+ bool m_insideModifierInvocation = false;
+
langutil::ErrorReporter& m_errorReporter;
};
diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp
index cf35a4889..58baec906 100644
--- a/libsolidity/analysis/ViewPureChecker.cpp
+++ b/libsolidity/analysis/ViewPureChecker.cpp
@@ -241,7 +241,7 @@ void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly)
void ViewPureChecker::reportMutability(
StateMutability _mutability,
SourceLocation const& _location,
- boost::optional const& _nestedLocation
+ std::optional const& _nestedLocation
)
{
if (_mutability > m_bestMutabilityAndLocation.mutability)
diff --git a/libsolidity/analysis/ViewPureChecker.h b/libsolidity/analysis/ViewPureChecker.h
index fd2432a7d..edcabf2f0 100644
--- a/libsolidity/analysis/ViewPureChecker.h
+++ b/libsolidity/analysis/ViewPureChecker.h
@@ -23,6 +23,7 @@
#include