Merge remote-tracking branch 'origin/develop' into breaking

This commit is contained in:
chriseth 2020-05-12 17:47:41 +02:00
commit 0be56a0abf
342 changed files with 4644 additions and 1159 deletions

View File

@ -7,15 +7,15 @@ The docker images are build locally on the developer machine:
```sh
cd .circleci/docker/
docker build -t ethereum/solidity-buildpack-deps:ubuntu1904-<revision> -f Dockerfile.ubuntu1904 .
docker push ethereum/solidity-buildpack-deps:ubuntu1904-<revision>
docker build -t ethereum/solidity-buildpack-deps:ubuntu2004-<revision> -f Dockerfile.ubuntu2004 .
docker push ethereum/solidity-buildpack-deps:ubuntu2004-<revision>
```
The current revisions per docker image are stored in [circle ci pipeline parameters](https://github.com/CircleCI-Public/api-preview-docs/blob/master/docs/pipeline-parameters.md#pipeline-parameters) called `<image-desc>-docker-image-rev` (e.g., `ubuntu-1904-docker-image-rev`). Please update the value assigned to the parameter(s) corresponding to the docker image(s) being updated at the time of the update. Please verify that the value assigned to the parameter matches the revision part of the docker image tag (`<revision>` in the docker build/push snippet shown above). Otherwise, the docker image used by circle ci and the one actually pushed to docker hub will differ.
The current revisions per docker image are stored in [circle ci pipeline parameters](https://github.com/CircleCI-Public/api-preview-docs/blob/master/docs/pipeline-parameters.md#pipeline-parameters) called `<image-desc>-docker-image-rev` (e.g., `ubuntu-2004-docker-image-rev`). Please update the value assigned to the parameter(s) corresponding to the docker image(s) being updated at the time of the update. Please verify that the value assigned to the parameter matches the revision part of the docker image tag (`<revision>` in the docker build/push snippet shown above). Otherwise, the docker image used by circle ci and the one actually pushed to docker hub will differ.
Once the docker image has been built and pushed to Dockerhub, you can find it at:
https://hub.docker.com/r/ethereum/solidity-buildpack-deps:ubuntu1904-<revision>
https://hub.docker.com/r/ethereum/solidity-buildpack-deps:ubuntu2004-<revision>
where the image tag reflects the target OS and revision to build Solidity and run its tests on.
@ -24,7 +24,7 @@ where the image tag reflects the target OS and revision to build Solidity and ru
```sh
cd solidity
# Mounts your local solidity directory in docker container for testing
docker run -v `pwd`:/src/solidity -ti ethereum/solidity-buildpack-deps:ubuntu1904-<revision> /bin/bash
docker run -v `pwd`:/src/solidity -ti ethereum/solidity-buildpack-deps:ubuntu2004-<revision> /bin/bash
cd /src/solidity
<commands_to_test_build_with_new_docker_image>
```

View File

@ -10,12 +10,12 @@ parameters:
ubuntu-1804-docker-image-rev:
type: string
default: "4"
ubuntu-1904-docker-image-rev:
ubuntu-2004-docker-image-rev:
type: string
default: "4"
ubuntu-1904-clang-docker-image-rev:
default: "1"
ubuntu-2004-clang-docker-image-rev:
type: string
default: "5"
default: "1"
ubuntu-1604-clang-ossfuzz-docker-image-rev:
type: string
default: "2"
@ -50,6 +50,7 @@ defaults:
cd build
protoc --proto_path=../test/tools/ossfuzz yulProto.proto --cpp_out=../test/tools/ossfuzz
protoc --proto_path=../test/tools/ossfuzz abiV2Proto.proto --cpp_out=../test/tools/ossfuzz
protoc --proto_path=../test/tools/ossfuzz solProto.proto --cpp_out=../test/tools/ossfuzz
cmake .. -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-Release} $CMAKE_OPTIONS
make ossfuzz ossfuzz_proto ossfuzz_abiv2 -j4
@ -97,6 +98,7 @@ defaults:
- test/tools/ossfuzz/strictasm_opt_ossfuzz
- test/tools/ossfuzz/yul_proto_diff_ossfuzz
- test/tools/ossfuzz/yul_proto_ossfuzz
- test/tools/ossfuzz/sol_proto_ossfuzz
# test result output directory
- artifacts_test_results: &artifacts_test_results
@ -137,9 +139,9 @@ defaults:
- store_test_results: *store_test_results
- store_artifacts: *artifacts_test_results
- test_ubuntu1904_clang: &test_ubuntu1904_clang
- test_ubuntu2004_clang: &test_ubuntu2004_clang
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-<< pipeline.parameters.ubuntu-1904-clang-docker-image-rev >>
- image: ethereum/solidity-buildpack-deps:ubuntu2004-clang-<< pipeline.parameters.ubuntu-2004-clang-docker-image-rev >>
steps:
- checkout
- attach_workspace:
@ -148,9 +150,9 @@ defaults:
- store_test_results: *store_test_results
- store_artifacts: *artifacts_test_results
- test_ubuntu1904: &test_ubuntu1904
- test_ubuntu2004: &test_ubuntu2004
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >>
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
steps:
- checkout
- attach_workspace:
@ -160,7 +162,7 @@ defaults:
- store_artifacts: *artifacts_test_results
- test_asan: &test_asan
<<: *test_ubuntu1904
<<: *test_ubuntu2004
steps:
- checkout
- attach_workspace:
@ -179,7 +181,7 @@ defaults:
tags:
only: /.*/
- workflow_ubuntu1904: &workflow_ubuntu1904
- workflow_ubuntu2004: &workflow_ubuntu2004
<<: *workflow_trigger_on_tags
requires:
- b_ubu
@ -189,17 +191,17 @@ defaults:
requires:
- b_ubu_ossfuzz
- workflow_ubuntu1904_clang: &workflow_ubuntu1904_clang
- workflow_ubuntu2004_clang: &workflow_ubuntu2004_clang
<<: *workflow_trigger_on_tags
requires:
- b_ubu_clang
- workflow_ubuntu1904_release: &workflow_ubuntu1904_release
- workflow_ubuntu2004_release: &workflow_ubuntu2004_release
<<: *workflow_trigger_on_tags
requires:
- b_ubu_release
- workflow_ubuntu1904_codecov: &workflow_ubuntu1904_codecov
- workflow_ubuntu2004_codecov: &workflow_ubuntu2004_codecov
<<: *workflow_trigger_on_tags
requires:
- b_ubu_codecov
@ -209,7 +211,7 @@ defaults:
requires:
- b_osx
- workflow_ubuntu1904_asan: &workflow_ubuntu1904_asan
- workflow_ubuntu2004_asan: &workflow_ubuntu2004_asan
<<: *workflow_trigger_on_tags
requires:
- b_ubu_asan
@ -359,16 +361,16 @@ jobs:
chk_docs_pragma_min_version:
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >>
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
environment:
TERM: xterm
steps:
- checkout
- run: *run_docs_pragma_min_version
b_ubu_clang: &build_ubuntu1904_clang
b_ubu_clang: &build_ubuntu2004_clang
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-<< pipeline.parameters.ubuntu-1904-clang-docker-image-rev >>
- image: ethereum/solidity-buildpack-deps:ubuntu2004-clang-<< pipeline.parameters.ubuntu-2004-clang-docker-image-rev >>
environment:
CC: clang
CXX: clang++
@ -378,9 +380,9 @@ jobs:
- store_artifacts: *artifacts_solc
- persist_to_workspace: *artifacts_executables
b_ubu: &build_ubuntu1904
b_ubu: &build_ubuntu2004
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >>
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
steps:
- checkout
- run: *run_build
@ -388,8 +390,8 @@ jobs:
- store_artifacts: *artifacts_tools
- persist_to_workspace: *artifacts_executables
b_ubu_release: &build_ubuntu1904_release
<<: *build_ubuntu1904
b_ubu_release: &build_ubuntu2004_release
<<: *build_ubuntu2004
environment:
FORCE_RELEASE: ON
@ -406,7 +408,7 @@ jobs:
- persist_to_workspace: *artifacts_executables
b_ubu_codecov:
<<: *build_ubuntu1904
<<: *build_ubuntu2004
environment:
COVERAGE: ON
CMAKE_BUILD_TYPE: Debug
@ -416,7 +418,7 @@ jobs:
- persist_to_workspace: *artifacts_build_dir
t_ubu_codecov:
<<: *test_ubuntu1904
<<: *test_ubuntu2004
environment:
EVM: constantinople
OPTIMIZE: 1
@ -439,7 +441,7 @@ jobs:
# Builds in C++20 mode and uses debug build in order to speed up.
# Do *NOT* store any artifacts or workspace as we don't run tests on this build.
b_ubu_cxx20:
<<: *build_ubuntu1904
<<: *build_ubuntu2004
environment:
CMAKE_BUILD_TYPE: Debug
CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF
@ -591,7 +593,7 @@ jobs:
# x64 ASAN build, for testing for memory related bugs
b_ubu_asan: &b_ubu_asan
<<: *build_ubuntu1904
<<: *build_ubuntu2004
environment:
CMAKE_OPTIONS: -DSANITIZE=address
CMAKE_BUILD_TYPE: Release
@ -603,7 +605,7 @@ jobs:
b_docs:
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >>
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
steps:
- checkout
- run: *setup_prerelease_commit_hash
@ -615,10 +617,27 @@ jobs:
destination: docs-html
t_ubu_soltest: &t_ubu_soltest
<<: *test_ubuntu1904
<<: *test_ubuntu2004
t_ubu_soltest_enforce_yul: &t_ubu_soltest_enforce_yul
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
environment:
EVM: constantinople
SOLTEST_FLAGS: --enforce-via-yul
OPTIMIZE: 0
TERM: xterm
steps:
- checkout
- attach_workspace:
at: build
- run: *run_soltest
- store_test_results: *store_test_results
- store_artifacts: *artifacts_test_results
t_ubu_clang_soltest: &t_ubu_clang_soltest
<<: *test_ubuntu1904_clang
<<: *test_ubuntu2004_clang
environment:
EVM: constantinople
OPTIMIZE: 0
@ -628,7 +647,7 @@ jobs:
t_ubu_cli: &t_ubu_cli
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >>
- image: ethereum/solidity-buildpack-deps:ubuntu2004-<< pipeline.parameters.ubuntu-2004-docker-image-rev >>
environment:
TERM: xterm
steps:
@ -816,20 +835,21 @@ workflows:
# Ubuntu build and tests
- b_ubu: *workflow_trigger_on_tags
- b_ubu18: *workflow_trigger_on_tags
- t_ubu_cli: *workflow_ubuntu1904
- t_ubu_soltest: *workflow_ubuntu1904
- t_ubu_cli: *workflow_ubuntu2004
- t_ubu_soltest: *workflow_ubuntu2004
- t_ubu_soltest_enforce_yul: *workflow_ubuntu2004
- b_ubu_clang: *workflow_trigger_on_tags
- t_ubu_clang_soltest: *workflow_ubuntu1904_clang
- t_ubu_clang_soltest: *workflow_ubuntu2004_clang
# Ubuntu fake release build and tests
- b_ubu_release: *workflow_trigger_on_tags
- t_ubu_release_cli: *workflow_ubuntu1904_release
- t_ubu_release_soltest: *workflow_ubuntu1904_release
- t_ubu_release_cli: *workflow_ubuntu2004_release
- t_ubu_release_soltest: *workflow_ubuntu2004_release
# ASan build and tests
- b_ubu_asan: *workflow_trigger_on_tags
- t_ubu_asan_constantinople: *workflow_ubuntu1904_asan
- t_ubu_asan_cli: *workflow_ubuntu1904_asan
- t_ubu_asan_constantinople: *workflow_ubuntu2004_asan
- t_ubu_asan_cli: *workflow_ubuntu2004_asan
# Emscripten build and selected tests
- b_ems: *workflow_trigger_on_tags
@ -856,4 +876,4 @@ workflows:
# Code Coverage enabled build and tests
- b_ubu_codecov: *workflow_trigger_on_tags
- t_ubu_codecov: *workflow_ubuntu1904_codecov
- t_ubu_codecov: *workflow_ubuntu2004_codecov

View File

@ -1,113 +0,0 @@
# vim:syntax=dockerfile
#------------------------------------------------------------------------------
# Dockerfile for building and testing Solidity Compiler on CI
# Target: Ubuntu 19.04 (Disco Dingo) Clang variant
# URL: https://hub.docker.com/r/ethereum/solidity-buildpack-deps
#
# This file is part of solidity.
#
# solidity is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# solidity is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with solidity. If not, see <http://www.gnu.org/licenses/>
#
# (c) 2016-2019 solidity contributors.
#------------------------------------------------------------------------------
FROM buildpack-deps:disco AS base
ARG DEBIAN_FRONTEND=noninteractive
RUN set -ex; \
dist=$(grep DISTRIB_CODENAME /etc/lsb-release | cut -d= -f2); \
echo "deb http://ppa.launchpad.net/ethereum/cpp-build-deps/ubuntu $dist main" >> /etc/apt/sources.list ; \
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1c52189c923f6ca9 ; \
apt-get update; \
apt-get install -qqy --no-install-recommends \
build-essential \
software-properties-common \
cmake ninja-build \
clang++-8 llvm-8-dev \
libjsoncpp-dev \
libleveldb1d \
; \
apt-get install -qy python-pip python-sphinx; \
update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer /usr/bin/llvm-symbolizer-8 1; \
update-alternatives --install /usr/bin/clang clang /usr/bin/clang-8 1; \
update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-8 1; \
pip install codecov; \
rm -rf /var/lib/apt/lists/*
FROM base AS libraries
ENV CC clang
ENV CXX clang++
# Boost
RUN git clone -b boost-1.69.0 https://github.com/boostorg/boost.git \
/usr/src/boost; \
cd /usr/src/boost; \
git submodule update --init --recursive; \
./bootstrap.sh --with-toolset=clang --prefix=/usr; \
./b2 toolset=clang headers; \
./b2 toolset=clang variant=release \
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.7 https://github.com/Z3Prover/z3.git \
/usr/src/z3; \
cd /usr/src/z3; \
python scripts/mk_make.py --prefix=/usr ; \
cd build; \
make -j; \
make install; \
rm -rf /usr/src/z3;
# OSSFUZZ: libprotobuf-mutator
RUN set -ex; \
git clone https://github.com/google/libprotobuf-mutator.git \
/usr/src/libprotobuf-mutator; \
cd /usr/src/libprotobuf-mutator; \
git checkout 3521f47a2828da9ace403e4ecc4aece1a84feb36; \
mkdir build; \
cd build; \
cmake .. -GNinja -DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=ON \
-DLIB_PROTO_MUTATOR_TESTING=OFF -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="/usr"; \
ninja; \
cp -vpr external.protobuf/bin/* /usr/bin/; \
cp -vpr external.protobuf/include/* /usr/include/; \
cp -vpr external.protobuf/lib/* /usr/lib/; \
ninja install/strip; \
rm -rf /usr/src/libprotobuf-mutator
# EVMONE
RUN set -ex; \
cd /usr/src; \
git clone --branch="v0.4.0" --recurse-submodules https://github.com/ethereum/evmone.git; \
cd evmone; \
mkdir build; \
cd build; \
# isoltest links against the evmone shared library
cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \
ninja; \
ninja install/strip; \
# abiv2_proto_ossfuzz links against the evmone standalone static library
cmake -G Ninja -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX="/usr" ..; \
ninja; \
ninja install/strip; \
rm -rf /usr/src/evmone
FROM base
COPY --from=libraries /usr/lib /usr/lib
COPY --from=libraries /usr/bin /usr/bin
COPY --from=libraries /usr/include /usr/include

View File

@ -21,59 +21,26 @@
#
# (c) 2016-2019 solidity contributors.
#------------------------------------------------------------------------------
FROM buildpack-deps:disco AS base
FROM buildpack-deps:focal AS base
ARG DEBIAN_FRONTEND=noninteractive
RUN set -ex; \
dist=$(grep DISTRIB_CODENAME /etc/lsb-release | cut -d= -f2); \
echo "deb http://ppa.launchpad.net/ethereum/cpp-build-deps/ubuntu $dist main" >> /etc/apt/sources.list ; \
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1c52189c923f6ca9 ; \
apt-get update; \
apt-get install -qqy --no-install-recommends \
build-essential \
software-properties-common \
cmake ninja-build clang++-8 libc++-8-dev libc++abi-8-dev \
cmake ninja-build \
libboost-filesystem-dev libboost-test-dev libboost-system-dev \
libboost-program-options-dev \
libjsoncpp-dev \
llvm-8-dev libcvc4-dev libz3-static-dev libleveldb1d \
libcvc4-dev z3 libz3-dev \
; \
apt-get install -qy python-pip python-sphinx; \
update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer /usr/bin/llvm-symbolizer-8 1; \
pip install codecov; \
apt-get install -qy python3-pip python3-sphinx; \
pip3 install codecov; \
rm -rf /var/lib/apt/lists/*
FROM base AS libraries
# OSSFUZZ: libprotobuf-mutator
RUN set -ex; \
git clone https://github.com/google/libprotobuf-mutator.git \
/usr/src/libprotobuf-mutator; \
cd /usr/src/libprotobuf-mutator; \
git checkout d1fe8a7d8ae18f3d454f055eba5213c291986f21; \
mkdir build; \
cd build; \
cmake .. -GNinja -DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=ON \
-DLIB_PROTO_MUTATOR_TESTING=OFF -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="/usr"; \
ninja; \
cp -vpr external.protobuf/bin/* /usr/bin/; \
cp -vpr external.protobuf/include/* /usr/include/; \
cp -vpr external.protobuf/lib/* /usr/lib/; \
ninja install/strip; \
rm -rf /usr/src/libprotobuf-mutator
# OSSFUZZ: libfuzzer
RUN set -ex; \
cd /var/tmp; \
svn co https://llvm.org/svn/llvm-project/compiler-rt/trunk/lib/fuzzer libfuzzer; \
mkdir -p build-libfuzzer; \
cd build-libfuzzer; \
clang++-8 -O1 -stdlib=libstdc++ -std=c++11 -O2 -fPIC -c ../libfuzzer/*.cpp -I../libfuzzer; \
ar r /usr/lib/libFuzzingEngine.a *.o; \
rm -rf /var/lib/libfuzzer
# EVMONE
RUN set -ex; \
cd /usr/src; \
@ -81,7 +48,6 @@ RUN set -ex; \
cd evmone; \
mkdir build; \
cd build; \
# isoltest links against the evmone shared library
cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \
ninja; \
ninja install/strip; \

View File

@ -0,0 +1,61 @@
# vim:syntax=dockerfile
#------------------------------------------------------------------------------
# Dockerfile for building and testing Solidity Compiler on CI
# Target: Ubuntu 19.04 (Disco Dingo) Clang variant
# URL: https://hub.docker.com/r/ethereum/solidity-buildpack-deps
#
# This file is part of solidity.
#
# solidity is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# solidity is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with solidity. If not, see <http://www.gnu.org/licenses/>
#
# (c) 2016-2019 solidity contributors.
#------------------------------------------------------------------------------
FROM buildpack-deps:focal AS base
ARG DEBIAN_FRONTEND=noninteractive
RUN set -ex; \
apt-get update; \
apt-get install -qqy --no-install-recommends \
build-essential \
software-properties-common \
cmake ninja-build \
libboost-filesystem-dev libboost-test-dev libboost-system-dev \
libboost-program-options-dev \
clang llvm-dev \
z3 libz3-dev \
; \
rm -rf /var/lib/apt/lists/*
FROM base AS libraries
ENV CC clang
ENV CXX clang++
# EVMONE
RUN set -ex; \
cd /usr/src; \
git clone --branch="v0.4.0" --recurse-submodules https://github.com/ethereum/evmone.git; \
cd evmone; \
mkdir build; \
cd build; \
cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \
ninja; \
ninja install/strip; \
rm -rf /usr/src/evmone
FROM base
COPY --from=libraries /usr/lib /usr/lib
COPY --from=libraries /usr/bin /usr/bin
COPY --from=libraries /usr/include /usr/include

View File

@ -57,7 +57,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 $SOLTEST_FLAGS"
test "${OPTIMIZE}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --optimize"
test "${ABI_ENCODER_V2}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --abiencoderv2 --optimize-yul"
test "${ABI_ENCODER_V2}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --abiencoderv2"
echo "Running ${REPODIR}/build/test/soltest ${BOOST_TEST_ARGS} -- ${SOLTEST_ARGS}"

View File

@ -16,17 +16,25 @@ Bugfixes:
### 0.6.8 (unreleased)
Language Features:
Important Bugfixes:
* Add missing callvalue check to the creation code of a contract that does not define a constructor but has a base that does define a constructor.
Language Features:
* Implemented ``type(X).min`` and ``type(X).max`` for every integer type ``X`` that returns the smallest and largest value representable by the type.
Compiler Features:
* Commandline Interface: Don't ignore `--yul-optimizations` in assembly mode.
Bugfixes:
* ABI: Skip ``private`` or ``internal`` constructors.
* Type Checker: Disallow accessing ``runtimeCode`` for contract types that contain immutable state variables.
* Fixed an "Assembly Exception in Bytecode" error where requested functions were generated twice.
* Natspec: Fixed a bug that ignored ``@return`` tag when no other developer-documentation tags were present.
* Yul assembler: Fix source location of variable declarations without value.
### 0.6.7 (2020-05-04)

View File

@ -1,4 +1,12 @@
[
{
"name": "ImplicitConstructorCallvalueCheck",
"summary": "The creation code of a contract that does not define a constructor but has a base that does define a constructor did not revert for calls with non-zero value.",
"description": "Starting from Solidity 0.4.5 the creation code of contracts without explicit payable constructor is supposed to contain a callvalue check that results in contract creation reverting, if non-zero value is passed. However, this check was missing in case no explicit constructor was defined in a contract at all, but the contract has a base that does define a constructor. In these cases it is possible to send value in a contract creation transaction or using inline assembly without revert, even though the creation code is supposed to be non-payable.",
"introduced": "0.4.5",
"fixed": "0.6.8",
"severity": "very low"
},
{
"name": "TupleAssignmentMultiStackSlotComponents",
"summary": "Tuple assignments with components that occupy several stack slots, i.e. nested tuples, pointers to external functions or references to dynamically sized calldata arrays, can result in invalid values.",

View File

@ -415,6 +415,7 @@
},
"0.4.10": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -433,6 +434,7 @@
},
"0.4.11": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -450,6 +452,7 @@
},
"0.4.12": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -466,6 +469,7 @@
},
"0.4.13": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -482,6 +486,7 @@
},
"0.4.14": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -497,6 +502,7 @@
},
"0.4.15": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -511,6 +517,7 @@
},
"0.4.16": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -527,6 +534,7 @@
},
"0.4.17": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -544,6 +552,7 @@
},
"0.4.18": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -560,6 +569,7 @@
},
"0.4.19": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -596,6 +606,7 @@
},
"0.4.20": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -613,6 +624,7 @@
},
"0.4.21": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -630,6 +642,7 @@
},
"0.4.22": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -647,6 +660,7 @@
},
"0.4.23": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -663,6 +677,7 @@
},
"0.4.24": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -679,6 +694,7 @@
},
"0.4.25": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -693,6 +709,7 @@
},
"0.4.26": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -739,6 +756,7 @@
},
"0.4.5": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -758,6 +776,7 @@
},
"0.4.6": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -776,6 +795,7 @@
},
"0.4.7": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -794,6 +814,7 @@
},
"0.4.8": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -812,6 +833,7 @@
},
"0.4.9": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -830,6 +852,7 @@
},
"0.5.0": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -844,6 +867,7 @@
},
"0.5.1": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -858,6 +882,7 @@
},
"0.5.10": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -868,6 +893,7 @@
},
"0.5.11": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -877,6 +903,7 @@
},
"0.5.12": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -886,6 +913,7 @@
},
"0.5.13": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -895,6 +923,7 @@
},
"0.5.14": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -905,6 +934,7 @@
},
"0.5.15": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -914,6 +944,7 @@
},
"0.5.16": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden"
@ -922,6 +953,7 @@
},
"0.5.17": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow"
],
@ -929,6 +961,7 @@
},
"0.5.2": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -943,6 +976,7 @@
},
"0.5.3": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -957,6 +991,7 @@
},
"0.5.4": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -971,6 +1006,7 @@
},
"0.5.5": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -987,6 +1023,7 @@
},
"0.5.6": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -1003,6 +1040,7 @@
},
"0.5.7": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -1017,6 +1055,7 @@
},
"0.5.8": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -1030,6 +1069,7 @@
},
"0.5.9": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
@ -1042,6 +1082,7 @@
},
"0.6.0": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"YulOptimizerRedundantAssignmentBreakContinue"
@ -1050,6 +1091,7 @@
},
"0.6.1": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow"
],
@ -1057,6 +1099,7 @@
},
"0.6.2": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow"
],
@ -1064,6 +1107,7 @@
},
"0.6.3": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow"
],
@ -1071,6 +1115,7 @@
},
"0.6.4": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow"
],
@ -1078,16 +1123,21 @@
},
"0.6.5": {
"bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents"
],
"released": "2020-04-06"
},
"0.6.6": {
"bugs": [],
"bugs": [
"ImplicitConstructorCallvalueCheck"
],
"released": "2020-04-09"
},
"0.6.7": {
"bugs": [],
"bugs": [
"ImplicitConstructorCallvalueCheck"
],
"released": "2020-05-04"
}
}

View File

@ -121,6 +121,8 @@ Global Variables
- ``type(C).creationCode`` (``bytes memory``): creation bytecode of the given contract, see :ref:`Type Information<meta-type>`.
- ``type(C).runtimeCode`` (``bytes memory``): runtime bytecode of the given contract, see :ref:`Type Information<meta-type>`.
- ``type(I).interfaceId`` (``bytes4``): value containing the EIP-165 interface identifier of the given interface, see :ref:`Type Information<meta-type>`.
- ``type(T).min`` (``T``): the minimum value representable by the integer type ``T``, see :ref:`Type Information<meta-type>`.
- ``type(T).max`` (``T``): the maximum value representable by the integer type ``T``, see :ref:`Type Information<meta-type>`.
.. note::
Do not rely on ``block.timestamp`` or ``blockhash`` as a source of randomness,

View File

@ -40,6 +40,9 @@ Operators:
* Shift operators: ``<<`` (left shift), ``>>`` (right shift)
* Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo), ``**`` (exponentiation)
For an integer type ``X``, you can use ``type(X).min`` and ``type(X).max`` to
access the minimum and maximum value representable by the type.
.. warning::
Integers in Solidity are restricted to a certain range. For example, with ``uint32``, this is ``0`` up to ``2**32 - 1``.

View File

@ -296,10 +296,11 @@ Furthermore, all functions of the current contract are callable directly includi
Type Information
----------------
The expression ``type(X)`` can be used to retrieve information about the
type ``X``. Currently, there is limited support for this feature, but
it might be expanded in the future. The following properties are
available for a contract type ``C``:
The expression ``type(X)`` can be used to retrieve information about the type
``X``. Currently, there is limited support for this feature (``X`` can be either
a contract or an integer type) but it might be expanded in the future.
The following properties are available for a contract type ``C``:
``type(C).name``
The name of the contract.
@ -329,3 +330,10 @@ for an interface type ``I``:
A ``bytes4`` value containing the `EIP-165 <https://eips.ethereum.org/EIPS/eip-165>`_
interface identifier of the given interface ``I``. This identifier is defined as the ``XOR`` of all
function selectors defined within the interface itself - excluding all inherited functions.
The following properties are available for an integer type ``T``:
``type(T).min``
The smallest value representable by type ``T``.
``type(T).max``
The largest value representable by type ``T``.

View File

@ -104,6 +104,8 @@ less-than comparison.
}
}
At the :ref:`end of the section <erc20yul>`, a complete implementation of
the ERC-20 standard can be found.
@ -1039,14 +1041,16 @@ Optimization step sequence
--------------------------
By default the Yul optimizer applies its predefined sequence of optimization steps to the generated assembly.
You can override this sequence and supply your own using the `--yul-optimizations` option when compiling
in Solidity mode:
You can override this sequence and supply your own using the ``--yul-optimizations`` option:
.. code-block:: sh
solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul'
By enclosing part of the sequence in square brackets (`[]`) you tell the optimizer to repeatedly
The order of steps is significant and affects the quality of the output.
Moreover, applying a step may uncover new optimization opportunities for others that were already
applied so repeating steps is often beneficial.
By enclosing part of the sequence in square brackets (``[]``) you tell the optimizer to repeatedly
apply that part until it no longer improves the size of the resulting assembly.
You can use brackets multiple times in a single sequence but they cannot be nested.
@ -1055,35 +1059,230 @@ The following optimization steps are available:
============ ===============================
Abbreviation Full name
============ ===============================
f `BlockFlattener`
l `CircularReferencesPruner`
c `CommonSubexpressionEliminator`
C `ConditionalSimplifier`
U `ConditionalUnsimplifier`
n `ControlFlowSimplifier`
D `DeadCodeEliminator`
v `EquivalentFunctionCombiner`
e `ExpressionInliner`
j `ExpressionJoiner`
s `ExpressionSimplifier`
x `ExpressionSplitter`
I `ForLoopConditionIntoBody`
O `ForLoopConditionOutOfBody`
o `ForLoopInitRewriter`
i `FullInliner`
g `FunctionGrouper`
h `FunctionHoister`
T `LiteralRematerialiser`
L `LoadResolver`
M `LoopInvariantCodeMotion`
r `RedundantAssignEliminator`
m `Rematerialiser`
V `SSAReverser`
a `SSATransform`
t `StructuralSimplifier`
u `UnusedPruner`
d `VarDeclInitializer`
``f`` ``BlockFlattener``
``l`` ``CircularReferencesPruner``
``c`` ``CommonSubexpressionEliminator``
``C`` ``ConditionalSimplifier``
``U`` ``ConditionalUnsimplifier``
``n`` ``ControlFlowSimplifier``
``D`` ``DeadCodeEliminator``
``v`` ``EquivalentFunctionCombiner``
``e`` ``ExpressionInliner``
``j`` ``ExpressionJoiner``
``s`` ``ExpressionSimplifier``
``x`` ``ExpressionSplitter``
``I`` ``ForLoopConditionIntoBody``
``O`` ``ForLoopConditionOutOfBody``
``o`` ``ForLoopInitRewriter``
``i`` ``FullInliner``
``g`` ``FunctionGrouper``
``h`` ``FunctionHoister``
``T`` ``LiteralRematerialiser``
``L`` ``LoadResolver``
``M`` ``LoopInvariantCodeMotion``
``r`` ``RedundantAssignEliminator``
``m`` ``Rematerialiser``
``V`` ``SSAReverser``
``a`` ``SSATransform``
``t`` ``StructuralSimplifier``
``u`` ``UnusedPruner``
``d`` ``VarDeclInitializer``
============ ===============================
Some steps depend on properties ensured by `BlockFlattener`, `FunctionGrouper`, `ForLoopInitRewriter`.
Some steps depend on properties ensured by ``BlockFlattener``, ``FunctionGrouper``, ``ForLoopInitRewriter``.
For this reason the Yul optimizer always applies them before applying any steps supplied by the user.
.. _erc20yul:
Complete ERC20 Example
======================
.. code-block:: yul
object "Token" {
code {
// Store the creator in slot zero.
sstore(0, caller())
// Deploy the contract
datacopy(0, dataoffset("runtime"), datasize("runtime"))
return(0, datasize("runtime"))
}
object "runtime" {
code {
// Protection against sending Ether
require(iszero(callvalue()))
// Dispatcher
switch selector()
case 0x70a08231 /* "balanceOf(address)" */ {
returnUint(balanceOf(decodeAsAddress(0)))
}
case 0x18160ddd /* "totalSupply()" */ {
returnUint(totalSupply())
}
case 0xa9059cbb /* "transfer(address,uint256)" */ {
transfer(decodeAsAddress(0), decodeAsUint(1))
returnTrue()
}
case 0x23b872dd /* "transferFrom(address,address,uint256)" */ {
transferFrom(decodeAsAddress(0), decodeAsAddress(1), decodeAsUint(2))
returnTrue()
}
case 0x095ea7b3 /* "approve(address,uint256)" */ {
approve(decodeAsAddress(0), decodeAsUint(1))
returnTrue()
}
case 0xdd62ed3e /* "allowance(address,address)" */ {
returnUint(allowance(decodeAsAddress(0), decodeAsAddress(1)))
}
case 0x40c10f19 /* "mint(address,uint256)" */ {
mint(decodeAsAddress(0), decodeAsUint(1))
returnTrue()
}
default {
revert(0, 0)
}
function mint(account, amount) {
require(calledByOwner())
mintTokens(amount)
addToBalance(account, amount)
emitTransfer(0, account, amount)
}
function transfer(to, amount) {
executeTransfer(caller(), to, amount)
}
function approve(spender, amount) {
revertIfZeroAddress(spender)
setAllowance(caller(), spender, amount)
emitApproval(caller(), spender, amount)
}
function transferFrom(from, to, amount) {
decreaseAllowanceBy(from, caller(), amount)
executeTransfer(from, to, amount)
}
function executeTransfer(from, to, amount) {
revertIfZeroAddress(to)
deductFromBalance(from, amount)
addToBalance(to, amount)
emitTransfer(from, to, amount)
}
/* ---------- calldata decoding functions ----------- */
function selector() -> s {
s := div(calldataload(0), 0x100000000000000000000000000000000000000000000000000000000)
}
function decodeAsAddress(offset) -> v {
v := decodeAsUint(offset)
if iszero(iszero(and(v, not(0xffffffffffffffffffffffffffffffffffffffff)))) {
revert(0, 0)
}
}
function decodeAsUint(offset) -> v {
let pos := add(4, mul(offset, 0x20))
if lt(calldatasize(), add(pos, 0x20)) {
revert(0, 0)
}
v := calldataload(pos)
}
/* ---------- calldata encoding functions ---------- */
function returnUint(v) {
mstore(0, v)
return(0, 0x20)
}
function returnTrue() {
returnUint(1)
}
/* -------- events ---------- */
function emitTransfer(from, to, amount) {
let signatureHash := 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
emitEvent(signatureHash, from, to, amount)
}
function emitApproval(from, spender, amount) {
let signatureHash := 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925
emitEvent(signatureHash, from, spender, amount)
}
function emitEvent(signatureHash, indexed1, indexed2, nonIndexed) {
mstore(0, nonIndexed)
log3(0, 0x20, signatureHash, indexed1, indexed2)
}
/* -------- storage layout ---------- */
function ownerPos() -> p { p := 0 }
function totalSupplyPos() -> p { p := 1 }
function accountToStorageOffset(account) -> offset {
offset := add(0x1000, account)
}
function allowanceStorageOffset(account, spender) -> offset {
offset := accountToStorageOffset(account)
mstore(0, offset)
mstore(0x20, spender)
offset := keccak256(0, 0x40)
}
/* -------- storage access ---------- */
function owner() -> o {
o := sload(ownerPos())
}
function totalSupply() -> supply {
supply := sload(totalSupplyPos())
}
function mintTokens(amount) {
sstore(totalSupplyPos(), safeAdd(totalSupply(), amount))
}
function balanceOf(account) -> bal {
bal := sload(accountToStorageOffset(account))
}
function addToBalance(account, amount) {
let offset := accountToStorageOffset(account)
sstore(offset, safeAdd(sload(offset), amount))
}
function deductFromBalance(account, amount) {
let offset := accountToStorageOffset(account)
let bal := sload(offset)
require(lte(amount, bal))
sstore(offset, sub(bal, amount))
}
function allowance(account, spender) -> amount {
amount := sload(allowanceStorageOffset(account, spender))
}
function setAllowance(account, spender, amount) {
sstore(allowanceStorageOffset(account, spender), amount)
}
function decreaseAllowanceBy(account, spender, amount) {
let offset := allowanceStorageOffset(account, spender)
let currentAllowance := sload(offset)
require(lte(amount, currentAllowance))
sstore(offset, sub(currentAllowance, amount))
}
/* ---------- utility functions ---------- */
function lte(a, b) -> r {
r := iszero(gt(a, b))
}
function gte(a, b) -> r {
r := iszero(lt(a, b))
}
function safeAdd(a, b) -> r {
r := add(a, b)
if or(lt(r, a), lt(r, b)) { revert(0, 0) }
}
function calledByOwner() -> cbo {
cbo := eq(owner(), caller())
}
function revertIfZeroAddress(addr) {
require(addr)
}
function require(condition) {
if iszero(condition) { revert(0, 0) }
}
}
}
}

View File

@ -26,9 +26,6 @@
#include <libevmasm/CommonSubexpressionEliminator.h>
#include <libevmasm/SimplificationRules.h>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/noncopyable.hpp>
#include <functional>
#include <tuple>
#include <utility>

View File

@ -29,9 +29,6 @@
#include <libevmasm/RuleList.h>
#include <libsolutil/Assertions.h>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/noncopyable.hpp>
#include <utility>
#include <functional>

View File

@ -28,6 +28,8 @@ using namespace std;
using namespace solidity;
using namespace solidity::langutil;
ErrorId solidity::langutil::operator"" _error(unsigned long long _error) { return ErrorId{ _error }; }
ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter)
{
if (&_errorReporter == this)
@ -36,30 +38,31 @@ ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter)
return *this;
}
void ErrorReporter::warning(string const& _description)
void ErrorReporter::warning(ErrorId _error, string const& _description)
{
error(Error::Type::Warning, SourceLocation(), _description);
error(_error, Error::Type::Warning, SourceLocation(), _description);
}
void ErrorReporter::warning(
ErrorId _error,
SourceLocation const& _location,
string const& _description
)
{
error(Error::Type::Warning, _location, _description);
error(_error, Error::Type::Warning, _location, _description);
}
void ErrorReporter::warning(
ErrorId _error,
SourceLocation const& _location,
string const& _description,
SecondarySourceLocation const& _secondaryLocation
)
{
error(Error::Type::Warning, _location, _secondaryLocation, _description);
error(_error, Error::Type::Warning, _location, _secondaryLocation, _description);
}
void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, string const& _description)
void ErrorReporter::error(ErrorId, Error::Type _type, SourceLocation const& _location, string const& _description)
{
if (checkForExcessiveErrors(_type))
return;
@ -72,7 +75,7 @@ void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, st
m_errorList.push_back(err);
}
void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
void ErrorReporter::error(ErrorId, Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
{
if (checkForExcessiveErrors(_type))
return;
@ -123,15 +126,15 @@ bool ErrorReporter::checkForExcessiveErrors(Error::Type _type)
return false;
}
void ErrorReporter::fatalError(Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
void ErrorReporter::fatalError(ErrorId _error, Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
{
error(_type, _location, _secondaryLocation, _description);
error(_error, _type, _location, _secondaryLocation, _description);
BOOST_THROW_EXCEPTION(FatalError());
}
void ErrorReporter::fatalError(Error::Type _type, SourceLocation const& _location, string const& _description)
void ErrorReporter::fatalError(ErrorId _error, Error::Type _type, SourceLocation const& _location, string const& _description)
{
error(_type, _location, _description);
error(_error, _type, _location, _description);
BOOST_THROW_EXCEPTION(FatalError());
}
@ -145,9 +148,10 @@ void ErrorReporter::clear()
m_errorList.clear();
}
void ErrorReporter::declarationError(SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
void ErrorReporter::declarationError(ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
{
error(
_error,
Error::Type::DeclarationError,
_location,
_secondaryLocation,
@ -155,53 +159,59 @@ void ErrorReporter::declarationError(SourceLocation const& _location, SecondaryS
);
}
void ErrorReporter::declarationError(SourceLocation const& _location, string const& _description)
void ErrorReporter::declarationError(ErrorId _error, SourceLocation const& _location, string const& _description)
{
error(
_error,
Error::Type::DeclarationError,
_location,
_description
);
}
void ErrorReporter::fatalDeclarationError(SourceLocation const& _location, std::string const& _description)
void ErrorReporter::fatalDeclarationError(ErrorId _error, SourceLocation const& _location, std::string const& _description)
{
fatalError(
_error,
Error::Type::DeclarationError,
_location,
_description);
}
void ErrorReporter::parserError(SourceLocation const& _location, string const& _description)
void ErrorReporter::parserError(ErrorId _error, SourceLocation const& _location, string const& _description)
{
error(
_error,
Error::Type::ParserError,
_location,
_description
);
}
void ErrorReporter::fatalParserError(SourceLocation const& _location, string const& _description)
void ErrorReporter::fatalParserError(ErrorId _error, SourceLocation const& _location, string const& _description)
{
fatalError(
_error,
Error::Type::ParserError,
_location,
_description
);
}
void ErrorReporter::syntaxError(SourceLocation const& _location, string const& _description)
void ErrorReporter::syntaxError(ErrorId _error, SourceLocation const& _location, string const& _description)
{
error(
_error,
Error::Type::SyntaxError,
_location,
_description
);
}
void ErrorReporter::typeError(SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
void ErrorReporter::typeError(ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
{
error(
_error,
Error::Type::TypeError,
_location,
_secondaryLocation,
@ -209,18 +219,21 @@ void ErrorReporter::typeError(SourceLocation const& _location, SecondarySourceLo
);
}
void ErrorReporter::typeError(SourceLocation const& _location, string const& _description)
void ErrorReporter::typeError(ErrorId _error, SourceLocation const& _location, string const& _description)
{
error(
_error,
Error::Type::TypeError,
_location,
_description
);
}
void ErrorReporter::fatalTypeError(SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
void ErrorReporter::fatalTypeError(ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description)
{
fatalError(
_error,
Error::Type::TypeError,
_location,
_secondaryLocation,
@ -228,26 +241,30 @@ void ErrorReporter::fatalTypeError(SourceLocation const& _location, SecondarySou
);
}
void ErrorReporter::fatalTypeError(SourceLocation const& _location, string const& _description)
void ErrorReporter::fatalTypeError(ErrorId _error, SourceLocation const& _location, string const& _description)
{
fatalError(Error::Type::TypeError,
fatalError(
_error,
Error::Type::TypeError,
_location,
_description
);
}
void ErrorReporter::docstringParsingError(string const& _description)
void ErrorReporter::docstringParsingError(ErrorId _error, string const& _description)
{
error(
_error,
Error::Type::DocstringParsingError,
SourceLocation(),
_description
);
}
void ErrorReporter::docstringParsingError(SourceLocation const& _location, string const& _description)
void ErrorReporter::docstringParsingError(ErrorId _error, SourceLocation const& _location, string const& _description)
{
error(
_error,
Error::Type::DocstringParsingError,
_location,
_description

View File

@ -33,6 +33,17 @@
namespace solidity::langutil
{
/**
* Unique identifiers are used to tag and track individual error cases.
* They are passed as the first parameter of error reporting functions.
* Suffix _error helps to find them in the sources.
* The struct ErrorId prevents incidental calls like typeError(3141) instead of typeError(3141_error).
* To create a new ID, one can add 0000_error and then run "python ./scripts/correct_error_ids.py"
* from the root of the repo.
*/
struct ErrorId { unsigned long long error = 0; };
ErrorId operator"" _error(unsigned long long error);
class ErrorReporter
{
public:
@ -50,64 +61,68 @@ public:
m_errorList += _errorList;
}
void warning(std::string const& _description);
void warning(ErrorId _error, std::string const& _description);
void warning(SourceLocation const& _location, std::string const& _description);
void warning(ErrorId _error, SourceLocation const& _location, std::string const& _description);
void warning(
ErrorId _error,
SourceLocation const& _location,
std::string const& _description,
SecondarySourceLocation const& _secondaryLocation
);
void error(
ErrorId _error,
Error::Type _type,
SourceLocation const& _location,
std::string const& _description
);
void declarationError(
ErrorId _error,
SourceLocation const& _location,
SecondarySourceLocation const& _secondaryLocation,
std::string const& _description
);
void declarationError(SourceLocation const& _location, std::string const& _description);
void declarationError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
void fatalDeclarationError(SourceLocation const& _location, std::string const& _description);
void fatalDeclarationError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
void parserError(SourceLocation const& _location, std::string const& _description);
void parserError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
void fatalParserError(SourceLocation const& _location, std::string const& _description);
void fatalParserError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
void syntaxError(SourceLocation const& _location, std::string const& _description);
void syntaxError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
void typeError(
ErrorId _error,
SourceLocation const& _location,
SecondarySourceLocation const& _secondaryLocation = SecondarySourceLocation(),
std::string const& _description = std::string()
);
void typeError(SourceLocation const& _location, std::string const& _description);
void typeError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
template <typename... Strings>
void typeErrorConcatenateDescriptions(SourceLocation const& _location, Strings const&... _descriptions)
void typeErrorConcatenateDescriptions(ErrorId _error, SourceLocation const& _location, Strings const&... _descriptions)
{
std::initializer_list<std::string> const descs = {_descriptions...};
std::initializer_list<std::string> const descs = { _descriptions... };
solAssert(descs.size() > 0, "Need error descriptions!");
auto filterEmpty = boost::adaptors::filtered([](std::string const& _s) { return !_s.empty(); });
std::string errorStr = util::joinHumanReadable(descs | filterEmpty, " ");
error(Error::Type::TypeError, _location, errorStr);
error(_error, Error::Type::TypeError, _location, errorStr);
}
void fatalTypeError(SourceLocation const& _location, std::string const& _description);
void fatalTypeError(SourceLocation const& _location, SecondarySourceLocation const& _secondLocation, std::string const& _description);
void fatalTypeError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
void fatalTypeError(ErrorId _error, SourceLocation const& _location, SecondarySourceLocation const& _secondLocation, std::string const& _description);
void docstringParsingError(std::string const& _description);
void docstringParsingError(SourceLocation const& _location, std::string const& _description);
void docstringParsingError(ErrorId _error, std::string const& _description);
void docstringParsingError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
ErrorList const& errors() const;
@ -124,18 +139,21 @@ public:
private:
void error(
ErrorId _error,
Error::Type _type,
SourceLocation const& _location,
SecondarySourceLocation const& _secondaryLocation,
std::string const& _description = std::string());
void fatalError(
ErrorId _error,
Error::Type _type,
SourceLocation const& _location,
SecondarySourceLocation const& _secondaryLocation,
std::string const& _description = std::string());
void fatalError(
ErrorId _error,
Error::Type _type,
SourceLocation const& _location = SourceLocation(),
std::string const& _description = std::string());

View File

@ -77,9 +77,9 @@ void ParserBase::expectToken(Token _value, bool _advance)
{
string const expectedToken = ParserBase::tokenName(_value);
if (m_parserErrorRecovery)
parserError("Expected " + expectedToken + " but got " + tokenName(tok));
parserError(6635_error, "Expected " + expectedToken + " but got " + tokenName(tok));
else
fatalParserError("Expected " + expectedToken + " but got " + tokenName(tok));
fatalParserError(2314_error, "Expected " + expectedToken + " but got " + tokenName(tok));
// Do not advance so that recovery can sync or make use of the current token.
// This is especially useful if the expected token
// is the only one that is missing and is at the end of a construct.
@ -108,21 +108,21 @@ void ParserBase::expectTokenOrConsumeUntil(Token _value, string const& _currentN
// rollback to where the token started, and raise exception to be caught at a higher level.
m_scanner->setPosition(startPosition);
m_inParserRecovery = true;
fatalParserError(errorLoc, msg);
fatalParserError(1957_error, errorLoc, msg);
}
else
{
if (m_inParserRecovery)
parserWarning("Recovered in " + _currentNodeName + " at " + expectedToken + ".");
parserWarning(3796_error, "Recovered in " + _currentNodeName + " at " + expectedToken + ".");
else
parserError(errorLoc, msg + "Recovered at next " + expectedToken);
parserError(1054_error, errorLoc, msg + "Recovered at next " + expectedToken);
m_inParserRecovery = false;
}
}
else if (m_inParserRecovery)
{
string expectedToken = ParserBase::tokenName(_value);
parserWarning("Recovered in " + _currentNodeName + " at " + expectedToken + ".");
parserWarning(3347_error, "Recovered in " + _currentNodeName + " at " + expectedToken + ".");
m_inParserRecovery = false;
}
@ -134,7 +134,7 @@ void ParserBase::increaseRecursionDepth()
{
m_recursionDepth++;
if (m_recursionDepth >= 1200)
fatalParserError("Maximum recursion depth reached during parsing.");
fatalParserError(7319_error, "Maximum recursion depth reached during parsing.");
}
void ParserBase::decreaseRecursionDepth()
@ -143,27 +143,27 @@ void ParserBase::decreaseRecursionDepth()
m_recursionDepth--;
}
void ParserBase::parserWarning(string const& _description)
void ParserBase::parserWarning(ErrorId _error, string const& _description)
{
m_errorReporter.warning(currentLocation(), _description);
m_errorReporter.warning(_error, currentLocation(), _description);
}
void ParserBase::parserError(SourceLocation const& _location, string const& _description)
void ParserBase::parserError(ErrorId _error, SourceLocation const& _location, string const& _description)
{
m_errorReporter.parserError(_location, _description);
m_errorReporter.parserError(_error, _location, _description);
}
void ParserBase::parserError(string const& _description)
void ParserBase::parserError(ErrorId _error, string const& _description)
{
parserError(currentLocation(), _description);
parserError(_error, currentLocation(), _description);
}
void ParserBase::fatalParserError(string const& _description)
void ParserBase::fatalParserError(ErrorId _error, string const& _description)
{
fatalParserError(currentLocation(), _description);
fatalParserError(_error, currentLocation(), _description);
}
void ParserBase::fatalParserError(SourceLocation const& _location, string const& _description)
void ParserBase::fatalParserError(ErrorId _error, SourceLocation const& _location, string const& _description)
{
m_errorReporter.fatalParserError(_location, _description);
m_errorReporter.fatalParserError(_error, _location, _description);
}

View File

@ -32,6 +32,7 @@ namespace solidity::langutil
class ErrorReporter;
class Scanner;
struct ErrorId;
class ParserBase
{
@ -88,17 +89,17 @@ protected:
/// Creates a @ref ParserError and annotates it with the current position and the
/// given @a _description.
void parserError(std::string const& _description);
void parserError(SourceLocation const& _location, std::string const& _description);
void parserError(ErrorId _error, std::string const& _description);
void parserError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
/// Creates a @ref ParserWarning and annotates it with the current position and the
/// given @a _description.
void parserWarning(std::string const& _description);
void parserWarning(ErrorId _error, std::string const& _description);
/// Creates a @ref ParserError and annotates it with the current position and the
/// given @a _description. Throws the FatalError.
void fatalParserError(std::string const& _description);
void fatalParserError(SourceLocation const& _location, std::string const& _description);
void fatalParserError(ErrorId _error, std::string const& _description);
void fatalParserError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
std::shared_ptr<Scanner> m_scanner;
/// The reference to the list of errors and warning to add errors/warnings during parsing

View File

@ -166,7 +166,7 @@ if (NOT (${Z3_FOUND} OR ${CVC4_FOUND}))
endif()
add_library(solidity ${sources} ${z3_SRCS} ${cvc4_SRCS})
target_link_libraries(solidity PUBLIC yul evmasm langutil solutil Boost::boost Boost::filesystem Boost::system)
target_link_libraries(solidity PUBLIC yul evmasm langutil solutil Boost::boost)
if (${Z3_FOUND})
target_link_libraries(solidity PUBLIC z3::libz3)

View File

@ -29,6 +29,7 @@
using namespace std;
using namespace solidity;
using namespace solidity::frontend;
using namespace solidity::langutil;
void ConstantEvaluator::endVisit(UnaryOperation const& _operation)
{
@ -46,6 +47,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
TypePointer commonType = left->binaryOperatorResult(_operation.getOperator(), right);
if (!commonType)
m_errorReporter.fatalTypeError(
6020_error,
_operation.location(),
"Operator " +
string(TokenTraits::toString(_operation.getOperator())) +
@ -82,7 +84,7 @@ void ConstantEvaluator::endVisit(Identifier const& _identifier)
else if (!m_types->count(value.get()))
{
if (m_depth > 32)
m_errorReporter.fatalTypeError(_identifier.location(), "Cyclic constant definition (or maximum recursion depth exhausted).");
m_errorReporter.fatalTypeError(5210_error, _identifier.location(), "Cyclic constant definition (or maximum recursion depth exhausted).");
ConstantEvaluator(m_errorReporter, m_depth + 1, m_types).evaluate(*value);
}

View File

@ -26,7 +26,6 @@
#include <libsolidity/analysis/TypeChecker.h>
#include <liblangutil/ErrorReporter.h>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/algorithm/string/predicate.hpp>
using namespace std;
@ -78,6 +77,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co
{
if (constructor)
m_errorReporter.declarationError(
7997_error,
function->location(),
SecondarySourceLocation().append("Another declaration is here:", constructor->location()),
"More than one constructor defined."
@ -88,6 +88,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co
{
if (fallback)
m_errorReporter.declarationError(
7301_error,
function->location(),
SecondarySourceLocation().append("Another declaration is here:", fallback->location()),
"Only one fallback function is allowed."
@ -98,6 +99,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co
{
if (receive)
m_errorReporter.declarationError(
4046_error,
function->location(),
SecondarySourceLocation().append("Another declaration is here:", receive->location()),
"Only one receive function is allowed."
@ -110,7 +112,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co
functions[function->name()].push_back(function);
}
findDuplicateDefinitions(functions, "Function with same name and arguments defined twice.");
findDuplicateDefinitions(functions);
}
void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contract)
@ -121,11 +123,11 @@ void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contr
for (EventDefinition const* event: _contract.events())
events[event->name()].push_back(event);
findDuplicateDefinitions(events, "Event with same name and arguments defined twice.");
findDuplicateDefinitions(events);
}
template <class T>
void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions, string _message)
void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions)
{
for (auto const& it: _definitions)
{
@ -144,12 +146,27 @@ void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const
if (ssl.infos.size() > 0)
{
ssl.limitSize(_message);
ErrorId error;
string message;
if constexpr (is_same_v<T, FunctionDefinition const*>)
{
error = 1686_error;
message = "Function with same name and arguments defined twice.";
}
else
{
static_assert(is_same_v<T, EventDefinition const*>, "Expected \"FunctionDefinition const*\" or \"EventDefinition const*\"");
error = 5883_error;
message = "Event with same name and arguments defined twice.";
}
ssl.limitSize(message);
m_errorReporter.declarationError(
error,
overloads[i]->location(),
ssl,
_message
message
);
}
}
@ -197,9 +214,9 @@ void ContractLevelChecker::checkAbstractDefinitions(ContractDefinition const& _c
if (_contract.abstract())
{
if (_contract.contractKind() == ContractKind::Interface)
m_errorReporter.typeError(_contract.location(), "Interfaces do not need the \"abstract\" keyword, they are abstract implicitly.");
m_errorReporter.typeError(9348_error, _contract.location(), "Interfaces do not need the \"abstract\" keyword, they are abstract implicitly.");
else if (_contract.contractKind() == ContractKind::Library)
m_errorReporter.typeError(_contract.location(), "Libraries cannot be abstract.");
m_errorReporter.typeError(9571_error, _contract.location(), "Libraries cannot be abstract.");
else
solAssert(_contract.contractKind() == ContractKind::Contract, "");
}
@ -215,10 +232,12 @@ void ContractLevelChecker::checkAbstractDefinitions(ContractDefinition const& _c
SecondarySourceLocation ssl;
for (auto declaration: _contract.annotation().unimplementedDeclarations)
ssl.append("Missing implementation: ", declaration->location());
m_errorReporter.typeError(_contract.location(), ssl,
"Contract \"" + _contract.annotation().canonicalName
+ "\" should be marked as abstract.");
m_errorReporter.typeError(
3656_error,
_contract.location(),
ssl,
"Contract \"" + _contract.annotation().canonicalName + "\" should be marked as abstract."
);
}
}
@ -243,6 +262,7 @@ void ContractLevelChecker::checkBaseConstructorArguments(ContractDefinition cons
}
else
m_errorReporter.declarationError(
1563_error,
modifier->location(),
"Modifier-style base constructor call without arguments."
);
@ -304,6 +324,7 @@ void ContractLevelChecker::annotateBaseConstructorArguments(
}
m_errorReporter.declarationError(
3364_error,
*mainLocation,
ssl,
"Base constructor arguments given twice."
@ -343,6 +364,7 @@ void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _c
for (size_t j = i + 1; j < it.second.size(); ++j)
if (!it.second[i].second->hasEqualParameterTypes(*it.second[j].second))
m_errorReporter.typeError(
9914_error,
it.second[j].first->location(),
"Function overload clash during conversion to external types for arguments."
);
@ -356,6 +378,7 @@ void ContractLevelChecker::checkHashCollisions(ContractDefinition const& _contra
util::FixedHash<4> const& hash = it.first;
if (hashes.count(hash))
m_errorReporter.typeError(
1860_error,
_contract.location(),
string("Function signature hash collision for ") + it.second->externalSignature()
);
@ -369,11 +392,11 @@ void ContractLevelChecker::checkLibraryRequirements(ContractDefinition const& _c
return;
if (!_contract.baseContracts().empty())
m_errorReporter.typeError(_contract.location(), "Library is not allowed to inherit.");
m_errorReporter.typeError(9469_error, _contract.location(), "Library is not allowed to inherit.");
for (auto const& var: _contract.stateVariables())
if (!var->isConstant())
m_errorReporter.typeError(var->location(), "Library cannot have non-constant state variables");
m_errorReporter.typeError(9957_error, var->location(), "Library cannot have non-constant state variables");
}
void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _contract)
@ -412,6 +435,7 @@ void ContractLevelChecker::checkBaseABICompatibility(ContractDefinition const& _
if (!errors.infos.empty())
m_errorReporter.fatalTypeError(
6594_error,
_contract.location(),
errors,
std::string("Contract \"") +
@ -428,6 +452,7 @@ void ContractLevelChecker::checkPayableFallbackWithoutReceive(ContractDefinition
if (auto const* fallback = _contract.fallbackFunction())
if (fallback->isPayable() && !_contract.interfaceFunctionList().empty() && !_contract.receiveFunction())
m_errorReporter.warning(
3628_error,
_contract.location(),
"This contract has a payable fallback function, but no receive ether function. Consider adding a receive ether function.",
SecondarySourceLocation{}.append("The payable fallback function is defined here.", fallback->location())

View File

@ -60,7 +60,7 @@ private:
void checkDuplicateFunctions(ContractDefinition const& _contract);
void checkDuplicateEvents(ContractDefinition const& _contract);
template <class T>
void findDuplicateDefinitions(std::map<std::string, std::vector<T>> const& _definitions, std::string _message);
void findDuplicateDefinitions(std::map<std::string, std::vector<T>> const& _definitions);
/// Checks for unimplemented functions and modifiers.
void checkAbstractDefinitions(ContractDefinition const& _contract);
/// Checks that the base constructor arguments are properly provided.

View File

@ -136,6 +136,7 @@ void ControlFlowAnalyzer::checkUninitializedAccess(CFGNode const* _entry, CFGNod
ssl.append("The variable was declared here.", variableOccurrence->declaration().location());
m_errorReporter.typeError(
3464_error,
variableOccurrence->occurrence() ?
*variableOccurrence->occurrence() :
variableOccurrence->declaration().location(),
@ -176,6 +177,6 @@ void ControlFlowAnalyzer::checkUnreachable(CFGNode const* _entry, CFGNode const*
// Extend the location, as long as the next location overlaps (unreachable is sorted).
for (; it != unreachable.end() && it->start <= location.end; ++it)
location.end = std::max(location.end, it->end);
m_errorReporter.warning(location, "Unreachable code.");
m_errorReporter.warning(5740_error, location, "Unreachable code.");
}
}

View File

@ -18,7 +18,6 @@
#include <libsolidity/analysis/ControlFlowGraph.h>
#include <libsolidity/analysis/ControlFlowBuilder.h>
#include <boost/range/adaptor/reversed.hpp>
#include <algorithm>
using namespace std;

View File

@ -188,12 +188,14 @@ void DeclarationTypeChecker::endVisit(Mapping const& _mapping)
{
if (contractType->contractDefinition().isLibrary())
m_errorReporter.fatalTypeError(
1665_error,
typeName->location(),
"Library types cannot be used as mapping keys."
);
}
else if (typeName->annotation().type->category() != Type::Category::Enum)
m_errorReporter.fatalTypeError(
7804_error,
typeName->location(),
"Only elementary types, contract types or enums are allowed as mapping keys."
);
@ -253,9 +255,9 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
return;
if (_variable.isConstant() && !_variable.isStateVariable())
m_errorReporter.declarationError(_variable.location(), "The \"constant\" keyword can only be used for state variables.");
m_errorReporter.declarationError(1788_error, _variable.location(), "The \"constant\" keyword can only be used for state variables.");
if (_variable.immutable() && !_variable.isStateVariable())
m_errorReporter.declarationError(_variable.location(), "The \"immutable\" keyword can only be used for state variables.");
m_errorReporter.declarationError(8297_error, _variable.location(), "The \"immutable\" keyword can only be used for state variables.");
if (!_variable.typeName())
{
@ -360,19 +362,19 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
void DeclarationTypeChecker::typeError(SourceLocation const& _location, string const& _description)
{
m_errorOccurred = true;
m_errorReporter.typeError(_location, _description);
m_errorReporter.typeError(2311_error, _location, _description);
}
void DeclarationTypeChecker::fatalTypeError(SourceLocation const& _location, string const& _description)
{
m_errorOccurred = true;
m_errorReporter.fatalTypeError(_location, _description);
m_errorReporter.fatalTypeError(5651_error, _location, _description);
}
void DeclarationTypeChecker::fatalDeclarationError(SourceLocation const& _location, string const& _description)
{
m_errorOccurred = true;
m_errorReporter.fatalDeclarationError(_location, _description);
m_errorReporter.fatalDeclarationError(2046_error, _location, _description);
}
bool DeclarationTypeChecker::check(ASTNode const& _node)

View File

@ -173,5 +173,5 @@ void DocStringAnalyser::parseDocStrings(
void DocStringAnalyser::appendError(SourceLocation const& _location, string const& _description)
{
m_errorOccured = true;
m_errorReporter.docstringParsingError(_location, _description);
m_errorReporter.docstringParsingError(7816_error, _location, _description);
}

View File

@ -100,11 +100,13 @@ inline vector<shared_ptr<MagicVariableDeclaration const>> constructMagicVariable
magicVarDecl("sha3", TypeProvider::function(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
magicVarDecl("suicide", TypeProvider::function(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
magicVarDecl("tx", TypeProvider::magic(MagicType::Kind::Transaction)),
// Accepts a MagicType that can be any contract type or an Integer type and returns a
// MagicType. The TypeChecker handles the correctness of the input and output types.
magicVarDecl("type", TypeProvider::function(
strings{"address"} /* accepts any contract type, handled by the type checker */,
strings{} /* returns a MagicType, handled by the type checker */,
strings{},
strings{},
FunctionType::Kind::MetaType,
false,
true,
StateMutability::Pure
)),
};
@ -123,7 +125,7 @@ vector<Declaration const*> GlobalContext::declarations() const
{
vector<Declaration const*> declarations;
declarations.reserve(m_magicVariables.size());
for (ASTPointer<Declaration const> const& variable: m_magicVariables)
for (ASTPointer<MagicVariableDeclaration const> const& variable: m_magicVariables)
declarations.push_back(variable.get());
return declarations;
}

View File

@ -22,6 +22,7 @@
#include <boost/range/adaptor/reversed.hpp>
using namespace solidity::frontend;
using namespace solidity::langutil;
void ImmutableValidator::analyze()
{
@ -42,7 +43,7 @@ void ImmutableValidator::analyze()
visitCallableIfNew(*contract->constructor());
for (ContractDefinition const* contract: linearizedContracts)
for (std::shared_ptr<InheritanceSpecifier> const inheritSpec: contract->baseContracts())
for (std::shared_ptr<InheritanceSpecifier> const& inheritSpec: contract->baseContracts())
if (auto args = inheritSpec->arguments())
ASTNode::listAccept(*args, *this);
@ -165,33 +166,39 @@ void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _va
{
if (!m_currentConstructor)
m_errorReporter.typeError(
1581_error,
_expression.location(),
"Immutable variables can only be initialized inline or assigned directly in the constructor."
);
else if (m_currentConstructor->annotation().contract->id() != _variableReference.annotation().contract->id())
m_errorReporter.typeError(
7484_error,
_expression.location(),
"Immutable variables must be initialized in the constructor of the contract they are defined in."
);
else if (m_inLoop)
m_errorReporter.typeError(
6672_error,
_expression.location(),
"Immutable variables can only be initialized once, not in a while statement."
);
else if (m_inBranch)
m_errorReporter.typeError(
4599_error,
_expression.location(),
"Immutable variables must be initialized unconditionally, not in an if statement."
);
if (!m_initializedStateVariables.emplace(&_variableReference).second)
m_errorReporter.typeError(
1574_error,
_expression.location(),
"Immutable state variable already initialized."
);
}
else if (m_inConstructionContext)
m_errorReporter.typeError(
7733_error,
_expression.location(),
"Immutable variables cannot be read during contract creation time, which means "
"they cannot be read in the constructor or any function or modifier called from it."
@ -205,6 +212,7 @@ void ImmutableValidator::checkAllVariablesInitialized(solidity::langutil::Source
if (varDecl->immutable())
if (!util::contains(m_initializedStateVariables, varDecl))
m_errorReporter.typeError(
2658_error,
_location,
solidity::langutil::SecondarySourceLocation().append("Not initialized: ", varDecl->location()),
"Construction control flow ends without initializing all immutable state variables."

View File

@ -80,6 +80,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
if (!_sourceUnits.count(path))
{
m_errorReporter.declarationError(
5073_error,
imp->location(),
"Import \"" + path + "\" (referenced as \"" + imp->path() + "\") not found."
);
@ -95,6 +96,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
if (declarations.empty())
{
m_errorReporter.declarationError(
2904_error,
imp->location(),
"Declaration \"" +
alias.symbol->name() +
@ -208,6 +210,7 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions()
// Don't warn the user for what the user did not.
continue;
m_errorReporter.warning(
8261_error,
declaration->location(),
"Variable is shadowed in inline assembly by an instruction of the same name"
);
@ -326,6 +329,7 @@ void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
}
m_errorReporter.declarationError(
9097_error,
secondDeclarationLocation,
SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
"Identifier already declared."
@ -343,19 +347,19 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract)
UserDefinedTypeName const& baseName = baseSpecifier->name();
auto base = dynamic_cast<ContractDefinition const*>(baseName.annotation().referencedDeclaration);
if (!base)
m_errorReporter.fatalTypeError(baseName.location(), "Contract expected.");
m_errorReporter.fatalTypeError(8758_error, baseName.location(), "Contract expected.");
// "push_front" has the effect that bases mentioned later can overwrite members of bases
// mentioned earlier
input.back().push_front(base);
vector<ContractDefinition const*> const& basesBases = base->annotation().linearizedBaseContracts;
if (basesBases.empty())
m_errorReporter.fatalTypeError(baseName.location(), "Definition of base has to precede definition of derived contract");
m_errorReporter.fatalTypeError(2449_error, baseName.location(), "Definition of base has to precede definition of derived contract");
input.push_front(list<ContractDefinition const*>(basesBases.begin(), basesBases.end()));
}
input.back().push_front(&_contract);
vector<ContractDefinition const*> result = cThreeMerge(input);
if (result.empty())
m_errorReporter.fatalTypeError(_contract.location(), "Linearization of inheritance graph impossible");
m_errorReporter.fatalTypeError(5005_error, _contract.location(), "Linearization of inheritance graph impossible");
_contract.annotation().linearizedBaseContracts = result;
_contract.annotation().contractDependencies.insert(result.begin() + 1, result.end());
}
@ -476,6 +480,7 @@ bool DeclarationRegistrationHelper::registerDeclaration(
}
_errorReporter.declarationError(
2333_error,
secondDeclarationLocation,
SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
"Identifier already declared."
@ -486,6 +491,7 @@ bool DeclarationRegistrationHelper::registerDeclaration(
{
if (dynamic_cast<MagicVariableDeclaration const*>(shadowedDeclaration))
_errorReporter.warning(
2319_error,
*_errorLocation,
"This declaration shadows a builtin symbol."
);
@ -493,6 +499,7 @@ bool DeclarationRegistrationHelper::registerDeclaration(
{
auto shadowedLocation = shadowedDeclaration->location();
_errorReporter.warning(
2519_error,
_declaration.location(),
"This declaration shadows an existing declaration.",
SecondarySourceLocation().append("The shadowed declaration is here:", shadowedLocation)

View File

@ -27,7 +27,6 @@
#include <liblangutil/ErrorReporter.h>
#include <libsolutil/Visitor.h>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/algorithm/string/predicate.hpp>
@ -461,6 +460,7 @@ void OverrideChecker::checkIllegalOverrides(ContractDefinition const& _contract)
{
if (contains_if(inheritedFuncs, MatchByName{modifier->name()}))
m_errorReporter.typeError(
5631_error,
modifier->location(),
"Override changes function or public state variable to modifier."
);
@ -474,7 +474,7 @@ void OverrideChecker::checkIllegalOverrides(ContractDefinition const& _contract)
continue;
if (contains_if(inheritedMods, MatchByName{function->name()}))
m_errorReporter.typeError(function->location(), "Override changes modifier to function.");
m_errorReporter.typeError(1469_error, function->location(), "Override changes modifier to function.");
checkOverrideList(OverrideProxy{function}, inheritedFuncs);
}
@ -484,7 +484,7 @@ void OverrideChecker::checkIllegalOverrides(ContractDefinition const& _contract)
continue;
if (contains_if(inheritedMods, MatchByName{stateVar->name()}))
m_errorReporter.typeError(stateVar->location(), "Override changes modifier to public state variable.");
m_errorReporter.typeError(1456_error, stateVar->location(), "Override changes modifier to public state variable.");
checkOverrideList(OverrideProxy{stateVar}, inheritedFuncs);
}
@ -500,17 +500,19 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
if (_overriding.isModifier() && *_overriding.modifierType() != *_super.modifierType())
m_errorReporter.typeError(
1078_error,
_overriding.location(),
"Override changes modifier signature."
);
if (!_overriding.overrides())
overrideError(_overriding, _super, "Overriding " + _overriding.astNodeName() + " is missing \"override\" specifier.");
overrideError(_overriding, _super, 9456_error, "Overriding " + _overriding.astNodeName() + " is missing \"override\" specifier.");
if (_super.isVariable())
overrideError(
_super,
_overriding,
1452_error,
"Cannot override public state variable.",
"Overriding " + _overriding.astNodeName() + " is here:"
);
@ -518,6 +520,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
overrideError(
_super,
_overriding,
4334_error,
"Trying to override non-virtual " + _super.astNodeName() + ". Did you forget to add \"virtual\"?",
"Overriding " + _overriding.astNodeName() + " is here:"
);
@ -525,7 +528,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
if (_overriding.isVariable())
{
if (_super.visibility() != Visibility::External)
overrideError(_overriding, _super, "Public state variables can only override functions with external visibility.");
overrideError(_overriding, _super, 5225_error, "Public state variables can only override functions with external visibility.");
solAssert(_overriding.visibility() == Visibility::External, "");
}
else if (_overriding.visibility() != _super.visibility())
@ -536,7 +539,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
_super.visibility() == Visibility::External &&
_overriding.visibility() == Visibility::Public
))
overrideError(_overriding, _super, "Overriding " + _overriding.astNodeName() + " visibility differs.");
overrideError(_overriding, _super, 9098_error, "Overriding " + _overriding.astNodeName() + " visibility differs.");
}
if (_super.isFunction())
@ -547,7 +550,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
solAssert(functionType->hasEqualParameterTypes(*superType), "Override doesn't have equal parameters!");
if (!functionType->hasEqualReturnTypes(*superType))
overrideError(_overriding, _super, "Overriding " + _overriding.astNodeName() + " return types differ.");
overrideError(_overriding, _super, 4822_error, "Overriding " + _overriding.astNodeName() + " return types differ.");
// This is only relevant for a function overriding a function.
if (_overriding.isFunction())
@ -556,6 +559,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
overrideError(
_overriding,
_super,
2837_error,
"Overriding function changes state mutability from \"" +
stateMutabilityToString(_super.stateMutability()) +
"\" to \"" +
@ -567,6 +571,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
overrideError(
_overriding,
_super,
4593_error,
"Overriding an implemented function with an unimplemented function is not allowed."
);
}
@ -576,6 +581,7 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
void OverrideChecker::overrideListError(
OverrideProxy const& _item,
set<ContractDefinition const*, CompareByID> _secondary,
ErrorId _error,
string const& _message1,
string const& _message2
)
@ -593,6 +599,7 @@ void OverrideChecker::overrideListError(
contractSingularPlural = "contracts ";
m_errorReporter.typeError(
_error,
_item.overrides() ? _item.overrides()->location() : _item.location(),
ssl,
_message1 +
@ -603,9 +610,10 @@ void OverrideChecker::overrideListError(
);
}
void OverrideChecker::overrideError(Declaration const& _overriding, Declaration const& _super, string const& _message, string const& _secondaryMsg)
void OverrideChecker::overrideError(Declaration const& _overriding, Declaration const& _super, ErrorId _error, string const& _message, string const& _secondaryMsg)
{
m_errorReporter.typeError(
_error,
_overriding.location(),
SecondarySourceLocation().append(_secondaryMsg, _super.location()),
_message
@ -613,9 +621,10 @@ void OverrideChecker::overrideError(Declaration const& _overriding, Declaration
}
void OverrideChecker::overrideError(OverrideProxy const& _overriding, OverrideProxy const& _super, string const& _message, string const& _secondaryMsg)
void OverrideChecker::overrideError(OverrideProxy const& _overriding, OverrideProxy const& _super, ErrorId _error, string const& _message, string const& _secondaryMsg)
{
m_errorReporter.typeError(
_error,
_overriding.location(),
SecondarySourceLocation().append(_secondaryMsg, _super.location()),
_message
@ -718,7 +727,7 @@ void OverrideChecker::checkAmbiguousOverridesInternal(set<OverrideProxy> _baseCa
" Since one of the bases defines a public state variable which cannot be overridden, "
"you have to change the inheritance layout or the names of the functions.";
m_errorReporter.typeError(_location, ssl, message);
m_errorReporter.typeError(6480_error, _location, ssl, message);
}
set<ContractDefinition const*, OverrideChecker::CompareByID> OverrideChecker::resolveOverrideList(OverrideSpecifier const& _overrides) const
@ -766,13 +775,14 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign
SecondarySourceLocation ssl;
ssl.append("First occurrence here: ", list[i-1]->location());
m_errorReporter.typeError(
4520_error,
list[i]->location(),
ssl,
"Duplicate contract \"" +
joinHumanReadable(list[i]->namePath(), ".") +
"\" found in override list of \"" +
_item.name() +
"\"."
"Duplicate contract \"" +
joinHumanReadable(list[i]->namePath(), ".") +
"\" found in override list of \"" +
_item.name() +
"\"."
);
}
}
@ -791,6 +801,7 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign
if (_item.overrides() && expectedContracts.empty())
m_errorReporter.typeError(
7792_error,
_item.overrides()->location(),
_item.astNodeNameCapitalized() + " has override specified but does not override anything."
);
@ -804,6 +815,7 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign
overrideListError(
_item,
missingContracts,
4327_error,
_item.astNodeNameCapitalized() + " needs to specify overridden ",
""
);
@ -813,6 +825,7 @@ void OverrideChecker::checkOverrideList(OverrideProxy _item, OverrideProxyBySign
overrideListError(
_item,
surplusContracts,
2353_error,
"Invalid ",
"specified in override list: "
);

View File

@ -32,6 +32,7 @@
namespace solidity::langutil
{
class ErrorReporter;
struct ErrorId;
struct SourceLocation;
}
@ -160,18 +161,21 @@ private:
void overrideListError(
OverrideProxy const& _item,
std::set<ContractDefinition const*, CompareByID> _secondary,
langutil::ErrorId _error,
std::string const& _message1,
std::string const& _message2
);
void overrideError(
Declaration const& _overriding,
Declaration const& _super,
langutil::ErrorId _error,
std::string const& _message,
std::string const& _secondaryMsg = "Overridden function is here:"
);
void overrideError(
OverrideProxy const& _overriding,
OverrideProxy const& _super,
langutil::ErrorId _error,
std::string const& _message,
std::string const& _secondaryMsg = "Overridden function is here:"
);

View File

@ -122,6 +122,7 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker
for (auto declaration: m_constVariables)
if (auto identifier = findCycle(*declaration))
m_errorReporter.typeError(
6161_error,
declaration->location(),
"The value of the constant " + declaration->name() +
" has a cyclic dependency via " + identifier->name() + "."
@ -165,7 +166,7 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker
auto visitor = [&](VariableDeclaration const& _variable, util::CycleDetector<VariableDeclaration>& _cycleDetector, size_t _depth)
{
if (_depth >= 256)
m_errorReporter.fatalDeclarationError(_variable.location(), "Variable definition exhausting cyclic dependency validator.");
m_errorReporter.fatalDeclarationError(7380_error, _variable.location(), "Variable definition exhausting cyclic dependency validator.");
// Iterating through the dependencies needs to be deterministic and thus cannot
// depend on the memory layout.
@ -209,6 +210,7 @@ struct OverrideSpecifierChecker: public PostTypeChecker::Checker
TypeType const* actualTypeType = dynamic_cast<TypeType const*>(decl->type());
m_errorReporter.typeError(
9301_error,
override->location(),
"Expected contract but got " +
actualTypeType->actualType()->toString(true) +
@ -243,6 +245,7 @@ struct ModifierContextChecker: public PostTypeChecker::Checker
if (ModifierType const* type = dynamic_cast<decltype(type)>(_identifier.annotation().type))
{
m_errorReporter.typeError(
3112_error,
_identifier.location(),
"Modifier can only be referenced in function headers."
);
@ -280,6 +283,7 @@ struct EventOutsideEmitChecker: public PostTypeChecker::Checker
// Check for event outside of emit statement
if (!m_insideEmitStatement && functionType->kind() == FunctionType::Kind::Event)
m_errorReporter.typeError(
3132_error,
_functionCall.location(),
"Event invocations have to be prefixed by \"emit\"."
);
@ -308,7 +312,7 @@ struct NoVariablesInInterfaceChecker: public PostTypeChecker::Checker
&& !_variable.isCallableOrCatchParameter()
&& !m_insideStruct
)
m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces.");
m_errorReporter.typeError(8274_error, _variable.location(), "Variables cannot be declared in interfaces.");
return true;
}

View File

@ -268,19 +268,19 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
void ReferencesResolver::declarationError(SourceLocation const& _location, string const& _description)
{
m_errorOccurred = true;
m_errorReporter.declarationError(_location, _description);
m_errorReporter.declarationError(8532_error, _location, _description);
}
void ReferencesResolver::declarationError(SourceLocation const& _location, SecondarySourceLocation const& _ssl, string const& _description)
{
m_errorOccurred = true;
m_errorReporter.declarationError(_location, _ssl, _description);
m_errorReporter.declarationError(3881_error, _location, _ssl, _description);
}
void ReferencesResolver::fatalDeclarationError(SourceLocation const& _location, string const& _description)
{
m_errorOccurred = true;
m_errorReporter.fatalDeclarationError(_location, _description);
m_errorReporter.fatalDeclarationError(6546_error, _location, _description);
}
}

View File

@ -120,13 +120,14 @@ void StaticAnalyzer::endVisit(FunctionDefinition const&)
{
if (var.first.second->isCallableOrCatchParameter())
m_errorReporter.warning(
5667_error,
var.first.second->location(),
"Unused " +
string(var.first.second->isTryCatchParameter() ? "try/catch" : "function") +
" parameter. Remove or comment out the variable name to silence this warning."
);
else
m_errorReporter.warning(var.first.second->location(), "Unused local variable.");
m_errorReporter.warning(2072_error, var.first.second->location(), "Unused local variable.");
}
m_localVarUseCount.clear();
m_constructor = false;
@ -159,6 +160,7 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable)
set<StructDefinition const*> structsSeen;
if (structureSizeEstimate(*_variable.type(), structsSeen) >= bigint(1) << 64)
m_errorReporter.warning(
3408_error,
_variable.location(),
"Variable covers a large part of storage and thus makes collisions likely. "
"Either use mappings or dynamic arrays and allow their size to be increased only "
@ -183,6 +185,7 @@ bool StaticAnalyzer::visit(ExpressionStatement const& _statement)
{
if (_statement.expression().annotation().isPure)
m_errorReporter.warning(
6133_error,
_statement.location(),
"Statement has no effect."
);
@ -196,11 +199,13 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
{
if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "gas")
m_errorReporter.typeError(
1400_error,
_memberAccess.location(),
"\"msg.gas\" has been deprecated in favor of \"gasleft()\""
);
else if (type->kind() == MagicType::Kind::Block && _memberAccess.memberName() == "blockhash")
m_errorReporter.typeError(
8113_error,
_memberAccess.location(),
"\"block.blockhash()\" has been deprecated in favor of \"blockhash()\""
);
@ -211,6 +216,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
ContractType const& contract = dynamic_cast<ContractType const&>(*type->typeArgument());
if (m_constructorUsesAssembly->check(contract.contractDefinition()))
m_errorReporter.warning(
6417_error,
_memberAccess.location(),
"The constructor of the contract (or its base) uses inline assembly. "
"Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode."
@ -222,6 +228,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
if (auto const* type = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type))
if (type->kind() == FunctionType::Kind::BareCallCode)
m_errorReporter.typeError(
2256_error,
_memberAccess.location(),
"\"callcode\" has been deprecated in favour of \"delegatecall\"."
);
@ -235,6 +242,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
{
if (id->name() == "this")
m_errorReporter.warning(
5805_error,
id->location(),
"\"this\" used in constructor. "
"Note that external functions of a contract "
@ -285,6 +293,7 @@ bool StaticAnalyzer::visit(BinaryOperation const& _operation)
))
if (rhs->isZero())
m_errorReporter.typeError(
1211_error,
_operation.location(),
(_operation.getOperator() == Token::Div) ? "Division by zero." : "Modulo zero."
);
@ -307,6 +316,7 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall)
))
if (lastArg->isZero())
m_errorReporter.typeError(
4195_error,
_functionCall.location(),
"Arithmetic modulo zero."
);
@ -317,6 +327,7 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall)
functionType->declaration().scope() == m_currentContract
)
m_errorReporter.typeError(
6700_error,
_functionCall.location(),
SecondarySourceLocation().append(
"The function declaration is here:",

View File

@ -27,7 +27,6 @@
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/SemVerHandler.h>
#include <boost/algorithm/cxx11/all_of.hpp>
#include <boost/algorithm/string.hpp>
#include <memory>
@ -69,7 +68,7 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit)
string(";\"");
// when reporting the warning, print the source name only
m_errorReporter.warning({-1, -1, _sourceUnit.location().source}, errorString);
m_errorReporter.warning(3420_error, {-1, -1, _sourceUnit.location().source}, errorString);
}
m_sourceUnit = nullptr;
}
@ -79,18 +78,20 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
solAssert(!_pragma.tokens().empty(), "");
solAssert(_pragma.tokens().size() == _pragma.literals().size(), "");
if (_pragma.tokens()[0] != Token::Identifier)
m_errorReporter.syntaxError(_pragma.location(), "Invalid pragma \"" + _pragma.literals()[0] + "\"");
m_errorReporter.syntaxError(5226_error, _pragma.location(), "Invalid pragma \"" + _pragma.literals()[0] + "\"");
else if (_pragma.literals()[0] == "experimental")
{
solAssert(m_sourceUnit, "");
vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end());
if (literals.empty())
m_errorReporter.syntaxError(
9679_error,
_pragma.location(),
"Experimental feature name is missing."
);
else if (literals.size() > 1)
m_errorReporter.syntaxError(
6022_error,
_pragma.location(),
"Stray arguments."
);
@ -98,17 +99,17 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
{
string const literal = literals[0];
if (literal.empty())
m_errorReporter.syntaxError(_pragma.location(), "Empty experimental feature name is invalid.");
m_errorReporter.syntaxError(3250_error, _pragma.location(), "Empty experimental feature name is invalid.");
else if (!ExperimentalFeatureNames.count(literal))
m_errorReporter.syntaxError(_pragma.location(), "Unsupported experimental feature name.");
m_errorReporter.syntaxError(8491_error, _pragma.location(), "Unsupported experimental feature name.");
else if (m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeatureNames.at(literal)))
m_errorReporter.syntaxError(_pragma.location(), "Duplicate experimental feature name.");
m_errorReporter.syntaxError(1231_error, _pragma.location(), "Duplicate experimental feature name.");
else
{
auto feature = ExperimentalFeatureNames.at(literal);
m_sourceUnit->annotation().experimentalFeatures.insert(feature);
if (!ExperimentalFeatureWithoutWarning.count(feature))
m_errorReporter.warning(_pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments.");
m_errorReporter.warning(2264_error, _pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments.");
}
}
}
@ -121,15 +122,16 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
static SemVerVersion const currentVersion{string(VersionString)};
if (!matchExpression.matches(currentVersion))
m_errorReporter.syntaxError(
3997_error,
_pragma.location(),
"Source file requires different compiler version (current compiler is " +
string(VersionString) + " - note that nightly builds are considered to be "
string(VersionString) + ") - note that nightly builds are considered to be "
"strictly less than the released version"
);
m_versionPragmaFound = true;
}
else
m_errorReporter.syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\"");
m_errorReporter.syntaxError(4936_error, _pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\"");
return true;
}
@ -142,7 +144,7 @@ bool SyntaxChecker::visit(ModifierDefinition const&)
void SyntaxChecker::endVisit(ModifierDefinition const& _modifier)
{
if (_modifier.isImplemented() && !m_placeholderFound)
m_errorReporter.syntaxError(_modifier.body().location(), "Modifier body does not contain '_'.");
m_errorReporter.syntaxError(2883_error, _modifier.body().location(), "Modifier body does not contain '_'.");
m_placeholderFound = false;
}
@ -150,7 +152,7 @@ void SyntaxChecker::checkSingleStatementVariableDeclaration(ASTNode const& _stat
{
auto varDecl = dynamic_cast<VariableDeclarationStatement const*>(&_statement);
if (varDecl)
m_errorReporter.syntaxError(_statement.location(), "Variable declarations can only be used inside blocks.");
m_errorReporter.syntaxError(9079_error, _statement.location(), "Variable declarations can only be used inside blocks.");
}
bool SyntaxChecker::visit(IfStatement const& _ifStatement)
@ -189,7 +191,7 @@ bool SyntaxChecker::visit(Continue const& _continueStatement)
{
if (m_inLoopDepth <= 0)
// we're not in a for/while loop, report syntax error
m_errorReporter.syntaxError(_continueStatement.location(), "\"continue\" has to be in a \"for\" or \"while\" loop.");
m_errorReporter.syntaxError(4123_error, _continueStatement.location(), "\"continue\" has to be in a \"for\" or \"while\" loop.");
return true;
}
@ -197,13 +199,14 @@ bool SyntaxChecker::visit(Break const& _breakStatement)
{
if (m_inLoopDepth <= 0)
// we're not in a for/while loop, report syntax error
m_errorReporter.syntaxError(_breakStatement.location(), "\"break\" has to be in a \"for\" or \"while\" loop.");
m_errorReporter.syntaxError(6102_error, _breakStatement.location(), "\"break\" has to be in a \"for\" or \"while\" loop.");
return true;
}
bool SyntaxChecker::visit(Throw const& _throwStatement)
{
m_errorReporter.syntaxError(
4538_error,
_throwStatement.location(),
"\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"."
);
@ -222,29 +225,29 @@ bool SyntaxChecker::visit(Literal const& _literal)
// Generic checks no matter what base this number literal is of:
if (value.back() == '_')
{
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No trailing underscores allowed.");
m_errorReporter.syntaxError(2090_error, _literal.location(), "Invalid use of underscores in number literal. No trailing underscores allowed.");
return true;
}
if (value.find("__") != ASTString::npos)
{
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed.");
m_errorReporter.syntaxError(2990_error, _literal.location(), "Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed.");
return true;
}
if (!_literal.isHexNumber()) // decimal literal
{
if (value.find("._") != ASTString::npos)
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed.");
m_errorReporter.syntaxError(3891_error, _literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed.");
if (value.find("_.") != ASTString::npos)
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed.");
m_errorReporter.syntaxError(1023_error, _literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed.");
if (value.find("_e") != ASTString::npos)
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscore at the end of the mantissa allowed.");
m_errorReporter.syntaxError(6415_error, _literal.location(), "Invalid use of underscores in number literal. No underscore at the end of the mantissa allowed.");
if (value.find("e_") != ASTString::npos)
m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscore in front of exponent allowed.");
m_errorReporter.syntaxError(6165_error, _literal.location(), "Invalid use of underscores in number literal. No underscore in front of exponent allowed.");
}
return true;
@ -253,7 +256,7 @@ bool SyntaxChecker::visit(Literal const& _literal)
bool SyntaxChecker::visit(UnaryOperation const& _operation)
{
if (_operation.getOperator() == Token::Add)
m_errorReporter.syntaxError(_operation.location(), "Use of unary + is disallowed.");
m_errorReporter.syntaxError(9636_error, _operation.location(), "Use of unary + is disallowed.");
return true;
}
@ -265,6 +268,7 @@ bool SyntaxChecker::visit(InlineAssembly const& _inlineAssembly)
if (yul::MSizeFinder::containsMSize(_inlineAssembly.dialect(), _inlineAssembly.operations()))
m_errorReporter.syntaxError(
6553_error,
_inlineAssembly.location(),
"The msize instruction cannot be used when the Yul optimizer is activated because "
"it can change its semantics. Either disable the Yul optimizer or do not use the instruction."
@ -285,7 +289,9 @@ bool SyntaxChecker::visit(ContractDefinition const& _contract)
ASTString const& contractName = _contract.name();
for (FunctionDefinition const* function: _contract.definedFunctions())
if (function->name() == contractName)
m_errorReporter.syntaxError(function->location(),
m_errorReporter.syntaxError(
5796_error,
function->location(),
"Functions are not allowed to have the same name as the contract. "
"If you intend this to be a constructor, use \"constructor(...) { ... }\" to define it."
);
@ -298,15 +304,16 @@ bool SyntaxChecker::visit(FunctionDefinition const& _function)
{
string suggestedVisibility = _function.isFallback() || _function.isReceive() || m_isInterface ? "external" : "public";
m_errorReporter.syntaxError(
4937_error,
_function.location(),
"No visibility specified. Did you intend to add \"" + suggestedVisibility + "\"?"
);
}
if (m_isInterface && !_function.modifiers().empty())
m_errorReporter.syntaxError(_function.location(), "Functions in interfaces cannot have modifiers.");
m_errorReporter.syntaxError(5842_error, _function.location(), "Functions in interfaces cannot have modifiers.");
else if (!_function.isImplemented() && !_function.modifiers().empty())
m_errorReporter.syntaxError(_function.location(), "Functions without implementation cannot have modifiers.");
m_errorReporter.syntaxError(2668_error, _function.location(), "Functions without implementation cannot have modifiers.");
return true;
}
@ -315,11 +322,11 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node)
{
for (auto const& decl: _node.parameterTypeList()->parameters())
if (!decl->name().empty())
m_errorReporter.warning(decl->location(), "Naming function type parameters is deprecated.");
m_errorReporter.warning(6162_error, decl->location(), "Naming function type parameters is deprecated.");
for (auto const& decl: _node.returnParameterTypeList()->parameters())
if (!decl->name().empty())
m_errorReporter.syntaxError(decl->location(), "Return parameters in function types may not be named.");
m_errorReporter.syntaxError(7304_error, decl->location(), "Return parameters in function types may not be named.");
return true;
}
@ -327,8 +334,13 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node)
bool SyntaxChecker::visit(VariableDeclarationStatement const& _statement)
{
// Report if none of the variable components in the tuple have a name (only possible via deprecated "var")
if (boost::algorithm::all_of_equal(_statement.declarations(), nullptr))
if (std::all_of(
_statement.declarations().begin(),
_statement.declarations().end(),
[](ASTPointer<VariableDeclaration> const& declaration) { return declaration == nullptr; }
))
m_errorReporter.syntaxError(
3299_error,
_statement.location(),
"The use of the \"var\" keyword is disallowed. The declaration part of the statement can be removed, since it is empty."
);
@ -339,7 +351,7 @@ bool SyntaxChecker::visit(VariableDeclarationStatement const& _statement)
bool SyntaxChecker::visit(StructDefinition const& _struct)
{
if (_struct.members().empty())
m_errorReporter.syntaxError(_struct.location(), "Defining empty structs is disallowed.");
m_errorReporter.syntaxError(5306_error, _struct.location(), "Defining empty structs is disallowed.");
return true;
}

File diff suppressed because it is too large Load Diff

View File

@ -171,6 +171,7 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef)
!_funDef.overrides()
)
m_errorReporter.warning(
2018_error,
_funDef.location(),
"Function state mutability can be restricted to " + stateMutabilityToString(m_bestMutabilityAndLocation.mutability)
);
@ -229,7 +230,7 @@ void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly)
{
AssemblyViewPureChecker{
_inlineAssembly.dialect(),
[=](StateMutability _mutability, SourceLocation const& _location) { reportMutability(_mutability, _location); }
[&](StateMutability _mutability, SourceLocation const& _location) { reportMutability(_mutability, _location); }
}(_inlineAssembly.operations());
}
@ -252,6 +253,7 @@ void ViewPureChecker::reportMutability(
))
{
m_errorReporter.typeError(
2527_error,
_location,
"Function declared as pure, but this expression (potentially) reads from the "
"environment or state and thus requires \"view\"."
@ -261,6 +263,7 @@ void ViewPureChecker::reportMutability(
else if (_mutability == StateMutability::NonPayable)
{
m_errorReporter.typeError(
8961_error,
_location,
"Function declared as " +
stateMutabilityToString(m_currentFunction->stateMutability()) +
@ -277,12 +280,14 @@ void ViewPureChecker::reportMutability(
{
if (_nestedLocation)
m_errorReporter.typeError(
4006_error,
_location,
SecondarySourceLocation().append("\"msg.value\" or \"callvalue()\" appear here inside the modifier.", *_nestedLocation),
"This modifier uses \"msg.value\" or \"callvalue()\" and thus the function has to be payable or internal."
);
else
m_errorReporter.typeError(
5887_error,
_location,
"\"msg.value\" and \"callvalue()\" can only be used in payable public functions. Make the function "
"\"payable\" or use an internal function to avoid this error."
@ -357,6 +362,8 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
{MagicType::Kind::MetaType, "runtimeCode"},
{MagicType::Kind::MetaType, "name"},
{MagicType::Kind::MetaType, "interfaceId"},
{MagicType::Kind::MetaType, "min"},
{MagicType::Kind::MetaType, "max"},
};
set<MagicMember> static const payableMembers{
{MagicType::Kind::Message, "value"}

View File

@ -556,7 +556,13 @@ MagicType const* TypeProvider::magic(MagicType::Kind _kind)
MagicType const* TypeProvider::meta(Type const* _type)
{
solAssert(_type && _type->category() == Type::Category::Contract, "Only contracts supported for now.");
solAssert(
_type && (
_type->category() == Type::Category::Contract ||
_type->category() == Type::Category::Integer
),
"Only contracts or integer types supported for now."
);
return createAndGet<MagicType>(_type);
}

View File

@ -556,6 +556,22 @@ string IntegerType::toString(bool) const
return prefix + util::toString(m_bits);
}
u256 IntegerType::min() const
{
if (isSigned())
return s2u(s256(minValue()));
else
return u256(minValue());
}
u256 IntegerType::max() const
{
if (isSigned())
return s2u(s256(maxValue()));
else
return u256(maxValue());
}
bigint IntegerType::minValue() const
{
if (isSigned())
@ -3763,20 +3779,35 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
case Kind::MetaType:
{
solAssert(
m_typeArgument && m_typeArgument->category() == Type::Category::Contract,
"Only contracts supported for now"
m_typeArgument && (
m_typeArgument->category() == Type::Category::Contract ||
m_typeArgument->category() == Type::Category::Integer
),
"Only contracts or integer types supported for now"
);
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_typeArgument).contractDefinition();
if (contract.canBeDeployed())
if (m_typeArgument->category() == Type::Category::Contract)
{
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_typeArgument).contractDefinition();
if (contract.canBeDeployed())
return MemberList::MemberMap({
{"creationCode", TypeProvider::array(DataLocation::Memory)},
{"runtimeCode", TypeProvider::array(DataLocation::Memory)},
{"name", TypeProvider::stringMemory()},
});
else
return MemberList::MemberMap({
{"interfaceId", TypeProvider::fixedBytes(4)},
});
}
else if (m_typeArgument->category() == Type::Category::Integer)
{
IntegerType const* integerTypePointer = dynamic_cast<IntegerType const*>(m_typeArgument);
return MemberList::MemberMap({
{"creationCode", TypeProvider::array(DataLocation::Memory)},
{"runtimeCode", TypeProvider::array(DataLocation::Memory)},
{"name", TypeProvider::stringMemory()},
});
else
return MemberList::MemberMap({
{"interfaceId", TypeProvider::fixedBytes(4)},
{"min", integerTypePointer},
{"max", integerTypePointer},
});
}
}
}
solAssert(false, "Unknown kind of magic.");

View File

@ -452,6 +452,9 @@ public:
unsigned numBits() const { return m_bits; }
bool isSigned() const { return m_modifier == Modifier::Signed; }
u256 min() const;
u256 max() const;
bigint minValue() const;
bigint maxValue() const;

View File

@ -28,7 +28,6 @@
#include <libsolutil/StringUtils.h>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/reversed.hpp>
using namespace std;
using namespace solidity;
@ -38,7 +37,8 @@ using namespace solidity::frontend;
string ABIFunctions::tupleEncoder(
TypePointers const& _givenTypes,
TypePointers const& _targetTypes,
bool _encodeAsLibraryTypes
bool _encodeAsLibraryTypes,
bool _reversed
)
{
EncodingOptions options;
@ -54,6 +54,8 @@ string ABIFunctions::tupleEncoder(
for (auto const& t: _targetTypes)
functionName += t->identifier() + "_";
functionName += options.toFunctionNameSuffix();
if (_reversed)
functionName += "_reversed";
return createFunction(functionName, [&]() {
// Note that the values are in reverse due to the difference in calling semantics.
@ -94,7 +96,10 @@ string ABIFunctions::tupleEncoder(
stackPos += sizeOnStack;
}
solAssert(headPos == headSize_, "");
string valueParams = suffixedVariableNameList("value", stackPos, 0);
string valueParams =
_reversed ?
suffixedVariableNameList("value", stackPos, 0) :
suffixedVariableNameList("value", 0, stackPos);
templ("valueParams", valueParams.empty() ? "" : ", " + valueParams);
templ("encodeElements", encodeElements);
@ -104,7 +109,8 @@ string ABIFunctions::tupleEncoder(
string ABIFunctions::tupleEncoderPacked(
TypePointers const& _givenTypes,
TypePointers const& _targetTypes
TypePointers const& _targetTypes,
bool _reversed
)
{
EncodingOptions options;
@ -120,6 +126,8 @@ string ABIFunctions::tupleEncoderPacked(
for (auto const& t: _targetTypes)
functionName += t->identifier() + "_";
functionName += options.toFunctionNameSuffix();
if (_reversed)
functionName += "_reversed";
return createFunction(functionName, [&]() {
solAssert(!_givenTypes.empty(), "");
@ -158,7 +166,10 @@ string ABIFunctions::tupleEncoderPacked(
encodeElements += elementTempl.render();
stackPos += sizeOnStack;
}
string valueParams = suffixedVariableNameList("value", stackPos, 0);
string valueParams =
_reversed ?
suffixedVariableNameList("value", stackPos, 0) :
suffixedVariableNameList("value", 0, stackPos);
templ("valueParams", valueParams.empty() ? "" : ", " + valueParams);
templ("encodeElements", encodeElements);

View File

@ -67,31 +67,53 @@ public:
/// @returns name of an assembly function to ABI-encode values of @a _givenTypes
/// into memory, converting the types to @a _targetTypes on the fly.
/// Parameters are: <headStart> <value_n> ... <value_1>, i.e.
/// the layout on the stack is <value_1> ... <value_n> <headStart> with
/// Parameters are: <headStart> <value_1> ... <value_n>, i.e.
/// the layout on the stack is <value_n> ... <value_1> <headStart> with
/// the top of the stack on the right.
/// The values represent stack slots. If a type occupies more or less than one
/// stack slot, it takes exactly that number of values.
/// Returns a pointer to the end of the area written in memory.
/// Does not allocate memory (does not change the free memory pointer), but writes
/// to memory starting at $headStart and an unrestricted amount after that.
/// If @reversed is true, the order of the variables after <headStart> is reversed.
std::string tupleEncoder(
TypePointers const& _givenTypes,
TypePointers const& _targetTypes,
bool _encodeAsLibraryTypes = false
bool _encodeAsLibraryTypes = false,
bool _reversed = false
);
/// Specialization of tupleEncoder to _reversed = true
std::string tupleEncoderReversed(
TypePointers const& _givenTypes,
TypePointers const& _targetTypes,
bool _encodeAsLibraryTypes = false
) {
return tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes, true);
}
/// @returns name of an assembly function to encode values of @a _givenTypes
/// with packed encoding into memory, converting the types to @a _targetTypes on the fly.
/// Parameters are: <memPos> <value_n> ... <value_1>, i.e.
/// the layout on the stack is <value_1> ... <value_n> <memPos> with
/// Parameters are: <memPos> <value_1> ... <value_n>, i.e.
/// the layout on the stack is <value_n> ... <value_1> <memPos> with
/// the top of the stack on the right.
/// The values represent stack slots. If a type occupies more or less than one
/// stack slot, it takes exactly that number of values.
/// Returns a pointer to the end of the area written in memory.
/// Does not allocate memory (does not change the free memory pointer), but writes
/// to memory starting at memPos and an unrestricted amount after that.
std::string tupleEncoderPacked(TypePointers const& _givenTypes, TypePointers const& _targetTypes);
/// If @reversed is true, the order of the variables after <headStart> is reversed.
std::string tupleEncoderPacked(
TypePointers const& _givenTypes,
TypePointers const& _targetTypes,
bool _reversed = false
);
/// Specialization of tupleEncoderPacked to _reversed = true
std::string tupleEncoderPackedReversed(TypePointers const& _givenTypes, TypePointers const& _targetTypes)
{
return tupleEncoderPacked(_givenTypes, _targetTypes, true);
}
/// @returns name of an assembly function to ABI-decode values of @a _types
/// into memory. If @a _fromMemory is true, decodes from memory instead of

View File

@ -49,6 +49,9 @@ void Compiler::compileContract(
m_runtimeSub = creationCompiler.compileConstructor(_contract, _otherCompilers);
m_context.optimise(m_optimiserSettings);
solAssert(m_context.requestedYulFunctionsRan(), "requestedYulFunctions() was not called.");
solAssert(m_runtimeContext.requestedYulFunctionsRan(), "requestedYulFunctions() was not called.");
}
std::shared_ptr<evmasm::Assembly> Compiler::runtimeAssemblyPtr() const

View File

@ -192,6 +192,9 @@ void CompilerContext::appendMissingLowLevelFunctions()
pair<string, set<string>> CompilerContext::requestedYulFunctions()
{
solAssert(!m_requestedYulFunctionsRan, "requestedYulFunctions called more than once.");
m_requestedYulFunctionsRan = true;
set<string> empty;
swap(empty, m_externallyUsedYulFunctions);
return {m_yulFunctionCollector.requestedFunctions(), std::move(empty)};

View File

@ -167,6 +167,7 @@ public:
/// Clears the internal list, i.e. calling it again will result in an
/// empty return value.
std::pair<std::string, std::set<std::string>> requestedYulFunctions();
bool requestedYulFunctionsRan() const { return m_requestedYulFunctionsRan; }
/// Returns the distance of the given local variable from the bottom of the stack (of the current function).
unsigned baseStackOffsetOfVariable(Declaration const& _declaration) const;
@ -389,6 +390,8 @@ private:
YulUtilFunctions m_yulUtilFunctions;
/// The queue of low-level functions to generate.
std::queue<std::tuple<std::string, unsigned, unsigned, std::function<void(CompilerContext&)>>> m_lowLevelFunctionGenerationQueue;
/// Flag to check that requestedYulFunctions() was called exactly once
bool m_requestedYulFunctionsRan = false;
};
}

View File

@ -559,8 +559,8 @@ void CompilerUtils::abiEncodeV2(
string encoderName =
_padToWordBoundaries ?
m_context.abiFunctions().tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes) :
m_context.abiFunctions().tupleEncoderPacked(_givenTypes, _targetTypes);
m_context.abiFunctions().tupleEncoderReversed(_givenTypes, _targetTypes, _encodeAsLibraryTypes) :
m_context.abiFunctions().tupleEncoderPackedReversed(_givenTypes, _targetTypes);
m_context.callYulFunction(encoderName, sizeOnStack(_givenTypes) + 1, 1);
}

View File

@ -103,8 +103,6 @@ void ContractCompiler::compileContract(
// and adds the function to the compilation queue. Additionally internal functions,
// which are referenced directly or indirectly will be added.
appendFunctionSelector(_contract);
// This processes the above populated queue until it is empty.
appendMissingFunctions();
}
size_t ContractCompiler::compileConstructor(
@ -159,10 +157,13 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c
if (FunctionDefinition const* constructor = _contract.constructor())
appendConstructor(*constructor);
else if (auto c = _contract.nextConstructor(m_context.mostDerivedContract()))
appendBaseConstructor(*c);
else
{
// Implicit constructors are always non-payable.
appendCallValueCheck();
if (auto c = _contract.nextConstructor(m_context.mostDerivedContract()))
appendBaseConstructor(*c);
}
}
size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _contract)
@ -215,6 +216,9 @@ size_t ContractCompiler::deployLibrary(ContractDefinition const& _contract)
solAssert(!!m_runtimeCompiler, "");
solAssert(_contract.isLibrary(), "Tried to deploy contract as library.");
appendMissingFunctions();
m_runtimeCompiler->appendMissingFunctions();
CompilerContext::LocationSetter locationSetter(m_context, _contract);
solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered");
@ -586,13 +590,13 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
if (!_function.isConstructor())
// adding 1 for return address.
m_context.adjustStackOffset(parametersSize + 1);
for (ASTPointer<VariableDeclaration const> const& variable: _function.parameters())
for (ASTPointer<VariableDeclaration> const& variable: _function.parameters())
{
m_context.addVariable(*variable, parametersSize);
parametersSize -= variable->annotation().type->sizeOnStack();
}
for (ASTPointer<VariableDeclaration const> const& variable: _function.returnParameters())
for (ASTPointer<VariableDeclaration> const& variable: _function.returnParameters())
appendStackVariableInitialisation(*variable);
if (_function.isConstructor())
@ -649,7 +653,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
if (stackLayout[i] != i)
solAssert(false, "Invalid stack layout on cleanup.");
for (ASTPointer<VariableDeclaration const> const& variable: _function.parameters() + _function.returnParameters())
for (ASTPointer<VariableDeclaration> const& variable: _function.parameters() + _function.returnParameters())
m_context.removeVariable(*variable);
m_context.adjustStackOffset(-(int)c_returnValuesSize);

View File

@ -36,7 +36,6 @@
#include <libsolutil/Whiskers.h>
#include <boost/algorithm/string/replace.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <numeric>
#include <utility>
@ -1591,6 +1590,16 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
result ^= fromBigEndian<uint64_t>(function.first.ref());
m_context << (u256{result} << (256 - 32));
}
else if (member == "min" || member == "max")
{
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument());
if (member == "min")
m_context << integerType->min();
else
m_context << integerType->max();
}
else if ((set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}).count(member))
{
// no-op

View File

@ -23,8 +23,6 @@
#include <liblangutil/Exceptions.h>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/reversed.hpp>
using namespace std;
using namespace solidity;

View File

@ -28,9 +28,6 @@
#include <libsolutil/Whiskers.h>
#include <libsolutil/StringUtils.h>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/reversed.hpp>
using namespace std;
using namespace solidity;
using namespace solidity::util;
@ -299,9 +296,6 @@ string YulUtilFunctions::shiftRightFunction(size_t _numBits)
string YulUtilFunctions::shiftRightFunctionDynamic()
{
// Note that if this is extended with signed shifts,
// the opcodes SAR and SDIV behave differently with regards to rounding!
string const functionName = "shift_right_unsigned_dynamic";
return m_functionCollector.createFunction(functionName, [&]() {
return
@ -321,6 +315,86 @@ string YulUtilFunctions::shiftRightFunctionDynamic()
});
}
string YulUtilFunctions::shiftRightSignedFunctionDynamic()
{
string const functionName = "shift_right_signed_dynamic";
return m_functionCollector.createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(bits, value) -> result {
<?hasShifts>
result := sar(bits, value)
<!hasShifts>
let divisor := exp(2, bits)
let xor_mask := sub(0, slt(value, 0))
result := xor(div(xor(value, xor_mask), divisor), xor_mask)
// combined version of
// switch slt(value, 0)
// case 0 { result := div(value, divisor) }
// default { result := not(div(not(value), divisor)) }
</hasShifts>
}
)")
("functionName", functionName)
("hasShifts", m_evmVersion.hasBitwiseShifting())
.render();
});
}
string YulUtilFunctions::typedShiftLeftFunction(Type const& _type, Type const& _amountType)
{
solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, "");
solAssert(_amountType.category() == Type::Category::Integer, "");
string const functionName = "shift_left_" + _type.identifier() + "_" + _amountType.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(value, bits) -> result {
bits := <cleanAmount>(bits)
<?amountSigned>
if slt(bits, 0) { invalid() }
</amountSigned>
result := <cleanup>(<shift>(bits, value))
}
)")
("functionName", functionName)
("amountSigned", dynamic_cast<IntegerType const&>(_amountType).isSigned())
("cleanAmount", cleanupFunction(_amountType))
("shift", shiftLeftFunctionDynamic())
("cleanup", cleanupFunction(_type))
.render();
});
}
string YulUtilFunctions::typedShiftRightFunction(Type const& _type, Type const& _amountType)
{
solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, "");
solAssert(_amountType.category() == Type::Category::Integer, "");
IntegerType const* integerType = dynamic_cast<IntegerType const*>(&_type);
bool valueSigned = integerType && integerType->isSigned();
string const functionName = "shift_right_" + _type.identifier() + "_" + _amountType.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(value, bits) -> result {
bits := <cleanAmount>(bits)
<?amountSigned>
if slt(bits, 0) { invalid() }
</amountSigned>
result := <cleanup>(<shift>(bits, <cleanup>(value)))
}
)")
("functionName", functionName)
("amountSigned", dynamic_cast<IntegerType const&>(_amountType).isSigned())
("cleanAmount", cleanupFunction(_amountType))
("shift", valueSigned ? shiftRightSignedFunctionDynamic() : shiftRightFunctionDynamic())
("cleanup", cleanupFunction(_type))
.render();
});
}
string YulUtilFunctions::updateByteSliceFunction(size_t _numBytes, size_t _shiftBytes)
{
solAssert(_numBytes <= 32, "");
@ -393,6 +467,8 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type)
return
Whiskers(R"(
function <functionName>(x, y) -> sum {
x := <cleanupFunction>(x)
y := <cleanupFunction>(y)
<?signed>
// overflow, if x >= 0 and y > (maxValue - x)
if and(iszero(slt(x, 0)), sgt(y, sub(<maxValue>, x))) { revert(0, 0) }
@ -409,6 +485,7 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type)
("signed", _type.isSigned())
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
("cleanupFunction", cleanupFunction(_type))
.render();
});
}
@ -421,6 +498,8 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type)
// Multiplication by zero could be treated separately and directly return zero.
Whiskers(R"(
function <functionName>(x, y) -> product {
x := <cleanupFunction>(x)
y := <cleanupFunction>(y)
<?signed>
// overflow, if x > 0, y > 0 and x > (maxValue / y)
if and(and(sgt(x, 0), sgt(y, 0)), gt(x, div(<maxValue>, y))) { revert(0, 0) }
@ -441,6 +520,7 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type)
("signed", _type.isSigned())
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
("cleanupFunction", cleanupFunction(_type))
.render();
});
}
@ -452,6 +532,8 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
return
Whiskers(R"(
function <functionName>(x, y) -> r {
x := <cleanupFunction>(x)
y := <cleanupFunction>(y)
if iszero(y) { revert(0, 0) }
<?signed>
// overflow for minVal / -1
@ -466,6 +548,7 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
("functionName", functionName)
("signed", _type.isSigned())
("minVal", toCompactHexWithPrefix(u256(_type.minValue())))
("cleanupFunction", cleanupFunction(_type))
.render();
});
}
@ -477,12 +560,15 @@ string YulUtilFunctions::checkedIntModFunction(IntegerType const& _type)
return
Whiskers(R"(
function <functionName>(x, y) -> r {
x := <cleanupFunction>(x)
y := <cleanupFunction>(y)
if iszero(y) { revert(0, 0) }
r := <?signed>s</signed>mod(x, y)
}
)")
("functionName", functionName)
("signed", _type.isSigned())
("cleanupFunction", cleanupFunction(_type))
.render();
});
}
@ -494,6 +580,8 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type)
return
Whiskers(R"(
function <functionName>(x, y) -> diff {
x := <cleanupFunction>(x)
y := <cleanupFunction>(y)
<?signed>
// underflow, if y >= 0 and x < (minValue + y)
if and(iszero(slt(y, 0)), slt(x, add(<minValue>, y))) { revert(0, 0) }
@ -509,6 +597,7 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type)
("signed", _type.isSigned())
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
("cleanupFunction", cleanupFunction(_type))
.render();
});
}
@ -1033,13 +1122,12 @@ string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingT
return m_functionCollector.createFunction(functionName, [&]() {
if (_mappingType.keyType()->isDynamicallySized())
return Whiskers(R"(
function <functionName>(slot <comma> <key>) -> dataSlot {
dataSlot := <hash>(slot <comma> <key>)
function <functionName>(slot <?+key>,</+key> <key>) -> dataSlot {
dataSlot := <hash>(<key> <?+key>,</+key> slot)
}
)")
("functionName", functionName)
("key", _keyType.sizeOnStack() > 0 ? "key" : "")
("comma", _keyType.sizeOnStack() > 0 ? "," : "")
("hash", packedHashFunction(
{&_keyType, TypeProvider::uint256()},
{_mappingType.keyType(), TypeProvider::uint256()}
@ -1764,8 +1852,18 @@ string YulUtilFunctions::cleanupFunction(Type const& _type)
solUnimplemented("Fixed point types not implemented.");
break;
case Type::Category::Function:
solAssert(dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::External, "");
templ("body", "cleaned := " + cleanupFunction(FixedBytesType(24)) + "(value)");
switch (dynamic_cast<FunctionType const&>(_type).kind())
{
case FunctionType::Kind::External:
templ("body", "cleaned := " + cleanupFunction(FixedBytesType(24)) + "(value)");
break;
case FunctionType::Kind::Internal:
templ("body", "cleaned := value");
break;
default:
solAssert(false, "");
break;
}
break;
case Type::Category::Array:
case Type::Category::Struct:
@ -1938,14 +2036,16 @@ std::string YulUtilFunctions::decrementCheckedFunction(Type const& _type)
return Whiskers(R"(
function <functionName>(value) -> ret {
value := <cleanupFunction>(value)
if <lt>(value, <minval>) { revert(0,0) }
ret := sub(value, 1)
}
)")
("functionName", functionName)
("minval", toCompactHexWithPrefix(minintval))
("lt", type.isSigned() ? "slt" : "lt")
.render();
("functionName", functionName)
("minval", toCompactHexWithPrefix(minintval))
("lt", type.isSigned() ? "slt" : "lt")
("cleanupFunction", cleanupFunction(_type))
.render();
});
}
@ -1966,14 +2066,16 @@ std::string YulUtilFunctions::incrementCheckedFunction(Type const& _type)
return Whiskers(R"(
function <functionName>(value) -> ret {
value := <cleanupFunction>(value)
if <gt>(value, <maxval>) { revert(0,0) }
ret := add(value, 1)
}
)")
("functionName", functionName)
("maxval", toCompactHexWithPrefix(maxintval))
("gt", type.isSigned() ? "sgt" : "gt")
.render();
("functionName", functionName)
("maxval", toCompactHexWithPrefix(maxintval))
("gt", type.isSigned() ? "sgt" : "gt")
("cleanupFunction", cleanupFunction(_type))
.render();
});
}
@ -1988,15 +2090,17 @@ string YulUtilFunctions::negateNumberCheckedFunction(Type const& _type)
return m_functionCollector.createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>(_value) -> ret {
if slt(_value, <minval>) { revert(0,0) }
ret := sub(0, _value)
function <functionName>(value) -> ret {
value := <cleanupFunction>(value)
if slt(value, <minval>) { revert(0,0) }
ret := sub(0, value)
}
)")
("functionName", functionName)
("minval", toCompactHexWithPrefix(minintval))
.render();
});
("functionName", functionName)
("minval", toCompactHexWithPrefix(minintval))
("cleanupFunction", cleanupFunction(_type))
.render();
});
}
string YulUtilFunctions::zeroValueFunction(Type const& _type, bool _splitFunctionTypes)
@ -2214,7 +2318,6 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC
}
solAssert(_type.isValueType(), "");
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
if (funType->kind() == FunctionType::Kind::External)
return Whiskers(R"(
@ -2228,18 +2331,23 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC
("splitFunction", splitExternalFunctionIdFunction())
.render();
return Whiskers(R"(
function <functionName>(memPtr) -> value {
value := <load>(memPtr)
<?needsValidation>
function <functionName>(ptr) -> value {
<?fromCalldata>
value := calldataload(ptr)
<validate>(value)
</needsValidation>
<!fromCalldata>
value := <cleanup>(mload(ptr))
</fromCalldata>
}
)")
("functionName", functionName)
("load", _fromCalldata ? "calldataload" : "mload")
("needsValidation", _fromCalldata)
("validate", _fromCalldata ? validatorFunction(_type) : "")
("fromCalldata", _fromCalldata)
("validate", validatorFunction(_type))
// Byte array elements generally need cleanup.
// Other types are cleaned as well to account for dirty memory e.g. due to inline assembly.
("cleanup", cleanupFunction(_type))
.render();
});
}

View File

@ -81,6 +81,14 @@ public:
std::string shiftLeftFunctionDynamic();
std::string shiftRightFunction(size_t _numBits);
std::string shiftRightFunctionDynamic();
std::string shiftRightSignedFunctionDynamic();
/// @returns the name of a function that performs a left shift and subsequent cleanup
/// and, if needed, prior cleanup.
/// If the amount to shift by is signed, a check for negativeness is performed.
/// signature: (value, amountToShift) -> result
std::string typedShiftLeftFunction(Type const& _type, Type const& _amountType);
std::string typedShiftRightFunction(Type const& _type, Type const& _amountType);
/// @returns the name of a function which replaces the
/// _numBytes bytes starting at byte position _shiftBytes (counted from the least significant
@ -204,7 +212,7 @@ public:
std::string readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes);
std::string readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes);
/// @returns a function that reads a value type from memory.
/// @returns a function that reads a value type from memory. Performs cleanup.
/// signature: (addr) -> value
std::string readFromMemory(Type const& _type);
/// @returns a function that reads a value type from calldata.

View File

@ -38,9 +38,6 @@
#include <liblangutil/SourceReferenceFormatter.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <sstream>
using namespace std;
@ -172,24 +169,24 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function)
string functionName = m_context.functionName(_function);
return m_context.functionCollector().createFunction(functionName, [&]() {
Whiskers t(R"(
function <functionName>(<params>) <returns> {
function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
<initReturnVariables>
<body>
}
)");
t("functionName", functionName);
string params;
vector<string> params;
for (auto const& varDecl: _function.parameters())
params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList();
t("params", params);
string retParams;
params += m_context.addLocalVariable(*varDecl).stackSlots();
t("params", joinHumanReadable(params));
vector<string> retParams;
string retInit;
for (auto const& varDecl: _function.returnParameters())
{
retParams += (retParams.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList();
retParams += m_context.addLocalVariable(*varDecl).stackSlots();
retInit += generateInitialAssignment(*varDecl);
}
t("returns", retParams.empty() ? "" : " -> " + retParams);
t("retParams", joinHumanReadable(retParams));
t("initReturnVariables", retInit);
t("body", generate(_function.body()));
return t.render();
@ -202,11 +199,11 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
Type const* type = _varDecl.annotation().type;
solAssert(!_varDecl.isConstant(), "");
solAssert(_varDecl.isStateVariable(), "");
if (auto const* mappingType = dynamic_cast<MappingType const*>(type))
return m_context.functionCollector().createFunction(functionName, [&]() {
solAssert(!_varDecl.isConstant() && !_varDecl.immutable(), "");
pair<u256, unsigned> slot_offset = m_context.storageLocationOfVariable(_varDecl);
solAssert(slot_offset.second == 0, "");
FunctionType funType(_varDecl);
@ -268,6 +265,16 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
("id", to_string(_varDecl.id()))
.render();
}
else if (_varDecl.isConstant())
return Whiskers(R"(
function <functionName>() -> <ret> {
<ret> := <constantValueFunction>()
}
)")
("functionName", functionName)
("constantValueFunction", IRGeneratorForStatements(m_context, m_utils).constantValueFunction(_varDecl))
("ret", suffixedVariableNameList("ret_", 0, _varDecl.type()->sizeOnStack()))
.render();
else
{
pair<u256, unsigned> slot_offset = m_context.storageLocationOfVariable(_varDecl);
@ -293,31 +300,35 @@ string IRGenerator::generateInitialAssignment(VariableDeclaration const& _varDec
return generator.code();
}
pair<string, map<ContractDefinition const*, string>> IRGenerator::evaluateConstructorArguments(
pair<string, map<ContractDefinition const*, vector<string>>> IRGenerator::evaluateConstructorArguments(
ContractDefinition const& _contract
)
{
map<ContractDefinition const*, string> constructorParams;
map<ContractDefinition const*, vector<string>> constructorParams;
vector<pair<ContractDefinition const*, std::vector<ASTPointer<Expression>>const *>> baseConstructorArguments;
for (ASTPointer<InheritanceSpecifier> const& base: _contract.baseContracts())
if (FunctionDefinition const* baseConstructor = dynamic_cast<ContractDefinition const*>(
base->name().annotation().referencedDeclaration
)->constructor(); baseConstructor && base->arguments())
base->name().annotation().referencedDeclaration
)->constructor(); baseConstructor && base->arguments())
baseConstructorArguments.emplace_back(
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
base->arguments()
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
base->arguments()
);
if (FunctionDefinition const* constructor = _contract.constructor())
for (auto const& modifier: constructor->modifiers())
if (FunctionDefinition const* baseConstructor = dynamic_cast<ContractDefinition const*>(
for (ASTPointer<ModifierInvocation> const& modifier: constructor->modifiers())
if (auto const* baseContract = dynamic_cast<ContractDefinition const*>(
modifier->name()->annotation().referencedDeclaration
)->constructor(); baseConstructor && modifier->arguments())
baseConstructorArguments.emplace_back(
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
modifier->arguments()
);
))
if (
FunctionDefinition const* baseConstructor = baseContract->constructor();
baseConstructor && modifier->arguments()
)
baseConstructorArguments.emplace_back(
dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
modifier->arguments()
);
IRGeneratorForStatements generator{m_context, m_utils};
for (auto&& [baseContract, arguments]: baseConstructorArguments)
@ -327,11 +338,11 @@ pair<string, map<ContractDefinition const*, string>> IRGenerator::evaluateConstr
{
vector<string> params;
for (size_t i = 0; i < arguments->size(); ++i)
params.emplace_back(generator.evaluateExpression(
*(arguments->at(i)),
*(baseContract->constructor()->parameters()[i]->type())
).commaSeparatedList());
constructorParams[baseContract] = joinHumanReadable(params);
params += generator.evaluateExpression(
*(arguments->at(i)),
*(baseContract->constructor()->parameters()[i]->type())
).stackSlots();
constructorParams[baseContract] = std::move(params);
}
}
@ -352,16 +363,16 @@ string IRGenerator::initStateVariables(ContractDefinition const& _contract)
void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contract)
{
auto listAllParams = [&](
map<ContractDefinition const*, string> const& baseParams) -> string
map<ContractDefinition const*, vector<string>> const& baseParams) -> vector<string>
{
vector<string> params;
for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
if (baseParams.count(contract))
params.emplace_back(baseParams.at(contract));
return joinHumanReadable(params);
params += baseParams.at(contract);
return params;
};
map<ContractDefinition const*, string> baseConstructorParams;
map<ContractDefinition const*, vector<string>> baseConstructorParams;
for (size_t i = 0; i < _contract.annotation().linearizedBaseContracts.size(); ++i)
{
ContractDefinition const* contract = _contract.annotation().linearizedBaseContracts[i];
@ -376,16 +387,16 @@ void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contra
<userDefinedConstructorBody>
}
)");
string params;
vector<string> params;
if (contract->constructor())
for (ASTPointer<VariableDeclaration> const& varDecl: contract->constructor()->parameters())
params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList();
t("params", params);
string baseParamsString = listAllParams(baseConstructorParams);
t("baseParams", baseParamsString);
t("comma", !params.empty() && !baseParamsString.empty() ? ", " : "");
params += m_context.addLocalVariable(*varDecl).stackSlots();
t("params", joinHumanReadable(params));
vector<string> baseParams = listAllParams(baseConstructorParams);
t("baseParams", joinHumanReadable(baseParams));
t("comma", !params.empty() && !baseParams.empty() ? ", " : "");
t("functionName", implicitConstructorName(*contract));
pair<string, map<ContractDefinition const*, string>> evaluatedArgs = evaluateConstructorArguments(*contract);
pair<string, map<ContractDefinition const*, vector<string>>> evaluatedArgs = evaluateConstructorArguments(*contract);
baseConstructorParams.insert(evaluatedArgs.second.begin(), evaluatedArgs.second.end());
t("evalBaseArguments", evaluatedArgs.first);
if (i < _contract.annotation().linearizedBaseContracts.size() - 1)
@ -393,7 +404,7 @@ void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contra
t("hasNextConstructor", true);
ContractDefinition const* nextContract = _contract.annotation().linearizedBaseContracts[i + 1];
t("nextConstructor", implicitConstructorName(*nextContract));
t("nextParams", listAllParams(baseConstructorParams));
t("nextParams", joinHumanReadable(listAllParams(baseConstructorParams)));
}
else
t("hasNextConstructor", false);
@ -468,10 +479,10 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
{
// <functionName>
<callValueCheck>
<assignToParams> <abiDecode>(4, calldatasize())
<assignToRetParams> <function>(<params>)
<?+params>let <params> := </+params> <abiDecode>(4, calldatasize())
<?+retParams>let <retParams> := </+retParams> <function>(<params>)
let memPos := <allocate>(0)
let memEnd := <abiEncode>(memPos <comma> <retParams>)
let memEnd := <abiEncode>(memPos <?+retParams>,</+retParams> <retParams>)
return(memPos, sub(memEnd, memPos))
}
</cases>
@ -493,13 +504,11 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
unsigned paramVars = make_shared<TupleType>(type->parameterTypes())->sizeOnStack();
unsigned retVars = make_shared<TupleType>(type->returnParameterTypes())->sizeOnStack();
templ["assignToParams"] = paramVars == 0 ? "" : "let " + suffixedVariableNameList("param_", 0, paramVars) + " := ";
templ["assignToRetParams"] = retVars == 0 ? "" : "let " + suffixedVariableNameList("ret_", 0, retVars) + " := ";
ABIFunctions abiFunctions(m_evmVersion, m_context.revertStrings(), m_context.functionCollector());
templ["abiDecode"] = abiFunctions.tupleDecoder(type->parameterTypes());
templ["params"] = suffixedVariableNameList("param_", 0, paramVars);
templ["retParams"] = suffixedVariableNameList("ret_", retVars, 0);
templ["retParams"] = suffixedVariableNameList("ret_", 0, retVars);
if (FunctionDefinition const* funDef = dynamic_cast<FunctionDefinition const*>(&type->declaration()))
templ["function"] = m_context.enqueueFunctionForCodeGeneration(*funDef);
@ -510,7 +519,6 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
templ["allocate"] = m_utils.allocationFunction();
templ["abiEncode"] = abiFunctions.tupleEncoder(type->returnParameterTypes(), type->returnParameterTypes(), false);
templ["comma"] = retVars == 0 ? "" : ", ";
}
t("cases", functions);
if (FunctionDefinition const* fallback = _contract.fallbackFunction())

View File

@ -81,9 +81,8 @@ private:
/// Evaluates constructor's arguments for all base contracts (listed in inheritance specifiers) of
/// @a _contract
/// @returns Pair of expressions needed to evaluate params and list of parameters in a map contract -> params
std::pair<std::string, std::map<ContractDefinition const*, std::string>> evaluateConstructorArguments(
ContractDefinition const& _contract
);
std::pair<std::string, std::map<ContractDefinition const*, std::vector<std::string>>>
evaluateConstructorArguments(ContractDefinition const& _contract);
/// Initializes state variables of
/// @a _contract

View File

@ -42,7 +42,6 @@
#include <libsolutil/Keccak256.h>
#include <libsolutil/Visitor.h>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/adaptor/transformed.hpp>
using namespace std;
@ -117,10 +116,12 @@ struct CopyTranslate: public yul::ASTCopier
reference.isOffset == false && reference.isSlot == false,
"Should not be called for offset/slot"
);
auto const& var = m_context.localVariable(*varDecl);
solAssert(var.type().sizeOnStack() == 1, "");
return yul::Identifier{
_identifier.location,
yul::YulString{m_context.localVariable(*varDecl).name()}
yul::YulString{var.commaSeparatedList()}
};
}
@ -178,6 +179,28 @@ IRVariable IRGeneratorForStatements::evaluateExpression(Expression const& _expre
return variable;
}
string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const& _constant)
{
string functionName = "constant_" + _constant.name() + "_" + to_string(_constant.id());
return m_context.functionCollector().createFunction(functionName, [&] {
Whiskers templ(R"(
function <functionName>() -> <ret> {
<code>
<ret> := <value>
}
)");
templ("functionName", functionName);
IRGeneratorForStatements generator(m_context, m_utils);
solAssert(_constant.value(), "");
Type const& constantType = *_constant.type();
templ("value", generator.evaluateExpression(*_constant.value(), constantType).commaSeparatedList());
templ("code", generator.code());
templ("ret", IRVariable("ret", constantType).commaSeparatedList());
return templ.render();
});
}
void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement)
{
if (Expression const* expression = _varDeclStatement.initialValue())
@ -230,29 +253,53 @@ bool IRGeneratorForStatements::visit(Conditional const& _conditional)
bool IRGeneratorForStatements::visit(Assignment const& _assignment)
{
_assignment.rightHandSide().accept(*this);
Type const* intermediateType = type(_assignment.rightHandSide()).closestTemporaryType(
&type(_assignment.leftHandSide())
);
IRVariable value = convert(_assignment.rightHandSide(), *intermediateType);
Token assignmentOperator = _assignment.assignmentOperator();
Token binaryOperator =
assignmentOperator == Token::Assign ?
assignmentOperator :
TokenTraits::AssignmentToBinaryOp(assignmentOperator);
Type const* rightIntermediateType =
TokenTraits::isShiftOp(binaryOperator) ?
type(_assignment.rightHandSide()).mobileType() :
type(_assignment.rightHandSide()).closestTemporaryType(
&type(_assignment.leftHandSide())
);
solAssert(rightIntermediateType, "");
IRVariable value = convert(_assignment.rightHandSide(), *rightIntermediateType);
_assignment.leftHandSide().accept(*this);
solAssert(!!m_currentLValue, "LValue not retrieved.");
if (_assignment.assignmentOperator() != Token::Assign)
if (assignmentOperator != Token::Assign)
{
solAssert(type(_assignment.leftHandSide()) == *intermediateType, "");
solAssert(intermediateType->isValueType(), "Compound operators only available for value types.");
solAssert(type(_assignment.leftHandSide()).isValueType(), "Compound operators only available for value types.");
solAssert(rightIntermediateType->isValueType(), "Compound operators only available for value types.");
IRVariable leftIntermediate = readFromLValue(*m_currentLValue);
m_code << value.name() << " := " << binaryOperation(
TokenTraits::AssignmentToBinaryOp(_assignment.assignmentOperator()),
*intermediateType,
leftIntermediate.name(),
value.name()
);
if (TokenTraits::isShiftOp(binaryOperator))
{
solAssert(type(_assignment) == leftIntermediate.type(), "");
solAssert(type(_assignment) == type(_assignment.leftHandSide()), "");
define(_assignment) << shiftOperation(binaryOperator, leftIntermediate, value);
writeToLValue(*m_currentLValue, IRVariable(_assignment));
m_currentLValue.reset();
return false;
}
else
{
solAssert(type(_assignment.leftHandSide()) == *rightIntermediateType, "");
m_code << value.name() << " := " << binaryOperation(
binaryOperator,
*rightIntermediateType,
leftIntermediate.name(),
value.name()
);
}
}
writeToLValue(*m_currentLValue, value);
m_currentLValue.reset();
if (*_assignment.annotation().type != *TypeProvider::emptyTuple())
define(_assignment, value);
@ -477,12 +524,16 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
return false;
}
if (commonType->category() == Type::Category::RationalNumber)
{
define(_binOp) << toCompactHexWithPrefix(commonType->literalValue(nullptr)) << "\n";
return false; // skip sub-expressions
}
_binOp.leftExpression().accept(*this);
_binOp.rightExpression().accept(*this);
if (commonType->category() == Type::Category::RationalNumber)
define(_binOp) << toCompactHexWithPrefix(commonType->literalValue(nullptr)) << "\n";
else if (TokenTraits::isCompareOp(op))
if (TokenTraits::isCompareOp(op))
{
if (auto type = dynamic_cast<FunctionType const*>(commonType))
{
@ -496,9 +547,9 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
isSigned = type->isSigned();
string args =
expressionAsType(_binOp.leftExpression(), *commonType) +
expressionAsType(_binOp.leftExpression(), *commonType, true) +
", " +
expressionAsType(_binOp.rightExpression(), *commonType);
expressionAsType(_binOp.rightExpression(), *commonType, true);
string expr;
if (op == Token::Equal)
@ -517,6 +568,12 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
solAssert(false, "Unknown comparison operator.");
define(_binOp) << expr << "\n";
}
else if (TokenTraits::isShiftOp(op))
{
IRVariable left = convert(_binOp.leftExpression(), *commonType);
IRVariable right = convert(_binOp.rightExpression(), *type(_binOp.rightExpression()).mobileType());
define(_binOp) << shiftOperation(_binOp.getOperator(), left, right) << "\n";
}
else
{
string left = expressionAsType(_binOp.leftExpression(), *commonType);
@ -584,13 +641,6 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
break;
case FunctionType::Kind::Internal:
{
vector<string> args;
for (size_t i = 0; i < arguments.size(); ++i)
if (functionType->takesArbitraryParameters())
args.emplace_back(IRVariable(*arguments[i]).commaSeparatedList());
else
args.emplace_back(convert(*arguments[i], *parameterTypes[i]).commaSeparatedList());
optional<FunctionDefinition const*> functionDef;
if (auto memberAccess = dynamic_cast<MemberAccess const*>(&_functionCall.expression()))
{
@ -628,6 +678,13 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
solAssert(functionDef.has_value(), "");
solAssert(functionDef.value() == nullptr || functionDef.value()->isImplemented(), "");
vector<string> args;
for (size_t i = 0; i < arguments.size(); ++i)
if (functionType->takesArbitraryParameters())
args += IRVariable(*arguments[i]).stackSlots();
else
args += convert(*arguments[i], *parameterTypes[i]).stackSlots();
if (functionDef.value() != nullptr)
define(_functionCall) <<
m_context.enqueueFunctionForCodeGeneration(*functionDef.value()) <<
@ -649,10 +706,12 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
}
case FunctionType::Kind::External:
case FunctionType::Kind::DelegateCall:
appendExternalFunctionCall(_functionCall, arguments);
break;
case FunctionType::Kind::BareCall:
case FunctionType::Kind::BareDelegateCall:
case FunctionType::Kind::BareStaticCall:
appendExternalFunctionCall(_functionCall, arguments);
appendBareCall(_functionCall, arguments);
break;
case FunctionType::Kind::BareCallCode:
solAssert(false, "Callcode has been removed.");
@ -663,7 +722,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
vector<IRVariable> indexedArgs;
string nonIndexedArgs;
vector<string> nonIndexedArgs;
TypePointers nonIndexedArgTypes;
TypePointers nonIndexedParamTypes;
if (!event.isAnonymous())
@ -686,10 +745,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
}
else
{
string vars = IRVariable(arg).commaSeparatedList();
if (!vars.empty())
// In reverse because abi_encode expects it like that.
nonIndexedArgs = ", " + move(vars) + nonIndexedArgs;
nonIndexedArgs += IRVariable(arg).stackSlots();
nonIndexedArgTypes.push_back(arg.annotation().type);
nonIndexedParamTypes.push_back(paramTypes[i]);
}
@ -704,7 +760,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
templ("end", m_context.newYulVariable());
templ("freeMemory", freeMemory());
templ("encode", abi.tupleEncoder(nonIndexedArgTypes, nonIndexedParamTypes));
templ("nonIndexedArgs", nonIndexedArgs);
templ("nonIndexedArgs", joinHumanReadablePrefixed(nonIndexedArgs));
templ("log", "log" + to_string(indexedArgs.size()));
templ("indexedArgs", joinHumanReadablePrefixed(indexedArgs | boost::adaptors::transformed([&](auto const& _arg) {
return _arg.commaSeparatedList();
@ -731,13 +787,134 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
break;
}
case FunctionType::Kind::ABIEncode:
case FunctionType::Kind::ABIEncodePacked:
case FunctionType::Kind::ABIEncodeWithSelector:
case FunctionType::Kind::ABIEncodeWithSignature:
{
bool const isPacked = functionType->kind() == FunctionType::Kind::ABIEncodePacked;
solAssert(functionType->padArguments() != isPacked, "");
bool const hasSelectorOrSignature =
functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector ||
functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature;
TypePointers argumentTypes;
TypePointers targetTypes;
vector<string> argumentVars;
for (size_t i = 0; i < arguments.size(); ++i)
{
// ignore selector
if (hasSelectorOrSignature && i == 0)
continue;
argumentTypes.emplace_back(&type(*arguments[i]));
targetTypes.emplace_back(type(*arguments[i]).fullEncodingType(false, true, isPacked));
argumentVars += IRVariable(*arguments[i]).stackSlots();
}
string selector;
if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature)
{
// hash the signature
Type const& selectorType = type(*arguments.front());
if (auto const* stringType = dynamic_cast<StringLiteralType const*>(&selectorType))
{
FixedHash<4> hash(keccak256(stringType->value()));
selector = formatNumber(u256(FixedHash<4>::Arith(hash)) << (256 - 32));
}
else
{
// Used to reset the free memory pointer later.
string freeMemoryPre = m_context.newYulVariable();
m_code << "let " << freeMemoryPre << " := " << freeMemory() << "\n";
IRVariable array = convert(*arguments[0], *TypeProvider::bytesMemory());
IRVariable hashVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(32));
define(hashVariable) <<
"keccak256(" <<
m_utils.arrayDataAreaFunction(*TypeProvider::bytesMemory()) <<
"(" <<
array.commaSeparatedList() <<
"), " <<
m_utils.arrayLengthFunction(*TypeProvider::bytesMemory()) <<
"(" <<
array.commaSeparatedList() <<
"))\n";
IRVariable selectorVariable(m_context.newYulVariable(), *TypeProvider::fixedBytes(4));
define(selectorVariable, hashVariable);
m_code << "mstore(" << to_string(CompilerUtils::freeMemoryPointer) << ", " << freeMemoryPre << ")\n";
}
}
else if (functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector)
selector = convert(*arguments.front(), *TypeProvider::fixedBytes(4)).name();
Whiskers templ(R"(
let <data> := <allocateTemporary>()
let <mpos> := add(<data>, 0x20)
<?+selector>
mstore(<mpos>, <selector>)
<mpos> := add(<mpos>, 4)
</+selector>
let <mend> := <encode>(<mpos><arguments>)
mstore(<data>, sub(<mend>, add(<data>, 0x20)))
mstore(<freeMemPtr>, <roundUp>(<mend>))
)");
templ("data", IRVariable(_functionCall).part("mpos").name());
templ("allocateTemporary", m_utils.allocationTemporaryMemoryFunction());
templ("mpos", m_context.newYulVariable());
templ("mend", m_context.newYulVariable());
templ("selector", selector);
templ("encode",
isPacked ?
m_context.abiFunctions().tupleEncoderPacked(argumentTypes, targetTypes) :
m_context.abiFunctions().tupleEncoder(argumentTypes, targetTypes, false)
);
templ("arguments", joinHumanReadablePrefixed(argumentVars));
templ("freeMemPtr", to_string(CompilerUtils::freeMemoryPointer));
templ("roundUp", m_utils.roundUpFunction());
m_code << templ.render();
break;
}
case FunctionType::Kind::Revert:
{
solAssert(arguments.size() == parameterTypes.size(), "");
if (arguments.empty())
m_code << "revert(0, 0)\n";
else
solUnimplementedAssert(false, "");
{
solAssert(arguments.size() == 1, "");
if (m_context.revertStrings() == RevertStrings::Strip)
m_code << "revert(0, 0)\n";
else
{
solAssert(type(*arguments.front()).isImplicitlyConvertibleTo(*TypeProvider::stringMemory()),"");
Whiskers templ(R"({
let <pos> := <allocateTemporary>()
mstore(<pos>, <hash>)
let <end> := <encode>(add(<pos>, 4) <argumentVars>)
revert(<pos>, sub(<end>, <pos>))
})");
templ("pos", m_context.newYulVariable());
templ("end", m_context.newYulVariable());
templ(
"hash",
(u256(util::FixedHash<4>::Arith(util::FixedHash<4>(util::keccak256("Error(string)")))) << (256 - 32)).str()
);
templ("allocateTemporary", m_utils.allocationTemporaryMemoryFunction());
templ(
"argumentVars",
joinHumanReadablePrefixed(IRVariable{*arguments.front()}.stackSlots())
);
templ("encode", m_context.abiFunctions().tupleEncoder(
{&type(*arguments.front())},
{TypeProvider::stringMemory()}
));
m_code << templ.render();
}
}
break;
}
@ -760,6 +937,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
solAssert(arguments.size() == 1, "");
ArrayType const* arrayType = TypeProvider::bytesMemory();
auto array = convert(*arguments[0], *arrayType);
define(_functionCall) <<
@ -905,11 +1083,11 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
);
TypePointers argumentTypes;
string constructorParams;
vector<string> constructorParams;
for (ASTPointer<Expression const> const& arg: arguments)
{
argumentTypes.push_back(arg->annotation().type);
constructorParams += ", " + IRVariable{*arg}.commaSeparatedList();
constructorParams += IRVariable{*arg}.stackSlots();
}
ContractDefinition const* contract =
@ -935,9 +1113,9 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
t("releaseTemporaryMemory", m_utils.releaseTemporaryMemoryFunction());
t("object", m_context.creationObjectName(*contract));
t("abiEncode",
m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(),false)
m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(), false)
);
t("constructorParams", constructorParams);
t("constructorParams", joinHumanReadablePrefixed(constructorParams));
t("value", functionType->valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
t("saltSet", functionType->saltSet());
if (functionType->saltSet())
@ -1126,7 +1304,20 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
solAssert(false, "Blockhash has been removed.");
else if (member == "creationCode" || member == "runtimeCode")
{
solUnimplementedAssert(false, "");
solUnimplementedAssert(member != "runtimeCode", "");
TypePointer arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument();
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition();
m_context.subObjectsCreated().insert(&contract);
m_code << Whiskers(R"(
let <size> := datasize("<objectName>")
let <result> := <allocationFunction>(add(<size>, 32))
mstore(<result>, <size>)
datacopy(add(<result>, 32), dataoffset("<objectName>"), <size>)
)")
("allocationFunction", m_utils.allocationFunction())
("size", m_context.newYulVariable())
("objectName", m_context.creationObjectName(contract))
("result", IRVariable(_memberAccess).commaSeparatedList()).render();
}
else if (member == "name")
{
@ -1141,6 +1332,16 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
result ^= fromBigEndian<uint64_t>(function.first.ref());
define(_memberAccess) << formatNumber(u256{result} << (256 - 32)) << "\n";
}
else if (member == "min" || member == "max")
{
MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type);
IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument());
if (member == "min")
define(_memberAccess) << formatNumber(integerType->min()) << "\n";
else
define(_memberAccess) << formatNumber(integerType->max()) << "\n";
}
else if (set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}.count(member))
{
// no-op
@ -1312,14 +1513,11 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
solAssert(keyType.sizeOnStack() <= 1, "");
string slot = m_context.newYulVariable();
Whiskers templ("let <slot> := <indexAccess>(<base> <key>)\n");
Whiskers templ("let <slot> := <indexAccess>(<base><?+key>,<key></+key>)\n");
templ("slot", slot);
templ("indexAccess", m_utils.mappingIndexAccessFunction(mappingType, keyType));
templ("base", IRVariable(_indexAccess.baseExpression()).commaSeparatedList());
if (keyType.sizeOnStack() == 0)
templ("key", "");
else
templ("key", ", " + IRVariable(*_indexAccess.indexExpression()).commaSeparatedList());
templ("key", IRVariable(*_indexAccess.indexExpression()).commaSeparatedList());
m_code << templ.render();
setLValue(_indexAccess, IRLValue{
*_indexAccess.annotation().type,
@ -1542,11 +1740,9 @@ void IRGeneratorForStatements::handleVariableReference(
Expression const& _referencingExpression
)
{
// TODO for the constant case, we have to be careful:
// If the value is visited twice, `defineExpression` is called twice on
// the same expression.
solUnimplementedAssert(!_variable.isConstant(), "");
if (_variable.isStateVariable() && _variable.immutable())
if (_variable.isStateVariable() && _variable.isConstant())
define(_referencingExpression) << constantValueFunction(_variable) << "()\n";
else if (_variable.isStateVariable() && _variable.immutable())
setLValue(_referencingExpression, IRLValue{
*_variable.annotation().type,
IRLValue::Immutable{&_variable}
@ -1574,18 +1770,20 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
)
{
FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression()));
solAssert(
funType.takesArbitraryParameters() ||
_arguments.size() == funType.parameterTypes().size(), ""
);
solUnimplementedAssert(!funType.bound(), "");
solAssert(!funType.takesArbitraryParameters(), "");
solAssert(_arguments.size() == funType.parameterTypes().size(), "");
solAssert(!funType.isBareCall(), "");
FunctionType::Kind const funKind = funType.kind();
solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), "");
solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed.");
solAssert(
funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall,
"Can only be used for regular external calls."
);
bool const isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
bool const useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall());
solUnimplementedAssert(!funType.bound(), "");
bool const isDelegateCall = funKind == FunctionType::Kind::DelegateCall;
bool const useStaticCall = funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall();
ReturnInfo const returnInfo{m_context.evmVersion(), funType};
@ -1594,12 +1792,9 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
for (auto const& arg: _arguments)
{
argumentTypes.emplace_back(&type(*arg));
if (IRVariable(*arg).type().sizeOnStack() > 0)
argumentStrings.emplace_back(IRVariable(*arg).commaSeparatedList());
argumentStrings += IRVariable(*arg).stackSlots();
}
string argumentString = argumentStrings.empty() ? ""s : (", " + joinHumanReadable(argumentStrings));
solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, "");
if (!m_context.evmVersion().canOverchargeGasForCall())
{
@ -1611,33 +1806,19 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
m_code << "mstore(add(" << freeMemory() << ", " << to_string(returnInfo.estimatedReturnSize) << "), 0)\n";
}
ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
Whiskers templ(R"(
<?checkExistence>
if iszero(extcodesize(<address>)) { revert(0, 0) }
</checkExistence>
if iszero(extcodesize(<address>)) { revert(0, 0) }
// storage for arguments and returned data
let <pos> := <freeMemory>
<?bareCall>
<!bareCall>
mstore(<pos>, <shl28>(<funId>))
</bareCall>
let <end> := <encodeArgs>(
<?bareCall>
<pos>
<!bareCall>
add(<pos>, 4)
</bareCall>
<argumentString>
)
mstore(<pos>, <shl28>(<funId>))
let <end> := <encodeArgs>(add(<pos>, 4) <argumentString>)
let <success> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <reservedReturnSize>)
<?noTryCall>
if iszero(<success>) { <forwardingRevert>() }
</noTryCall>
<?hasRetVars> let <retVars> </hasRetVars>
<?+retVars> let <retVars> </+retVars>
if <success> {
<?dynamicReturnSize>
// copy dynamic return data out
@ -1648,12 +1829,11 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
mstore(<freeMemoryPointer>, add(<pos>, <roundUp>(<returnSize>)))
// decode return parameters from external try-call into retVars
<?hasRetVars> <retVars> := </hasRetVars> <abiDecode>(<pos>, add(<pos>, <returnSize>))
<?+retVars> <retVars> := </+retVars> <abiDecode>(<pos>, add(<pos>, <returnSize>))
}
)");
templ("pos", m_context.newYulVariable());
templ("end", m_context.newYulVariable());
templ("bareCall", funType.isBareCall());
if (_functionCall.annotation().tryCall)
templ("success", m_context.trySuccessConditionVariable(_functionCall));
else
@ -1661,17 +1841,8 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
templ("freeMemory", freeMemory());
templ("shl28", m_utils.shiftLeftFunction(8 * (32 - 4)));
if (!funType.isBareCall())
templ("funId", IRVariable(_functionCall.expression()).part("functionIdentifier").name());
if (funKind == FunctionType::Kind::ECRecover)
templ("address", "1");
else if (funKind == FunctionType::Kind::SHA256)
templ("address", "2");
else if (funKind == FunctionType::Kind::RIPEMD160)
templ("address", "3");
else
templ("address", IRVariable(_functionCall.expression()).part("address").name());
templ("funId", IRVariable(_functionCall.expression()).part("functionIdentifier").name());
templ("address", IRVariable(_functionCall.expression()).part("address").name());
// Always use the actual return length, and not our calculated expected length, if returndatacopy is supported.
// This ensures it can catch badly formatted input from external calls.
@ -1684,39 +1855,20 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
string const retVars = IRVariable(_functionCall).commaSeparatedList();
templ("retVars", retVars);
templ("hasRetVars", !retVars.empty());
solAssert(retVars.empty() == returnInfo.returnTypes.empty(), "");
templ("roundUp", m_utils.roundUpFunction());
templ("abiDecode", abi.tupleDecoder(returnInfo.returnTypes, true));
templ("abiDecode", m_context.abiFunctions().tupleDecoder(returnInfo.returnTypes, true));
templ("dynamicReturnSize", returnInfo.dynamicReturnSize);
templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer));
templ("noTryCall", !_functionCall.annotation().tryCall);
// If the function takes arbitrary parameters or is a bare call, copy dynamic length data in place.
// Move arguments to memory, will not update the free memory pointer (but will update the memory
// pointer on the stack).
bool encodeInPlace = funType.takesArbitraryParameters() || funType.isBareCall();
if (funType.kind() == FunctionType::Kind::ECRecover)
// This would be the only combination of padding and in-place encoding,
// but all parameters of ecrecover are value types anyway.
encodeInPlace = false;
bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall;
solUnimplementedAssert(encodeInPlace == !funType.padArguments(), "");
if (encodeInPlace)
{
solUnimplementedAssert(!encodeForLibraryCall, "");
templ("encodeArgs", abi.tupleEncoderPacked(argumentTypes, funType.parameterTypes()));
}
else
templ("encodeArgs", abi.tupleEncoder(argumentTypes, funType.parameterTypes(), encodeForLibraryCall));
templ("argumentString", argumentString);
// Output data will replace input data, unless we have ECRecover (then, output
// area will be 32 bytes just before input area).
solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, "");
solAssert(funType.padArguments(), "");
templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, funType.parameterTypes(), encodeForLibraryCall));
templ("argumentString", joinHumanReadablePrefixed(argumentStrings));
solAssert(!isDelegateCall || !funType.valueSet(), "Value set for delegatecall");
solAssert(!useStaticCall || !funType.valueSet(), "Value set for staticcall");
@ -1724,10 +1876,6 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
templ("hasValue", !isDelegateCall && !useStaticCall);
templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
// Check that the target contract exists (has code) for non-low-level calls.
bool checkExistence = (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall);
templ("checkExistence", checkExistence);
if (funType.gasSet())
templ("gas", IRVariable(_functionCall.expression()).part("gas").name());
else if (m_context.evmVersion().canOverchargeGasForCall())
@ -1740,8 +1888,6 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10;
if (funType.valueSet())
gasNeededByCaller += evmasm::GasCosts::callValueTransferGas;
if (!checkExistence)
gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know
templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
}
// Order is important here, STATICCALL might overlap with DELEGATECALL.
@ -1754,8 +1900,103 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
templ("forwardingRevert", m_utils.forwardingRevertFunction());
solUnimplementedAssert(funKind != FunctionType::Kind::RIPEMD160, "");
solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, "");
m_code << templ.render();
}
void IRGeneratorForStatements::appendBareCall(
FunctionCall const& _functionCall,
vector<ASTPointer<Expression const>> const& _arguments
)
{
FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression()));
solAssert(
!funType.bound() &&
!funType.takesArbitraryParameters() &&
_arguments.size() == 1 &&
funType.parameterTypes().size() == 1, ""
);
FunctionType::Kind const funKind = funType.kind();
solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), "");
solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed.");
solAssert(
funKind == FunctionType::Kind::BareCall ||
funKind == FunctionType::Kind::BareDelegateCall ||
funKind == FunctionType::Kind::BareStaticCall, ""
);
solAssert(!_functionCall.annotation().tryCall, "");
Whiskers templ(R"(
<?needsEncoding>
let <pos> := mload(<freeMemoryPointer>)
let <length> := sub(<encode>(<pos> <?+arg>,</+arg> <arg>), <pos>)
<!needsEncoding>
let <pos> := add(<arg>, 0x20)
let <length> := mload(<arg>)
</needsEncoding>
let <success> := <call>(<gas>, <address>, <?+value> <value>, </+value> <pos>, <length>, 0, 0)
<?+returndataVar>
let <returndataVar> := <extractReturndataFunction>()
</+returndataVar>
)");
templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer));
templ("pos", m_context.newYulVariable());
templ("length", m_context.newYulVariable());
templ("arg", IRVariable(*_arguments.front()).commaSeparatedList());
Type const& argType = type(*_arguments.front());
if (argType == *TypeProvider::bytesMemory() || argType == *TypeProvider::stringMemory())
templ("needsEncoding", false);
else
{
templ("needsEncoding", true);
ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
templ("encode", abi.tupleEncoderPacked({&argType}, {TypeProvider::bytesMemory()}));
}
templ("success", IRVariable(_functionCall).tupleComponent(0).name());
if (IRVariable(_functionCall).tupleComponent(1).type().category() == Type::Category::InaccessibleDynamic)
templ("returndataVar", "");
else
{
templ("returndataVar", IRVariable(_functionCall).tupleComponent(1).part("mpos").name());
templ("extractReturndataFunction", m_utils.extractReturndataFunction());
}
templ("address", IRVariable(_functionCall.expression()).part("address").name());
if (funKind == FunctionType::Kind::BareCall)
{
templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
templ("call", "call");
}
else
{
solAssert(!funType.valueSet(), "Value set for delegatecall or staticcall.");
templ("value", "");
if (funKind == FunctionType::Kind::BareStaticCall)
templ("call", "staticcall");
else
templ("call", "delegatecall");
}
if (funType.gasSet())
templ("gas", IRVariable(_functionCall.expression()).part("gas").name());
else if (m_context.evmVersion().canOverchargeGasForCall())
// Send all gas (requires tangerine whistle EVM)
templ("gas", "gas()");
else
{
// send all gas except the amount needed to execute "SUB" and "CALL"
// @todo this retains too much gas for now, needs to be fine-tuned.
u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10;
if (funType.valueSet())
gasNeededByCaller += evmasm::GasCosts::callValueTransferGas;
gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know
templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
}
m_code << templ.render();
}
@ -1777,11 +2018,16 @@ IRVariable IRGeneratorForStatements::convert(IRVariable const& _from, Type const
}
}
std::string IRGeneratorForStatements::expressionAsType(Expression const& _expression, Type const& _to)
std::string IRGeneratorForStatements::expressionAsType(Expression const& _expression, Type const& _to, bool _forceCleanup)
{
IRVariable from(_expression);
if (from.type() == _to)
return from.commaSeparatedList();
{
if (_forceCleanup)
return m_utils.cleanupFunction(_to) + "(" + from.commaSeparatedList() + ")";
else
return from.commaSeparatedList();
}
else
return m_utils.conversionFunction(from.type(), _to) + "(" + from.commaSeparatedList() + ")";
}
@ -1860,6 +2106,10 @@ string IRGeneratorForStatements::binaryOperation(
string const& _right
)
{
solAssert(
!TokenTraits::isShiftOp(_operator),
"Have to use specific shift operation function for shifts."
);
if (IntegerType const* type = dynamic_cast<IntegerType const*>(&_type))
{
string fun;
@ -1903,6 +2153,31 @@ string IRGeneratorForStatements::binaryOperation(
return {};
}
std::string IRGeneratorForStatements::shiftOperation(
langutil::Token _operator,
IRVariable const& _value,
IRVariable const& _amountToShift
)
{
IntegerType const* amountType = dynamic_cast<IntegerType const*>(&_amountToShift.type());
solAssert(amountType, "");
solAssert(_operator == Token::SHL || _operator == Token::SAR, "");
return
Whiskers(R"(
<shift>(<value>, <amount>)
)")
("shift",
_operator == Token::SHL ?
m_utils.typedShiftLeftFunction(_value.type(), *amountType) :
m_utils.typedShiftRightFunction(_value.type(), *amountType)
)
("value", _value.name())
("amount", _amountToShift.name())
.render();
}
void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _binOp)
{
langutil::Token const op = _binOp.getOperator();
@ -2021,13 +2296,7 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue)
")\n";
},
[&](IRLValue::Memory const& _memory) {
if (_memory.byteArrayElement)
define(result) <<
m_utils.cleanupFunction(_lvalue.type) <<
"(mload(" <<
_memory.address <<
"))\n";
else if (_lvalue.type.isValueType())
if (_lvalue.type.isValueType())
define(result) <<
m_utils.readFromMemory(_lvalue.type) <<
"(" <<

View File

@ -54,6 +54,10 @@ public:
/// Calculates expression's value and returns variable where it was stored
IRVariable evaluateExpression(Expression const& _expression, Type const& _to);
/// @returns the name of a function that computes the value of the given constant
/// and also generates the function.
std::string constantValueFunction(VariableDeclaration const& _constant);
void endVisit(VariableDeclarationStatement const& _variableDeclaration) override;
bool visit(Conditional const& _conditional) override;
bool visit(Assignment const& _assignment) override;
@ -102,6 +106,13 @@ private:
std::vector<ASTPointer<Expression const>> const& _arguments
);
/// Appends code for .call / .delegatecall / .staticcall.
/// All involved expressions have already been visited.
void appendBareCall(
FunctionCall const& _functionCall,
std::vector<ASTPointer<Expression const>> const& _arguments
);
/// @returns code that evaluates to the first unused memory slot (which does not have to
/// be empty).
static std::string freeMemory();
@ -112,7 +123,8 @@ private:
/// @returns a Yul expression representing the current value of @a _expression,
/// converted to type @a _to if it does not yet have that type.
std::string expressionAsType(Expression const& _expression, Type const& _to);
/// If @a _forceCleanup is set to true, it also cleans the value, in case it already has type @a _to.
std::string expressionAsType(Expression const& _expression, Type const& _to, bool _forceCleanup = false);
/// @returns an output stream that can be used to define @a _var using a function call or
/// single stack slot expression.
@ -142,6 +154,11 @@ private:
std::string const& _right
);
/// @returns code to perform the given shift operation.
/// The operation itself will be performed in the type of the value,
/// while the amount to shift can have its own type.
std::string shiftOperation(langutil::Token _op, IRVariable const& _value, IRVariable const& _shiftAmount);
/// Assigns the value of @a _value to the lvalue @a _lvalue.
void writeToLValue(IRLValue const& _lvalue, IRVariable const& _value);
/// @returns a fresh IR variable containing the value of the lvalue @a _lvalue.

View File

@ -54,7 +54,7 @@ IRVariable IRVariable::part(string const& _name) const
solAssert(itemName.empty() || itemType, "");
return IRVariable{suffixedName(itemName), itemType ? *itemType : m_type};
}
solAssert(false, "Invalid stack item name.");
solAssert(false, "Invalid stack item name: " + _name);
}
vector<string> IRVariable::stackSlots() const
@ -89,7 +89,7 @@ string IRVariable::name() const
{
solAssert(m_type.sizeOnStack() == 1, "");
auto const& [itemName, type] = m_type.stackItems().front();
solAssert(!type, "");
solAssert(!type, "Expected null type for name " + itemName);
return suffixedName(itemName);
}

View File

@ -69,10 +69,11 @@ public:
/// The returned IRVariable is itself typed with the type of the stack slot as defined
/// in ``m_type.stackItems()`` and may again occupy multiple stack slots.
IRVariable part(std::string const& _slot) const;
private:
/// @returns a vector containing the names of the stack slots of the variable.
std::vector<std::string> stackSlots() const;
private:
/// @returns a name consisting of the base name appended with an underscore and @æ _suffix,
/// unless @a _suffix is empty, in which case the base name itself is returned.
std::string suffixedName(std::string const& _suffix) const;

View File

@ -21,8 +21,6 @@
#include <libsolidity/formal/SymbolicState.h>
#include <libsolidity/formal/SymbolicTypes.h>
#include <boost/algorithm/string/replace.hpp>
using namespace std;
using namespace solidity;
using namespace solidity::util;
@ -44,6 +42,7 @@ BMC::BMC(
if (_enabledSolvers.some())
if (!_smtlib2Responses.empty())
m_errorReporter.warning(
5622_error,
"SMT-LIB2 query responses were given in the auxiliary input, "
"but this Solidity binary uses an SMT solver (Z3/CVC4) directly."
"These responses will be ignored."
@ -74,6 +73,7 @@ void BMC::analyze(SourceUnit const& _source, set<Expression const*> _safeAsserti
{
m_noSolverWarning = true;
m_outerErrorReporter.warning(
8084_error,
SourceLocation(),
"BMC analysis was not possible since no integrated SMT solver (Z3 or CVC4) was found."
);
@ -467,6 +467,7 @@ void BMC::internalOrExternalFunctionCall(FunctionCall const& _funCall)
inlineFunctionCall(_funCall);
else if (funType.kind() == FunctionType::Kind::Internal)
m_errorReporter.warning(
5729_error,
_funCall.location(),
"Assertion checker does not yet implement this type of function call."
);
@ -591,8 +592,7 @@ void BMC::checkConstantCondition(BMCVerificationTarget& _target)
*_target.expression,
_target.constraints,
_target.value,
_target.callStack,
"Condition is always $VALUE."
_target.callStack
);
}
@ -610,6 +610,8 @@ void BMC::checkUnderflow(BMCVerificationTarget& _target, smt::Expression const&
_target.callStack,
_target.modelExpressions,
_target.expression->location(),
4144_error,
8312_error,
"Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ")",
"<result>",
&_target.value
@ -630,6 +632,8 @@ void BMC::checkOverflow(BMCVerificationTarget& _target, smt::Expression const& _
_target.callStack,
_target.modelExpressions,
_target.expression->location(),
2661_error,
8065_error,
"Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ")",
"<result>",
&_target.value
@ -644,6 +648,8 @@ void BMC::checkDivByZero(BMCVerificationTarget& _target)
_target.callStack,
_target.modelExpressions,
_target.expression->location(),
3046_error,
5272_error,
"Division by zero",
"<result>",
&_target.value
@ -658,6 +664,8 @@ void BMC::checkBalance(BMCVerificationTarget& _target)
_target.callStack,
_target.modelExpressions,
_target.expression->location(),
1236_error,
4010_error,
"Insufficient funds",
"address(this).balance"
);
@ -672,6 +680,8 @@ void BMC::checkAssert(BMCVerificationTarget& _target)
_target.callStack,
_target.modelExpressions,
_target.expression->location(),
4661_error,
7812_error,
"Assertion violation"
);
}
@ -702,9 +712,11 @@ void BMC::addVerificationTarget(
void BMC::checkCondition(
smt::Expression _condition,
vector<SMTEncoder::CallStackEntry> const& callStack,
vector<SMTEncoder::CallStackEntry> const& _callStack,
pair<vector<smt::Expression>, vector<string>> const& _modelExpressions,
SourceLocation const& _location,
ErrorId _errorHappens,
ErrorId _errorMightHappen,
string const& _description,
string const& _additionalValueName,
smt::Expression const* _additionalValue
@ -716,7 +728,7 @@ void BMC::checkCondition(
vector<smt::Expression> expressionsToEvaluate;
vector<string> expressionNames;
tie(expressionsToEvaluate, expressionNames) = _modelExpressions;
if (callStack.size())
if (_callStack.size())
if (_additionalValue)
{
expressionsToEvaluate.emplace_back(*_additionalValue);
@ -747,7 +759,7 @@ void BMC::checkCondition(
{
std::ostringstream message;
message << _description << " happens here";
if (callStack.size())
if (_callStack.size())
{
std::ostringstream modelMessage;
modelMessage << " for:\n";
@ -760,30 +772,31 @@ void BMC::checkCondition(
for (auto const& eval: sortedModel)
modelMessage << " " << eval.first << " = " << eval.second << "\n";
m_errorReporter.warning(
_errorHappens,
_location,
message.str(),
SecondarySourceLocation().append(modelMessage.str(), SourceLocation{})
.append(SMTEncoder::callStackMessage(callStack))
.append(SMTEncoder::callStackMessage(_callStack))
.append(move(secondaryLocation))
);
}
else
{
message << ".";
m_errorReporter.warning(_location, message.str(), secondaryLocation);
m_errorReporter.warning(6084_error, _location, message.str(), secondaryLocation);
}
break;
}
case smt::CheckResult::UNSATISFIABLE:
break;
case smt::CheckResult::UNKNOWN:
m_errorReporter.warning(_location, _description + " might happen here.", secondaryLocation);
m_errorReporter.warning(_errorMightHappen, _location, _description + " might happen here.", secondaryLocation);
break;
case smt::CheckResult::CONFLICTING:
m_errorReporter.warning(_location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
m_errorReporter.warning(1584_error, _location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
break;
case smt::CheckResult::ERROR:
m_errorReporter.warning(_location, "Error trying to invoke SMT solver.");
m_errorReporter.warning(1823_error, _location, "Error trying to invoke SMT solver.");
break;
}
@ -794,8 +807,7 @@ void BMC::checkBooleanNotConstant(
Expression const& _condition,
smt::Expression const& _constraints,
smt::Expression const& _value,
vector<SMTEncoder::CallStackEntry> const& _callStack,
string const& _description
vector<SMTEncoder::CallStackEntry> const& _callStack
)
{
// Do not check for const-ness if this is a constant.
@ -813,9 +825,9 @@ void BMC::checkBooleanNotConstant(
m_interface->pop();
if (positiveResult == smt::CheckResult::ERROR || negatedResult == smt::CheckResult::ERROR)
m_errorReporter.warning(_condition.location(), "Error trying to invoke SMT solver.");
m_errorReporter.warning(8592_error, _condition.location(), "Error trying to invoke SMT solver.");
else if (positiveResult == smt::CheckResult::CONFLICTING || negatedResult == smt::CheckResult::CONFLICTING)
m_errorReporter.warning(_condition.location(), "At least two SMT solvers provided conflicting answers. Results might not be sound.");
m_errorReporter.warning(3356_error, _condition.location(), "At least two SMT solvers provided conflicting answers. Results might not be sound.");
else if (positiveResult == smt::CheckResult::SATISFIABLE && negatedResult == smt::CheckResult::SATISFIABLE)
{
// everything fine.
@ -825,24 +837,25 @@ void BMC::checkBooleanNotConstant(
// can't do anything.
}
else if (positiveResult == smt::CheckResult::UNSATISFIABLE && negatedResult == smt::CheckResult::UNSATISFIABLE)
m_errorReporter.warning(_condition.location(), "Condition unreachable.", SMTEncoder::callStackMessage(_callStack));
m_errorReporter.warning(2512_error, _condition.location(), "Condition unreachable.", SMTEncoder::callStackMessage(_callStack));
else
{
string value;
string description;
if (positiveResult == smt::CheckResult::SATISFIABLE)
{
solAssert(negatedResult == smt::CheckResult::UNSATISFIABLE, "");
value = "true";
description = "Condition is always true.";
}
else
{
solAssert(positiveResult == smt::CheckResult::UNSATISFIABLE, "");
solAssert(negatedResult == smt::CheckResult::SATISFIABLE, "");
value = "false";
description = "Condition is always false.";
}
m_errorReporter.warning(
6838_error,
_condition.location(),
boost::algorithm::replace_all_copy(_description, "$VALUE", value),
description,
SMTEncoder::callStackMessage(_callStack)
);
}
@ -862,7 +875,7 @@ BMC::checkSatisfiableAndGenerateModel(vector<smt::Expression> const& _expression
string description("Error querying SMT solver");
if (_e.comment())
description += ": " + *_e.comment();
m_errorReporter.warning(description);
m_errorReporter.warning(8140_error, description);
result = smt::CheckResult::ERROR;
}

View File

@ -44,6 +44,7 @@ using solidity::util::h256;
namespace solidity::langutil
{
class ErrorReporter;
struct ErrorId;
struct SourceLocation;
}
@ -144,22 +145,22 @@ private:
/// Check that a condition can be satisfied.
void checkCondition(
smt::Expression _condition,
std::vector<CallStackEntry> const& callStack,
std::vector<CallStackEntry> const& _callStack,
std::pair<std::vector<smt::Expression>, std::vector<std::string>> const& _modelExpressions,
langutil::SourceLocation const& _location,
langutil::ErrorId _errorHappens,
langutil::ErrorId _errorMightHappen,
std::string const& _description,
std::string const& _additionalValueName = "",
smt::Expression const* _additionalValue = nullptr
);
/// Checks that a boolean condition is not constant. Do not warn if the expression
/// is a literal constant.
/// @param _description the warning string, $VALUE will be replaced by the constant value.
void checkBooleanNotConstant(
Expression const& _condition,
smt::Expression const& _constraints,
smt::Expression const& _value,
std::vector<CallStackEntry> const& _callStack,
std::string const& _description
std::vector<CallStackEntry> const& _callStack
);
std::pair<smt::CheckResult, std::vector<std::string>>
checkSatisfiableAndGenerateModel(std::vector<smt::Expression> const& _expressionsToEvaluate);

View File

@ -965,10 +965,10 @@ pair<smt::CheckResult, vector<string>> CHC::query(smt::Expression const& _query,
case smt::CheckResult::UNKNOWN:
break;
case smt::CheckResult::CONFLICTING:
m_outerErrorReporter.warning(_location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
m_outerErrorReporter.warning(1988_error, _location, "At least two SMT solvers provided conflicting answers. Results might not be sound.");
break;
case smt::CheckResult::ERROR:
m_outerErrorReporter.warning(_location, "Error trying to invoke SMT solver.");
m_outerErrorReporter.warning(1218_error, _location, "Error trying to invoke SMT solver.");
break;
}
return {result, values};

View File

@ -21,7 +21,6 @@
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem/operations.hpp>
#include <array>
#include <fstream>

View File

@ -266,6 +266,7 @@ void SMTEncoder::endVisit(FunctionDefinition const&)
bool SMTEncoder::visit(InlineAssembly const& _inlineAsm)
{
m_errorReporter.warning(
7737_error,
_inlineAsm.location(),
"Assertion checker does not support inline assembly."
);
@ -325,6 +326,7 @@ void SMTEncoder::endVisit(VariableDeclarationStatement const& _varDecl)
}
else
m_errorReporter.warning(
7186_error,
_varDecl.location(),
"Assertion checker does not yet implement such variable declarations."
);
@ -350,6 +352,7 @@ void SMTEncoder::endVisit(Assignment const& _assignment)
m_context.newValue(*varDecl);
m_errorReporter.warning(
9149_error,
_assignment.location(),
"Assertion checker does not yet implement this assignment operator."
);
@ -409,6 +412,7 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple)
if (_tuple.isInlineArray())
m_errorReporter.warning(
2177_error,
_tuple.location(),
"Assertion checker does not yet implement inline arrays."
);
@ -493,6 +497,7 @@ void SMTEncoder::endVisit(UnaryOperation const& _op)
}
else
m_errorReporter.warning(
1950_error,
_op.location(),
"Assertion checker does not yet implement such increments / decrements."
);
@ -522,6 +527,7 @@ void SMTEncoder::endVisit(UnaryOperation const& _op)
arrayIndexAssignment(_op.subExpression(), symbVar->currentValue());
else
m_errorReporter.warning(
2683_error,
_op.location(),
"Assertion checker does not yet implement \"delete\" for this expression."
);
@ -530,6 +536,7 @@ void SMTEncoder::endVisit(UnaryOperation const& _op)
}
default:
m_errorReporter.warning(
3682_error,
_op.location(),
"Assertion checker does not yet implement this operator."
);
@ -568,6 +575,7 @@ void SMTEncoder::endVisit(BinaryOperation const& _op)
compareOperation(_op);
else
m_errorReporter.warning(
3876_error,
_op.location(),
"Assertion checker does not yet implement this operator."
);
@ -580,6 +588,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
if (_funCall.annotation().kind == FunctionCallKind::StructConstructorCall)
{
m_errorReporter.warning(
4639_error,
_funCall.location(),
"Assertion checker does not yet implement this expression."
);
@ -639,6 +648,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
}
default:
m_errorReporter.warning(
4588_error,
_funCall.location(),
"Assertion checker does not yet implement this type of function call."
);
@ -771,6 +781,7 @@ void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall)
}
m_errorReporter.warning(
5084_error,
_funCall.location(),
"Type conversion is not yet fully supported and might yield false positives."
);
@ -800,6 +811,7 @@ void SMTEncoder::endVisit(Literal const& _literal)
else
{
m_errorReporter.warning(
7885_error,
_literal.location(),
"Assertion checker does not yet support the type of this literal (" +
_literal.annotation().type->toString() +
@ -850,6 +862,7 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess)
accessedName = identifier->name();
else
m_errorReporter.warning(
9551_error,
_memberAccess.location(),
"Assertion checker does not yet support this expression."
);
@ -879,6 +892,7 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess)
}
else
m_errorReporter.warning(
7650_error,
_memberAccess.location(),
"Assertion checker does not yet support this expression."
);
@ -903,6 +917,7 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
if (varDecl->type()->category() == Type::Category::FixedBytes)
{
m_errorReporter.warning(
7989_error,
_indexAccess.location(),
"Assertion checker does not yet support index accessing fixed bytes."
);
@ -917,6 +932,7 @@ void SMTEncoder::endVisit(IndexAccess const& _indexAccess)
else
{
m_errorReporter.warning(
9118_error,
_indexAccess.location(),
"Assertion checker does not yet implement this expression."
);
@ -940,6 +956,7 @@ void SMTEncoder::endVisit(IndexRangeAccess const& _indexRangeAccess)
{
createExpr(_indexRangeAccess);
m_errorReporter.warning(
2923_error,
_indexRangeAccess.location(),
"Assertion checker does not yet implement this expression."
);
@ -1019,6 +1036,7 @@ void SMTEncoder::arrayIndexAssignment(Expression const& _expr, smt::Expression c
else
{
m_errorReporter.warning(
9056_error,
_expr.location(),
"Assertion checker does not yet implement this expression."
);
@ -1034,6 +1052,7 @@ void SMTEncoder::defineGlobalVariable(string const& _name, Expression const& _ex
bool abstract = m_context.createGlobalSymbol(_name, _expr);
if (abstract)
m_errorReporter.warning(
1695_error,
_expr.location(),
"Assertion checker does not yet support this global variable."
);
@ -1087,6 +1106,7 @@ void SMTEncoder::arithmeticOperation(BinaryOperation const& _op)
}
default:
m_errorReporter.warning(
5188_error,
_op.location(),
"Assertion checker does not yet implement this operator."
);
@ -1094,6 +1114,7 @@ void SMTEncoder::arithmeticOperation(BinaryOperation const& _op)
}
else
m_errorReporter.warning(
9011_error,
_op.location(),
"Assertion checker does not yet implement this operator for type " + type->richIdentifier() + "."
);
@ -1185,6 +1206,7 @@ void SMTEncoder::compareOperation(BinaryOperation const& _op)
}
else
m_errorReporter.warning(
7229_error,
_op.location(),
"Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for comparisons"
);
@ -1213,6 +1235,7 @@ void SMTEncoder::booleanOperation(BinaryOperation const& _op)
}
else
m_errorReporter.warning(
3263_error,
_op.location(),
"Assertion checker does not yet implement the type " + _op.annotation().commonType->toString() + " for boolean operations"
);
@ -1245,6 +1268,7 @@ void SMTEncoder::assignment(
m_context.newValue(*varDecl);
m_errorReporter.warning(
6191_error,
_location,
"Assertion checker does not yet implement type " + _type->toString()
);
@ -1272,6 +1296,7 @@ void SMTEncoder::assignment(
}
else
m_errorReporter.warning(
8182_error,
_location,
"Assertion checker does not yet implement such assignments."
);
@ -1482,6 +1507,7 @@ bool SMTEncoder::createVariable(VariableDeclaration const& _varDecl)
if (abstract)
{
m_errorReporter.warning(
8115_error,
_varDecl.location(),
"Assertion checker does not yet support the type of this variable."
);
@ -1494,7 +1520,7 @@ smt::Expression SMTEncoder::expr(Expression const& _e, TypePointer _targetType)
{
if (!m_context.knownExpression(_e))
{
m_errorReporter.warning(_e.location(), "Internal error: Expression undefined for SMT solver." );
m_errorReporter.warning(6031_error, _e.location(), "Internal error: Expression undefined for SMT solver." );
createExpr(_e);
}
@ -1506,6 +1532,7 @@ void SMTEncoder::createExpr(Expression const& _e)
bool abstract = m_context.createExpression(_e);
if (abstract)
m_errorReporter.warning(
8364_error,
_e.location(),
"Assertion checker does not yet implement type " + _e.annotation().type->toString()
);

View File

@ -21,7 +21,6 @@
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem/operations.hpp>
#include <array>
#include <fstream>

View File

@ -73,9 +73,10 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
);
abi.emplace(std::move(method));
}
if (_contractDef.constructor())
FunctionDefinition const* constructor = _contractDef.constructor();
if (constructor && constructor->visibility() >= Visibility::Public)
{
FunctionType constrType(*_contractDef.constructor());
FunctionType constrType(*constructor);
FunctionType const* externalFunctionType = constrType.interfaceFunctionType();
solAssert(!!externalFunctionType, "");
Json::Value method;

View File

@ -67,7 +67,7 @@
#include <json/json.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <utility>
using namespace std;
@ -239,7 +239,7 @@ bool CompilerStack::parse()
m_errorReporter.clear();
if (SemVerVersion{string(VersionString)}.isPrerelease())
m_errorReporter.warning("This is a pre-release compiler version, please do not use it in production.");
m_errorReporter.warning(3805_error, "This is a pre-release compiler version, please do not use it in production.");
Parser parser{m_errorReporter, m_evmVersion, m_parserErrorRecovery};
@ -949,6 +949,7 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string
else
{
m_errorReporter.parserError(
6275_error,
import->location(),
string("Source \"" + importPath + "\" not found: " + result.responseOrErrorMessage)
);
@ -1110,6 +1111,7 @@ void CompilerStack::compileContract(
compiledContract.runtimeObject.bytecode.size() > 0x6000
)
m_errorReporter.warning(
5574_error,
_contract.location(),
"Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). "
"This contract may not be deployable on mainnet. "

View File

@ -103,16 +103,14 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
{
Json::Value method(devDocumentation(fun->annotation().docTags));
// add the function, only if we have any documentation to add
Json::Value jsonReturn = extractReturnParameterDocs(fun->annotation().docTags, *fun);
if (!jsonReturn.empty())
method["returns"] = jsonReturn;
if (!method.empty())
{
// add the function, only if we have any documentation to add
Json::Value jsonReturn = extractReturnParameterDocs(fun->annotation().docTags, *fun);
if (!jsonReturn.empty())
method["returns"] = jsonReturn;
methods[it.second->externalSignature()] = method;
}
}
}

View File

@ -31,8 +31,7 @@
#include <libsolutil/JSON.h>
#include <libsolutil/Keccak256.h>
#include <boost/algorithm/cxx11/any_of.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <algorithm>
#include <optional>
@ -491,7 +490,7 @@ std::optional<Json::Value> checkOutputSelection(Json::Value const& _outputSelect
}
/// Validates the optimizer settings and returns them in a parsed object.
/// On error returns the json-formatted error message.
boost::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Value const& _jsonInput)
std::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Value const& _jsonInput)
{
if (auto result = checkOptimizerKeys(_jsonInput))
return *result;
@ -552,7 +551,7 @@ boost::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Valu
}
boost::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler::parseInput(Json::Value const& _input)
std::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompiler::parseInput(Json::Value const& _input)
{
InputsAndSettings ret;
@ -740,10 +739,10 @@ boost::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompile
if (settings.isMember("optimizer"))
{
auto optimiserSettings = parseOptimizerSettings(settings["optimizer"]);
if (optimiserSettings.type() == typeid(Json::Value))
return boost::get<Json::Value>(std::move(optimiserSettings)); // was an error
if (std::holds_alternative<Json::Value>(optimiserSettings))
return std::get<Json::Value>(std::move(optimiserSettings)); // was an error
else
ret.optimiserSettings = boost::get<OptimiserSettings>(std::move(optimiserSettings));
ret.optimiserSettings = std::get<OptimiserSettings>(std::move(optimiserSettings));
}
Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue));
@ -1155,9 +1154,9 @@ Json::Value StandardCompiler::compile(Json::Value const& _input) noexcept
try
{
auto parsed = parseInput(_input);
if (parsed.type() == typeid(Json::Value))
return boost::get<Json::Value>(std::move(parsed));
InputsAndSettings settings = boost::get<InputsAndSettings>(std::move(parsed));
if (std::holds_alternative<Json::Value>(parsed))
return std::get<Json::Value>(std::move(parsed));
InputsAndSettings settings = std::get<InputsAndSettings>(std::move(parsed));
if (settings.language == "Solidity")
return compileSolidity(std::move(settings));
else if (settings.language == "Yul")

View File

@ -24,9 +24,9 @@
#include <libsolidity/interface/CompilerStack.h>
#include <boost/variant.hpp>
#include <optional>
#include <utility>
#include <variant>
namespace solidity::frontend
{
@ -73,7 +73,7 @@ private:
/// Parses the input json (and potentially invokes the read callback) and either returns
/// it in condensed form or an error as a json object.
boost::variant<InputsAndSettings, Json::Value> parseInput(Json::Value const& _input);
std::variant<InputsAndSettings, Json::Value> parseInput(Json::Value const& _input);
Json::Value compileSolidity(InputsAndSettings _inputsAndSettings);
Json::Value compileYul(InputsAndSettings _inputsAndSettings);

View File

@ -195,5 +195,5 @@ void DocStringParser::newTag(string const& _tagName)
void DocStringParser::appendError(string const& _description)
{
m_errorsOccurred = true;
m_errorReporter->docstringParsingError(_description);
m_errorReporter->docstringParsingError(9440_error, _description);
}

View File

@ -103,7 +103,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
nodes.push_back(parseEnumDefinition());
break;
default:
fatalParserError(string("Expected pragma, import directive or contract/interface/library/struct/enum definition."));
fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum definition.");
}
}
solAssert(m_recursionDepth == 0, "");
@ -128,9 +128,10 @@ void Parser::parsePragmaVersion(SourceLocation const& _location, vector<Token> c
// so we don't need to report anything here.
if (!m_parserErrorRecovery)
m_errorReporter.fatalParserError(
5333_error,
_location,
"Source file requires different compiler version (current compiler is " +
string(VersionString) + " - note that nightly builds are considered to be "
string(VersionString) + ") - note that nightly builds are considered to be "
"strictly less than the released version"
);
}
@ -162,7 +163,7 @@ ASTPointer<PragmaDirective> Parser::parsePragmaDirective()
{
Token token = m_scanner->currentToken();
if (token == Token::Illegal)
parserError("Token incompatible with Solidity parser as part of pragma directive.");
parserError(6281_error, "Token incompatible with Solidity parser as part of pragma directive.");
else
{
string literal = m_scanner->currentLiteral();
@ -240,18 +241,18 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
unitAlias = expectIdentifierToken();
}
else
fatalParserError("Expected string literal (path), \"*\" or alias list.");
fatalParserError(9478_error, "Expected string literal (path), \"*\" or alias list.");
// "from" is not a keyword but parsed as an identifier because of backwards
// compatibility and because it is a really common word.
if (m_scanner->currentToken() != Token::Identifier || m_scanner->currentLiteral() != "from")
fatalParserError("Expected \"from\".");
fatalParserError(8208_error, "Expected \"from\".");
m_scanner->next();
if (m_scanner->currentToken() != Token::StringLiteral)
fatalParserError("Expected import path.");
fatalParserError(6845_error, "Expected import path.");
path = getLiteralAndAdvance();
}
if (path->empty())
fatalParserError("Import path cannot be empty.");
fatalParserError(6326_error, "Import path cannot be empty.");
nodeFactory.markEndPosition();
expectToken(Token::Semicolon);
return nodeFactory.createNode<ImportDirective>(path, unitAlias, move(symbolAliases));
@ -278,7 +279,7 @@ std::pair<ContractKind, bool> Parser::parseContractKind()
kind = ContractKind::Library;
break;
default:
parserError("Expected keyword \"contract\", \"interface\" or \"library\".");
parserError(3515_error, "Expected keyword \"contract\", \"interface\" or \"library\".");
return std::make_pair(ContractKind::Contract, abstract);
}
m_scanner->next();
@ -343,7 +344,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
else if (currentTokenValue == Token::Using)
subNodes.push_back(parseUsingDirective());
else
fatalParserError(string("Function, variable, struct or modifier declaration expected."));
fatalParserError(9182_error, "Function, variable, struct or modifier declaration expected.");
}
}
catch (FatalError const&)
@ -462,6 +463,7 @@ StateMutability Parser::parseStateMutability()
case Token::Constant:
stateMutability = StateMutability::View;
parserError(
7698_error,
"The state mutability modifier \"constant\" was removed in version 0.5.0. "
"Use \"view\" or \"pure\" instead."
);
@ -494,11 +496,12 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari
// Detect this and return early.
if (_isStateVariable && (result.visibility == Visibility::External || result.visibility == Visibility::Internal))
break;
parserError(string(
parserError(
9439_error,
"Visibility already specified as \"" +
Declaration::visibilityToString(result.visibility) +
"\"."
));
);
m_scanner->next();
}
else
@ -508,11 +511,12 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari
{
if (result.stateMutability != StateMutability::NonPayable)
{
parserError(string(
parserError(
9680_error,
"State mutability already specified as \"" +
stateMutabilityToString(result.stateMutability) +
"\"."
));
);
m_scanner->next();
}
else
@ -521,14 +525,14 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari
else if (!_isStateVariable && token == Token::Override)
{
if (result.overrides)
parserError("Override already specified.");
parserError(1827_error, "Override already specified.");
result.overrides = parseOverrideSpecifier();
}
else if (!_isStateVariable && token == Token::Virtual)
{
if (result.isVirtual)
parserError("Virtual already specified.");
parserError(6879_error, "Virtual already specified.");
result.isVirtual = true;
m_scanner->next();
@ -576,9 +580,9 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinition()
"the \"function\" keyword to define it."
};
if (m_scanner->currentToken() == Token::Constructor)
parserError(message);
parserError(3323_error, message);
else
parserWarning(message);
parserWarning(3445_error, message);
m_scanner->next();
}
else
@ -659,10 +663,10 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
break;
expectToken(Token::Comma);
if (m_scanner->currentToken() != Token::Identifier)
fatalParserError(string("Expected identifier after ','"));
fatalParserError(1612_error, "Expected identifier after ','");
}
if (members.empty())
parserError({"enum with no members is not allowed."});
parserError(3147_error, "enum with no members is not allowed.");
nodeFactory.markEndPosition();
expectToken(Token::RBrace);
@ -689,6 +693,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
if (dynamic_cast<FunctionTypeName*>(type.get()) && _options.isStateVariable && m_scanner->currentToken() == Token::LBrace)
fatalParserError(
2915_error,
"Expected a state variable declaration. If you intended this as a fallback function "
"or a function to handle plain ether transactions, use the \"fallback\" keyword "
"or the \"receive\" keyword instead."
@ -709,11 +714,12 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
nodeFactory.markEndPosition();
if (visibility != Visibility::Default)
{
parserError(string(
parserError(
4110_error,
"Visibility already specified as \"" +
Declaration::visibilityToString(visibility) +
"\"."
));
);
m_scanner->next();
}
else
@ -722,7 +728,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
else if (_options.isStateVariable && token == Token::Override)
{
if (overrides)
parserError("Override already specified.");
parserError(9125_error, "Override already specified.");
overrides = parseOverrideSpecifier();
}
@ -734,6 +740,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
{
if (mutability != VariableDeclaration::Mutability::Mutable)
parserError(
3109_error,
string("Mutability already set to ") +
(mutability == VariableDeclaration::Mutability::Constant ? "\"constant\"" : "\"immutable\"")
);
@ -745,9 +752,9 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
else if (_options.allowLocationSpecifier && TokenTraits::isLocationSpecifier(token))
{
if (location != VariableDeclaration::Location::Unspecified)
parserError(string("Location already specified."));
parserError(3548_error, "Location already specified.");
else if (!type)
parserError(string("Location specifier needs explicit type name."));
parserError(7439_error, "Location specifier needs explicit type name.");
else
{
switch (token)
@ -836,13 +843,13 @@ ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
if (m_scanner->currentToken() == Token::Override)
{
if (overrides)
parserError("Override already specified.");
parserError(9102_error, "Override already specified.");
overrides = parseOverrideSpecifier();
}
else if (m_scanner->currentToken() == Token::Virtual)
{
if (isVirtual)
parserError("Virtual already specified.");
parserError(2662_error, "Virtual already specified.");
isVirtual = true;
m_scanner->next();
@ -990,7 +997,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
}
else
{
parserError("State mutability can only be specified for address types.");
parserError(9106_error, "State mutability can only be specified for address types.");
m_scanner->next();
}
}
@ -999,7 +1006,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
else if (token == Token::Var)
{
if (!_allowVar)
parserError(string("Expected explicit type name."));
parserError(7059_error, "Expected explicit type name.");
m_scanner->next();
}
else if (token == Token::Function)
@ -1009,7 +1016,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
else if (token == Token::Identifier)
type = parseUserDefinedTypeName();
else
fatalParserError(string("Expected type name"));
fatalParserError(3546_error, "Expected type name");
if (type)
// Parse "[...]" postfixes for arrays.
@ -1052,7 +1059,7 @@ ASTPointer<Mapping> Parser::parseMapping()
m_scanner->next();
}
else
fatalParserError(string("Expected elementary type name or identifier for mapping key type"));
fatalParserError(1005_error, "Expected elementary type name or identifier for mapping key type");
expectToken(Token::Arrow);
bool const allowVar = false;
ASTPointer<TypeName> valueType = parseTypeName(allowVar);
@ -1078,7 +1085,7 @@ ASTPointer<ParameterList> Parser::parseParameterList(
while (m_scanner->currentToken() != Token::RParen)
{
if (m_scanner->currentToken() == Token::Comma && m_scanner->peekNextToken() == Token::RParen)
fatalParserError("Unexpected trailing comma in parameter list.");
fatalParserError(7591_error, "Unexpected trailing comma in parameter list.");
expectToken(Token::Comma);
parameters.push_back(parseVariableDeclaration(options));
}
@ -1213,7 +1220,7 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con
if (m_scanner->currentToken() == Token::StringLiteral)
{
if (m_scanner->currentLiteral() != "evmasm")
fatalParserError("Only \"evmasm\" supported.");
fatalParserError(4531_error, "Only \"evmasm\" supported.");
// This can be used in the future to set the dialect.
m_scanner->next();
}
@ -1376,7 +1383,7 @@ ASTPointer<EmitStatement> Parser::parseEmitStatement(ASTPointer<ASTString> const
ASTNodeFactory eventCallNodeFactory(*this);
if (m_scanner->currentToken() != Token::Identifier)
fatalParserError("Expected event name or path.");
fatalParserError(5620_error, "Expected event name or path.");
IndexAccessedPath iap;
while (true)
@ -1844,7 +1851,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
nodeFactory.markEndPosition();
m_scanner->next();
if (m_scanner->currentToken() == Token::Illegal)
fatalParserError(to_string(m_scanner->currentError()));
fatalParserError(5428_error, to_string(m_scanner->currentError()));
expression = nodeFactory.createNode<Literal>(token, make_shared<ASTString>(literal));
break;
}
@ -1875,7 +1882,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
if (m_scanner->currentToken() != Token::Comma && m_scanner->currentToken() != oppositeToken)
components.push_back(parseExpression());
else if (isArray)
parserError("Expected expression (inline array elements cannot be omitted).");
parserError(4799_error, "Expected expression (inline array elements cannot be omitted).");
else
components.push_back(ASTPointer<Expression>());
@ -1890,7 +1897,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
break;
}
case Token::Illegal:
fatalParserError(to_string(m_scanner->currentError()));
fatalParserError(8936_error, to_string(m_scanner->currentError()));
break;
default:
if (TokenTraits::isElementaryTypeName(token))
@ -1906,7 +1913,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
m_scanner->next();
}
else
fatalParserError(string("Expected primary expression."));
fatalParserError(6933_error, "Expected primary expression.");
break;
}
return expression;
@ -1964,7 +1971,7 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars
m_scanner->peekNextToken() == Token::RBrace
)
{
parserError("Unexpected trailing comma.");
parserError(2074_error, "Unexpected trailing comma.");
m_scanner->next();
}
@ -2083,7 +2090,7 @@ ASTPointer<TypeName> Parser::typeNameFromIndexAccessStructure(Parser::IndexAcces
for (auto const& lengthExpression: _iap.indices)
{
if (lengthExpression.end)
parserError(lengthExpression.location, "Expected array length expression.");
parserError(5464_error, lengthExpression.location, "Expected array length expression.");
nodeFactory.setLocation(lengthExpression.location);
type = nodeFactory.createNode<ArrayTypeName>(type, lengthExpression.start);
}

View File

@ -24,7 +24,6 @@
#pragma once
#include <libsolutil/Common.h>
#include <boost/filesystem.hpp>
#include <sstream>
#include <string>

View File

@ -527,7 +527,7 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation
// Similarly we assume bitwise shifting and create2 go together.
yulAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), "");
auto errorForVM = [=](string const& vmKindMessage) {
auto errorForVM = [&](string const& vmKindMessage) {
typeError(
_location,
"The \"" +
@ -583,6 +583,7 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation
)
{
m_errorReporter.error(
4316_error,
Error::Type::SyntaxError,
_location,
"Jump instructions and labels are low-level EVM features that can lead to "
@ -599,13 +600,13 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation
void AsmAnalyzer::typeError(SourceLocation const& _location, string const& _description)
{
m_errorReporter.typeError(_location, _description);
m_errorReporter.typeError(7569_error, _location, _description);
m_success = false;
}
void AsmAnalyzer::declarationError(SourceLocation const& _location, string const& _description)
{
m_errorReporter.declarationError(_location, _description);
m_errorReporter.declarationError(9595_error, _location, _description);
m_success = false;
}

View File

@ -20,6 +20,8 @@
* Converts inline assembly AST to JSON format
*/
#pragma once
#include <libyul/AsmDataForward.h>
#include <liblangutil/SourceLocation.h>
#include <json/json.h>

View File

@ -122,11 +122,11 @@ Statement Parser::parseStatement()
if (currentToken() == Token::Default)
_switch.cases.emplace_back(parseCase());
if (currentToken() == Token::Default)
fatalParserError("Only one default case allowed.");
fatalParserError(6931_error, "Only one default case allowed.");
else if (currentToken() == Token::Case)
fatalParserError("Case not allowed after default case.");
fatalParserError(4904_error, "Case not allowed after default case.");
if (_switch.cases.empty())
fatalParserError("Switch statement without any cases.");
fatalParserError(2418_error, "Switch statement without any cases.");
_switch.location.end = _switch.cases.back().body.location.end;
return Statement{move(_switch)};
}
@ -151,7 +151,7 @@ Statement Parser::parseStatement()
{
Statement stmt{createWithLocation<Leave>()};
if (!m_insideFunction)
m_errorReporter.syntaxError(currentLocation(), "Keyword \"leave\" can only be used inside a function.");
m_errorReporter.syntaxError(8149_error, currentLocation(), "Keyword \"leave\" can only be used inside a function.");
m_scanner->next();
return stmt;
}
@ -184,6 +184,7 @@ Statement Parser::parseStatement()
auto const token = currentToken() == Token::Comma ? "," : ":=";
fatalParserError(
2856_error,
std::string("Variable name must precede \"") +
token +
"\"" +
@ -194,7 +195,7 @@ Statement Parser::parseStatement()
auto const& identifier = std::get<Identifier>(elementary);
if (m_dialect.builtin(identifier.name))
fatalParserError("Cannot assign to builtin function \"" + identifier.name.str() + "\".");
fatalParserError(6272_error, "Cannot assign to builtin function \"" + identifier.name.str() + "\".");
variableNames.emplace_back(identifier);
@ -218,7 +219,7 @@ Statement Parser::parseStatement()
return Statement{std::move(assignment)};
}
default:
fatalParserError("Call or assignment expected.");
fatalParserError(6913_error, "Call or assignment expected.");
break;
}
@ -250,7 +251,7 @@ Case Parser::parseCase()
advance();
ElementaryOperation literal = parseElementaryOperation();
if (!holds_alternative<Literal>(literal))
fatalParserError("Literal expected.");
fatalParserError(4805_error, "Literal expected.");
_case.value = make_unique<Literal>(std::get<Literal>(std::move(literal)));
}
else
@ -325,6 +326,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
case Token::Bool:
case Token::Address:
case Token::Var:
case Token::In:
{
YulString literal{currentLiteral()};
if (m_dialect.builtin(literal))
@ -352,7 +354,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
break;
case Token::Number:
if (!isValidNumberLiteral(currentLiteral()))
fatalParserError("Invalid number literal.");
fatalParserError(4828_error, "Invalid number literal.");
kind = LiteralKind::Number;
break;
case Token::TrueLiteral:
@ -381,7 +383,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
break;
}
default:
fatalParserError("Literal or identifier expected.");
fatalParserError(1856_error, "Literal or identifier expected.");
}
return ret;
}
@ -416,6 +418,7 @@ FunctionDefinition Parser::parseFunctionDefinition()
if (m_currentForLoopComponent == ForLoopComponent::ForLoopPre)
m_errorReporter.syntaxError(
3441_error,
currentLocation(),
"Functions cannot be defined inside a for-loop init block."
);
@ -470,7 +473,7 @@ Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp)
else if (holds_alternative<FunctionCall>(_initialOp))
ret = std::move(std::get<FunctionCall>(_initialOp));
else
fatalParserError("Function name expected.");
fatalParserError(9980_error, "Function name expected.");
expectToken(Token::LParen);
if (currentToken() != Token::RParen)
@ -515,6 +518,7 @@ YulString Parser::expectAsmIdentifier()
case Token::Bool:
case Token::Identifier:
case Token::Var:
case Token::In:
break;
default:
expectToken(Token::Identifier);
@ -522,7 +526,7 @@ YulString Parser::expectAsmIdentifier()
}
if (m_dialect.builtin(name))
fatalParserError("Cannot use builtin function name \"" + name.str() + "\" as identifier name.");
fatalParserError(5568_error, "Cannot use builtin function name \"" + name.str() + "\" as identifier name.");
advance();
return name;
}
@ -532,13 +536,13 @@ void Parser::checkBreakContinuePosition(string const& _which)
switch (m_currentForLoopComponent)
{
case ForLoopComponent::None:
m_errorReporter.syntaxError(currentLocation(), "Keyword \"" + _which + "\" needs to be inside a for-loop body.");
m_errorReporter.syntaxError(2592_error, currentLocation(), "Keyword \"" + _which + "\" needs to be inside a for-loop body.");
break;
case ForLoopComponent::ForLoopPre:
m_errorReporter.syntaxError(currentLocation(), "Keyword \"" + _which + "\" in for-loop init block is not allowed.");
m_errorReporter.syntaxError(9615_error, currentLocation(), "Keyword \"" + _which + "\" in for-loop init block is not allowed.");
break;
case ForLoopComponent::ForLoopPost:
m_errorReporter.syntaxError(currentLocation(), "Keyword \"" + _which + "\" in for-loop post block is not allowed.");
m_errorReporter.syntaxError(2461_error, currentLocation(), "Keyword \"" + _which + "\" in for-loop post block is not allowed.");
break;
case ForLoopComponent::ForLoopBody:
break;

View File

@ -29,8 +29,6 @@
#include <libsolutil/CommonData.h>
#include <boost/range/adaptor/reversed.hpp>
#include <memory>
#include <functional>
@ -141,6 +139,7 @@ bool ScopeFiller::registerVariable(TypedName const& _name, SourceLocation const&
{
//@TODO secondary location
m_errorReporter.declarationError(
1395_error,
_location,
"Variable name " + _name.name.str() + " already taken in this scope."
);
@ -161,6 +160,7 @@ bool ScopeFiller::registerFunction(FunctionDefinition const& _funDef)
{
//@TODO secondary location
m_errorReporter.declarationError(
6052_error,
_funDef.location,
"Function name " + _funDef.name.str() + " already taken in this scope."
);

View File

@ -66,7 +66,7 @@ shared_ptr<Object> ObjectParser::parseObject(Object* _containingObject)
RecursionGuard guard(*this);
if (currentToken() != Token::Identifier || currentLiteral() != "object")
fatalParserError("Expected keyword \"object\".");
fatalParserError(4294_error, "Expected keyword \"object\".");
advance();
shared_ptr<Object> ret = make_shared<Object>();
@ -83,7 +83,7 @@ shared_ptr<Object> ObjectParser::parseObject(Object* _containingObject)
else if (currentToken() == Token::Identifier && currentLiteral() == "data")
parseData(*ret);
else
fatalParserError("Expected keyword \"data\" or \"object\" or \"}\".");
fatalParserError(8143_error, "Expected keyword \"data\" or \"object\" or \"}\".");
}
if (_containingObject)
addNamedSubObject(*_containingObject, ret->name, ret);
@ -96,7 +96,7 @@ shared_ptr<Object> ObjectParser::parseObject(Object* _containingObject)
shared_ptr<Block> ObjectParser::parseCode()
{
if (currentToken() != Token::Identifier || currentLiteral() != "code")
fatalParserError("Expected keyword \"code\".");
fatalParserError(4846_error, "Expected keyword \"code\".");
advance();
return parseBlock();
@ -133,11 +133,11 @@ YulString ObjectParser::parseUniqueName(Object const* _containingObject)
expectToken(Token::StringLiteral, false);
YulString name{currentLiteral()};
if (name.empty())
parserError("Object name cannot be empty.");
parserError(3287_error, "Object name cannot be empty.");
else if (_containingObject && _containingObject->name == name)
parserError("Object name cannot be the same as the name of the containing object.");
parserError(8311_error, "Object name cannot be the same as the name of the containing object.");
else if (_containingObject && _containingObject->subIndexByName.count(name))
parserError("Object name \"" + name.str() + "\" already exists inside the containing object.");
parserError(8794_error, "Object name \"" + name.str() + "\" already exists inside the containing object.");
advance();
return name;
}

View File

@ -38,9 +38,6 @@ using namespace solidity;
using namespace solidity::yul;
using namespace solidity::util;
using boost::split;
using boost::is_any_of;
string solidity::yul::reindent(string const& _code)
{
int constexpr indentationWidth = 4;
@ -55,7 +52,7 @@ string solidity::yul::reindent(string const& _code)
};
vector<string> lines;
split(lines, _code, is_any_of("\n"));
boost::split(lines, _code, boost::is_any_of("\n"));
for (string& line: lines)
boost::trim(line);

View File

@ -85,11 +85,11 @@ void VariableReferenceCounter::operator()(Block const& _block)
void VariableReferenceCounter::increaseRefIfFound(YulString _variableName)
{
m_scope->lookup(_variableName, GenericVisitor{
[=](Scope::Variable const& _var)
[&](Scope::Variable const& _var)
{
++m_context.variableReferences[&_var];
},
[=](Scope::Function const&) { }
[](Scope::Function const&) { }
});
}
@ -184,11 +184,13 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
}
else
{
m_assembly.setSourceLocation(_varDecl.location);
int variablesLeft = numVariables;
while (variablesLeft--)
m_assembly.appendConstant(u256(0));
}
m_assembly.setSourceLocation(_varDecl.location);
bool atTopOfStack = true;
for (int varIndex = numVariables - 1; varIndex >= 0; --varIndex)
{
@ -203,7 +205,6 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
if (atTopOfStack)
{
m_context->variableStackHeights.erase(&var);
m_assembly.setSourceLocation(_varDecl.location);
m_assembly.appendInstruction(evmasm::Instruction::POP);
}
else
@ -216,7 +217,6 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
int slot = *m_unusedStackSlots.begin();
m_unusedStackSlots.erase(m_unusedStackSlots.begin());
m_context->variableStackHeights[&var] = slot;
m_assembly.setSourceLocation(_varDecl.location);
if (int heightDiff = variableHeightDiff(var, varName, true))
m_assembly.appendInstruction(evmasm::swapInstruction(heightDiff - 1));
m_assembly.appendInstruction(evmasm::Instruction::POP);
@ -272,7 +272,7 @@ void CodeTransform::operator()(FunctionCall const& _call)
Scope::Function* function = nullptr;
yulAssert(m_scope->lookup(_call.functionName.name, GenericVisitor{
[=](Scope::Variable&) { yulAssert(false, "Expected function name."); },
[](Scope::Variable&) { yulAssert(false, "Expected function name."); },
[&](Scope::Function& _function) { function = &_function; }
}), "Function name not found.");
yulAssert(function, "");
@ -296,7 +296,7 @@ void CodeTransform::operator()(Identifier const& _identifier)
// First search internals, then externals.
yulAssert(m_scope, "");
if (m_scope->lookup(_identifier.name, GenericVisitor{
[=](Scope::Variable& _var)
[&](Scope::Variable& _var)
{
// TODO: opportunity for optimization: Do not DUP if this is the last reference
// to the top most element of the stack
@ -307,7 +307,7 @@ void CodeTransform::operator()(Identifier const& _identifier)
m_assembly.appendConstant(u256(0));
decreaseReference(_identifier.name, _var);
},
[=](Scope::Function&)
[](Scope::Function&)
{
yulAssert(false, "Function not removed during desugaring.");
}

View File

@ -1232,7 +1232,7 @@ Object EVMToEwasmTranslator::run(Object const& _object)
FunctionHoister::run(context, ast);
FunctionGrouper::run(context, ast);
MainFunction{}(ast);
MainFunction::run(context, ast);
ForLoopConditionIntoBody::run(context, ast);
ExpressionSplitter::run(context, ast);
WordSizeTransform::run(m_dialect, WasmDialect::instance(), ast, nameDispenser);

View File

@ -29,9 +29,6 @@
#include <liblangutil/Exceptions.h>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/adaptor/transformed.hpp>
using namespace std;
using namespace solidity;
using namespace solidity::yul;

View File

@ -25,7 +25,6 @@
#include <libsolutil/Visitor.h>
#include <boost/range/algorithm_ext/erase.hpp>
#include <boost/algorithm/cxx11/any_of.hpp>
using namespace std;
using namespace solidity;
@ -60,8 +59,9 @@ void removeEmptyDefaultFromSwitch(Switch& _switchStmt)
void removeEmptyCasesFromSwitch(Switch& _switchStmt)
{
bool hasDefault = boost::algorithm::any_of(
_switchStmt.cases,
bool hasDefault = std::any_of(
_switchStmt.cases.begin(),
_switchStmt.cases.end(),
[](Case const& _case) { return !_case.value; }
);

View File

@ -31,8 +31,6 @@
#include <libsolutil/CommonData.h>
#include <libsolutil/Visitor.h>
#include <boost/range/adaptor/reversed.hpp>
using namespace std;
using namespace solidity;
using namespace solidity::yul;

View File

@ -34,8 +34,6 @@
#include <libsolutil/CommonData.h>
#include <libsolutil/Visitor.h>
#include <boost/range/adaptor/reversed.hpp>
using namespace std;
using namespace solidity;
using namespace solidity::yul;

View File

@ -4,6 +4,7 @@ planned state of the optimiser.
Table of Contents:
- [Selecting optimisations](#selecting-optimisations)
- [Preprocessing](#preprocessing)
- [Pseudo-SSA Transformation](#pseudo-ssa-transformation)
- [Tools](#tools)
@ -33,6 +34,17 @@ the following transformation steps are the main components:
- [Redundant Assign Eliminator](#redundant-assign-eliminator)
- [Full Function Inliner](#full-function-inliner)
## Selecting optimisations
By default the optimiser applies its predefined sequence of optimisation steps to the generated assembly.
You can override this sequence and supply your own using the `--yul-optimizations` option:
``` bash
solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul'
```
There's a [table listing available abbreviations in the optimiser docs](/docs/yul.rst#optimization-step-sequence).
## Preprocessing
The preprocessing components perform transformations to get the program

View File

@ -306,7 +306,7 @@ void RedundantAssignEliminator::finalize(YulString _variable, RedundantAssignEli
void AssignmentRemover::operator()(Block& _block)
{
boost::range::remove_erase_if(_block.statements, [=](Statement const& _statement) -> bool {
boost::range::remove_erase_if(_block.statements, [&](Statement const& _statement) -> bool {
return holds_alternative<Assignment>(_statement) && m_toRemove.count(&std::get<Assignment>(_statement));
});

View File

@ -22,7 +22,6 @@
#include <libsolutil/Visitor.h>
#include <boost/range/algorithm_ext/erase.hpp>
#include <boost/algorithm/cxx11/any_of.hpp>
using namespace std;
using namespace solidity;

View File

@ -29,8 +29,6 @@
#include <libyul/Dialect.h>
#include <libyul/SideEffects.h>
#include <boost/algorithm/cxx11/none_of.hpp>
using namespace std;
using namespace solidity;
using namespace solidity::yul;
@ -85,9 +83,10 @@ void UnusedPruner::operator()(Block& _block)
// movable or it returns a single value. In the latter case, we
// replace `let a := f()` by `pop(f())` (in pure Yul, this will be
// `drop(f())`).
if (boost::algorithm::none_of(
varDecl.variables,
[=](TypedName const& _typedName) { return used(_typedName.name); }
if (std::none_of(
varDecl.variables.begin(),
varDecl.variables.end(),
[&](TypedName const& _typedName) { return used(_typedName.name); }
))
{
if (!varDecl.value)

View File

@ -0,0 +1,156 @@
import random
import re
import os
from os import path
ENCODING = "utf-8"
PATTERN = r"\b\d+_error\b"
def read_file(file_name):
content = None
try:
with open(file_name, "r", encoding=ENCODING) as f:
content = f.read()
finally:
if content == None:
print(f"Error reading: {file_name}")
return content
def write_file(file_name, content):
with open(file_name, "w", encoding=ENCODING) as f:
f.write(content)
def in_comment(source, pos):
slash_slash_pos = source.rfind("//", 0, pos)
lf_pos = source.rfind("\n", 0, pos)
if slash_slash_pos > lf_pos:
return True
slash_star_pos = source.rfind("/*", 0, pos)
star_slash_pos = source.rfind("*/", 0, pos)
return slash_star_pos > star_slash_pos
def find_ids_in_file(file_name, ids):
source = read_file(file_name)
for m in re.finditer(PATTERN, source):
if in_comment(source, m.start()):
continue
underscore_pos = m.group(0).index("_")
id = m.group(0)[0:underscore_pos]
if id in ids:
ids[id] += 1
else:
ids[id] = 1
def get_used_ids(file_names):
used_ids = {}
for file_name in file_names:
find_ids_in_file(file_name, used_ids)
return used_ids
def get_id(available_ids, used_ids):
while len(available_ids) > 0:
random.seed(len(available_ids))
k = random.randrange(len(available_ids))
id = list(available_ids.keys())[k]
del available_ids[id]
if id not in used_ids:
return id
assert False, "Out of IDs"
def fix_ids_in_file(file_name, available_ids, used_ids):
source = read_file(file_name)
k = 0
destination = []
for m in re.finditer(PATTERN, source):
destination.extend(source[k:m.start()])
underscore_pos = m.group(0).index("_")
id = m.group(0)[0:underscore_pos]
# incorrect id or id has a duplicate somewhere
if not in_comment(source, m.start()) and (len(id) != 4 or id[0] == "0" or used_ids[id] > 1):
assert id in used_ids
new_id = get_id(available_ids, used_ids)
used_ids[id] -= 1
else:
new_id = id
destination.extend(new_id + "_error")
k = m.end()
destination.extend(source[k:])
destination = ''.join(destination)
if source != destination:
write_file(file_name, destination)
print(f"Fixed file: {file_name}")
def fix_ids(used_ids, file_names):
available_ids = {str(id): None for id in range(1000, 10000)}
for file_name in file_names:
fix_ids_in_file(file_name, available_ids, used_ids)
def find_source_files(top_dir):
"""Builds the list of .h and .cpp files in top_dir directory"""
source_file_names = []
dirs = ['libevmasm', 'liblangutil', 'libsolc', 'libsolidity', 'libsolutil', 'libyul', 'solc']
for dir in dirs:
for root, _, file_names in os.walk(os.path.join(top_dir, dir), onerror=lambda e: exit(f"Walk error: {e}")):
for file_name in file_names:
_, ext = path.splitext(file_name)
if ext in [".h", ".cpp"]:
source_file_names.append(path.join(root, file_name))
return source_file_names
def main():
cwd = os.getcwd()
answer = input(
f"This script checks and corrects *_error literals in .h and .cpp files\n"
f"in {cwd}, recursively.\n\n"
f"Please commit current changes first, and review the results when the script finishes.\n\n"
f"Do you want to start [Y/N]? "
)
while len(answer) == 0 or answer not in "YNyn":
answer = input("[Y/N]? ")
if answer not in "yY":
return
source_file_names = find_source_files(cwd)
used_ids = get_used_ids(source_file_names)
ok = True
for id in sorted(used_ids):
if len(id) != 4:
print(f"ID {id} length != 4")
ok = False
if id[0] == "0":
print(f"ID {id} starts with zero")
ok = False
if used_ids[id] > 1:
print(f"ID {id} appears {used_ids[id]} times")
ok = False
if ok:
print("No incorrect IDs found")
else:
fix_ids(used_ids, source_file_names)
print("Fixing compteted")
if __name__ == "__main__":
main()

View File

@ -28,6 +28,6 @@
set -e
cd docs
pip install -r requirements.txt
pip3 install -r requirements.txt
sphinx-build -nW -b html -d _build/doctrees . _build/html
cd ..

View File

@ -100,7 +100,7 @@ do
force_abiv2_flag=""
if [[ "$abiv2" == "yes" ]]
then
force_abiv2_flag="--abiencoderv2 --optimize-yul"
force_abiv2_flag="--abiencoderv2"
fi
printTask "--> Running tests using "$optimize" --evm-version "$vm" $force_abiv2_flag..."

View File

@ -699,7 +699,7 @@ void CommandLineInterface::createFile(string const& _fileName, string const& _da
string pathName = (p / _fileName).string();
if (fs::exists(pathName) && !m_args.count(g_strOverwrite))
{
serr() << "Refusing to overwrite existing file \"" << pathName << "\" (use --overwrite to force)." << endl;
serr() << "Refusing to overwrite existing file \"" << pathName << "\" (use --" << g_strOverwrite << " to force)." << endl;
m_error = true;
return;
}
@ -719,10 +719,10 @@ bool CommandLineInterface::parseArguments(int _argc, char** _argv)
g_hasOutput = false;
// Declare the supported options.
po::options_description desc(R"(solc, the Solidity commandline compiler.
po::options_description desc((R"(solc, the Solidity commandline compiler.
This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you
are welcome to redistribute it under certain conditions. See 'solc --license'
are welcome to redistribute it under certain conditions. See 'solc --)" + g_strLicense + R"('
for details.
Usage: solc [options] [input_file...]
@ -732,9 +732,9 @@ at standard output or in files in the output directory, if specified.
Imports are automatically read from the filesystem, but it is also possible to
remap paths using the context:prefix=path syntax.
Example:
solc --bin -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol
solc --)" + g_argBinary + R"( -o /tmp/solcoutput dapp-bin=/usr/local/lib/dapp-bin contract.sol
Allowed options)",
Allowed options)").c_str(),
po::options_description::m_default_line_length,
po::options_description::m_default_line_length - 23
);
@ -780,20 +780,27 @@ Allowed options)",
)
(
g_argImportAst.c_str(),
"Import ASTs to be compiled, assumes input holds the AST in compact JSON format. "
"Supported Inputs is the output of the --standard-json or the one produced by --combined-json ast,compact-format"
("Import ASTs to be compiled, assumes input holds the AST in compact JSON format. "
"Supported Inputs is the output of the --" + g_argStandardJSON + " or the one produced by "
"--" + g_argCombinedJson + " " + g_strAst + "," + g_strCompactJSON).c_str()
)
(
g_argAssemble.c_str(),
"Switch to assembly mode, ignoring all options except --machine, --yul-dialect and --optimize and assumes input is assembly."
("Switch to assembly mode, ignoring all options except "
"--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " "
"and assumes input is assembly.").c_str()
)
(
g_argYul.c_str(),
"Switch to Yul mode, ignoring all options except --machine, --yul-dialect and --optimize and assumes input is Yul."
("Switch to Yul mode, ignoring all options except "
"--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " "
"and assumes input is Yul.").c_str()
)
(
g_argStrictAssembly.c_str(),
"Switch to strict assembly mode, ignoring all options except --machine, --yul-dialect and --optimize and assumes input is strict assembly."
("Switch to strict assembly mode, ignoring all options except "
"--" + g_argMachine + ", --" + g_strYulDialect + ", --" + g_argOptimize + " and --" + g_strYulOptimizations + " "
"and assumes input is strict assembly.").c_str()
)
(
g_strYulDialect.c_str(),
@ -807,8 +814,8 @@ Allowed options)",
)
(
g_argLink.c_str(),
"Switch to linker mode, ignoring all options apart from --libraries "
"and modify binaries in place."
("Switch to linker mode, ignoring all options apart from --" + g_argLibraries + " "
"and modify binaries in place.").c_str()
)
(
g_argMetadataHash.c_str(),
@ -835,7 +842,7 @@ Allowed options)",
"Set for how many contract runs to optimize. "
"Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage."
)
(g_strOptimizeYul.c_str(), "Legacy option, ignored. Use the general --optimize to enable Yul optimizer.")
(g_strOptimizeYul.c_str(), ("Legacy option, ignored. Use the general --" + g_argOptimize + " to enable Yul optimizer.").c_str())
(g_strNoOptimizeYul.c_str(), "Disable Yul optimizer in Solidity.")
(
g_strYulOptimizations.c_str(),
@ -933,7 +940,7 @@ Allowed options)",
for (string const& item: boost::split(requests, m_args[g_argCombinedJson].as<string>(), boost::is_any_of(",")))
if (!g_combinedJsonArgs.count(item))
{
serr() << "Invalid option to --combined-json: " << item << endl;
serr() << "Invalid option to --" << g_argCombinedJson << ": " << item << endl;
return false;
}
}
@ -1047,7 +1054,7 @@ bool CommandLineInterface::processInput()
std::optional<langutil::EVMVersion> versionOption = langutil::EVMVersion::fromString(versionOptionStr);
if (!versionOption)
{
serr() << "Invalid option for --evm-version: " << versionOptionStr << endl;
serr() << "Invalid option for --" << g_strEVMVersion << ": " << versionOptionStr << endl;
return false;
}
m_evmVersion = *versionOption;
@ -1064,14 +1071,37 @@ bool CommandLineInterface::processInput()
bool optimize = m_args.count(g_argOptimize);
if (m_args.count(g_strOptimizeYul))
{
serr() << "--optimize-yul is invalid in assembly mode. Use --optimize instead." << endl;
serr() << "--" << g_strOptimizeYul << " is invalid in assembly mode. Use --" << g_argOptimize << " instead." << endl;
return false;
}
if (m_args.count(g_strNoOptimizeYul))
{
serr() << "--no-optimize-yul is invalid in assembly mode. Optimization is disabled by default and can be enabled with --optimize." << endl;
serr() << "--" << g_strNoOptimizeYul << " is invalid in assembly mode. Optimization is disabled by default and can be enabled with --" << g_argOptimize << "." << endl;
return false;
}
optional<string> yulOptimiserSteps;
if (m_args.count(g_strYulOptimizations))
{
if (!optimize)
{
serr() << "--" << g_strYulOptimizations << " is invalid if Yul optimizer is disabled" << endl;
return false;
}
try
{
yul::OptimiserSuite::validateSequence(m_args[g_strYulOptimizations].as<string>());
}
catch (yul::OptimizerException const& _exception)
{
serr() << "Invalid optimizer step sequence in --" << g_strYulOptimizations << ": " << _exception.what() << endl;
return false;
}
yulOptimiserSteps = m_args[g_strYulOptimizations].as<string>();
}
if (m_args.count(g_argMachine))
{
string machine = m_args[g_argMachine].as<string>();
@ -1083,7 +1113,7 @@ bool CommandLineInterface::processInput()
targetMachine = Machine::Ewasm;
else
{
serr() << "Invalid option for --machine: " << machine << endl;
serr() << "Invalid option for --" << g_argMachine << ": " << machine << endl;
return false;
}
}
@ -1099,13 +1129,14 @@ bool CommandLineInterface::processInput()
inputLanguage = Input::Ewasm;
if (targetMachine != Machine::Ewasm)
{
serr() << "If you select Ewasm as --yul-dialect, --machine has to be Ewasm as well." << endl;
serr() << "If you select Ewasm as --" << g_strYulDialect << ", ";
serr() << "--" << g_argMachine << " has to be Ewasm as well." << endl;
return false;
}
}
else
{
serr() << "Invalid option for --yul-dialect: " << dialect << endl;
serr() << "Invalid option for --" << g_strYulDialect << ": " << dialect << endl;
return false;
}
}
@ -1122,7 +1153,7 @@ bool CommandLineInterface::processInput()
"Warning: Yul is still experimental. Please use the output with care." <<
endl;
return assemble(inputLanguage, targetMachine, optimize);
return assemble(inputLanguage, targetMachine, optimize, yulOptimiserSteps);
}
if (m_args.count(g_argLink))
{
@ -1142,7 +1173,7 @@ bool CommandLineInterface::processInput()
m_metadataHash = CompilerStack::MetadataHash::None;
else
{
serr() << "Invalid option for --metadata-hash: " << hashStr << endl;
serr() << "Invalid option for --" << g_argMetadataHash << ": " << hashStr << endl;
return false;
}
}
@ -1534,18 +1565,21 @@ string CommandLineInterface::objectWithLinkRefsHex(evmasm::LinkerObject const& _
bool CommandLineInterface::assemble(
yul::AssemblyStack::Language _language,
yul::AssemblyStack::Machine _targetMachine,
bool _optimize
bool _optimize,
optional<string> _yulOptimiserSteps
)
{
solAssert(_optimize || !_yulOptimiserSteps.has_value(), "");
bool successful = true;
map<string, yul::AssemblyStack> assemblyStacks;
for (auto const& src: m_sourceCodes)
{
auto& stack = assemblyStacks[src.first] = yul::AssemblyStack(
m_evmVersion,
_language,
_optimize ? OptimiserSettings::full() : OptimiserSettings::minimal()
);
OptimiserSettings settings = _optimize ? OptimiserSettings::full() : OptimiserSettings::minimal();
if (_yulOptimiserSteps.has_value())
settings.yulOptimiserSteps = _yulOptimiserSteps.value();
auto& stack = assemblyStacks[src.first] = yul::AssemblyStack(m_evmVersion, _language, settings);
try
{
if (!stack.parseAndAnalyze(src.first, src.second))

View File

@ -56,7 +56,12 @@ private:
/// @returns the full object with library placeholder hints in hex.
static std::string objectWithLinkRefsHex(evmasm::LinkerObject const& _obj);
bool assemble(yul::AssemblyStack::Language _language, yul::AssemblyStack::Machine _targetMachine, bool _optimize);
bool assemble(
yul::AssemblyStack::Language _language,
yul::AssemblyStack::Machine _targetMachine,
bool _optimize,
std::optional<std::string> _yulOptimiserSteps = std::nullopt
);
void outputCompilationResults();

View File

@ -187,7 +187,7 @@ add_executable(soltest ${sources}
${libsolidity_util_sources}
${yul_phaser_sources}
)
target_link_libraries(soltest PRIVATE libsolc yul solidity yulInterpreter evmasm solutil Boost::boost Boost::program_options Boost::unit_test_framework evmc)
target_link_libraries(soltest PRIVATE libsolc yul solidity yulInterpreter evmasm solutil Boost::boost Boost::filesystem Boost::program_options Boost::unit_test_framework evmc)
# Special compilation flag for Visual Studio (version 2019 at least affected)

View File

@ -94,7 +94,7 @@ CommonOptions::CommonOptions(std::string _caption):
("evmonepath", po::value<fs::path>(&evmonePath)->default_value(EVMOneEnvOrDefaultPath()), "path to evmone library")
("no-smt", po::bool_switch(&disableSMT), "disable SMT checker")
("optimize", po::bool_switch(&optimize), "enables optimization")
("optimize-yul", po::bool_switch(&optimizeYul), "enables Yul optimization")
("enforce-via-yul", po::bool_switch(&enforceViaYul), "Enforce compiling all tests via yul to see if additional tests can be activated.")
("abiencoderv2", po::bool_switch(&useABIEncoderV2), "enables abi encoder v2")
("show-messages", po::bool_switch(&showMessages), "enables message output")
("show-metadata", po::bool_switch(&showMetadata), "enables metadata output");

View File

@ -46,7 +46,7 @@ struct CommonOptions: boost::noncopyable
boost::filesystem::path evmonePath;
boost::filesystem::path testPath;
bool optimize = false;
bool optimizeYul = false;
bool enforceViaYul = false;
bool disableSMT = false;
bool useABIEncoderV2 = false;
bool showMessages = false;

View File

@ -49,9 +49,7 @@ ExecutionFramework::ExecutionFramework(langutil::EVMVersion _evmVersion):
m_showMessages(solidity::test::CommonOptions::get().showMessages),
m_evmHost(make_shared<EVMHost>(m_evmVersion))
{
if (solidity::test::CommonOptions::get().optimizeYul)
m_optimiserSettings = solidity::frontend::OptimiserSettings::full();
else if (solidity::test::CommonOptions::get().optimize)
if (solidity::test::CommonOptions::get().optimize)
m_optimiserSettings = solidity::frontend::OptimiserSettings::standard();
reset();

View File

@ -40,6 +40,11 @@ void TestCase::printSettings(ostream& _stream, const string& _linePrefix, const
_stream << _linePrefix << "// " << setting.first << ": " << setting.second << endl;
}
void TestCase::printUpdatedSettings(std::ostream& _stream, std::string const& _linePrefix)
{
printSettings(_stream, _linePrefix);
}
bool TestCase::isTestFilename(boost::filesystem::path const& _filename)
{
string extension = _filename.extension().string();

View File

@ -38,6 +38,7 @@ public:
{
std::string filename;
langutil::EVMVersion evmVersion;
bool enforceCompileViaYul;
};
enum class TestResult { Success, Failure, FatalError };
@ -59,6 +60,8 @@ public:
virtual void printSource(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false) const = 0;
/// Outputs settings.
virtual void printSettings(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false);
/// Outputs updated settings
virtual void printUpdatedSettings(std::ostream& _stream, std::string const& _linePrefix = "");
/// Outputs test expectations to @arg _stream that match the actual results of the test.
/// Each line of output is prefixed with @arg _linePrefix.
virtual void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const = 0;

View File

@ -64,12 +64,13 @@ int registerTests(
boost::unit_test::test_suite& _suite,
boost::filesystem::path const& _basepath,
boost::filesystem::path const& _path,
bool _enforceViaYul,
TestCase::TestCaseCreator _testCaseCreator
)
{
int numTestsAdded = 0;
fs::path fullpath = _basepath / _path;
TestCase::Config config{fullpath.string(), solidity::test::CommonOptions::get().evmVersion()};
TestCase::Config config{fullpath.string(), solidity::test::CommonOptions::get().evmVersion(), _enforceViaYul};
if (fs::is_directory(fullpath))
{
test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string());
@ -78,7 +79,12 @@ int registerTests(
fs::directory_iterator()
))
if (fs::is_directory(entry.path()) || TestCase::isTestFilename(entry.path().filename()))
numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename(), _testCaseCreator);
numTestsAdded += registerTests(
*sub_suite,
_basepath, _path / entry.path().filename(),
_enforceViaYul,
_testCaseCreator
);
_suite.add(sub_suite);
}
else
@ -164,6 +170,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
master,
options.testPath / ts.path,
ts.subpath,
options.enforceViaYul,
ts.testCaseCreator
) > 0, std::string("no ") + ts.title + " tests found");
}

Some files were not shown because too many files have changed in this diff Show More