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 ```sh
cd .circleci/docker/ cd .circleci/docker/
docker build -t ethereum/solidity-buildpack-deps:ubuntu1904-<revision> -f Dockerfile.ubuntu1904 . docker build -t ethereum/solidity-buildpack-deps:ubuntu2004-<revision> -f Dockerfile.ubuntu2004 .
docker push ethereum/solidity-buildpack-deps:ubuntu1904-<revision> 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: 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. 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 ```sh
cd solidity cd solidity
# Mounts your local solidity directory in docker container for testing # 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 cd /src/solidity
<commands_to_test_build_with_new_docker_image> <commands_to_test_build_with_new_docker_image>
``` ```

View File

@ -10,12 +10,12 @@ parameters:
ubuntu-1804-docker-image-rev: ubuntu-1804-docker-image-rev:
type: string type: string
default: "4" default: "4"
ubuntu-1904-docker-image-rev: ubuntu-2004-docker-image-rev:
type: string type: string
default: "4" default: "1"
ubuntu-1904-clang-docker-image-rev: ubuntu-2004-clang-docker-image-rev:
type: string type: string
default: "5" default: "1"
ubuntu-1604-clang-ossfuzz-docker-image-rev: ubuntu-1604-clang-ossfuzz-docker-image-rev:
type: string type: string
default: "2" default: "2"
@ -50,6 +50,7 @@ defaults:
cd build cd build
protoc --proto_path=../test/tools/ossfuzz yulProto.proto --cpp_out=../test/tools/ossfuzz 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 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 cmake .. -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-Release} $CMAKE_OPTIONS
make ossfuzz ossfuzz_proto ossfuzz_abiv2 -j4 make ossfuzz ossfuzz_proto ossfuzz_abiv2 -j4
@ -97,6 +98,7 @@ defaults:
- test/tools/ossfuzz/strictasm_opt_ossfuzz - test/tools/ossfuzz/strictasm_opt_ossfuzz
- test/tools/ossfuzz/yul_proto_diff_ossfuzz - test/tools/ossfuzz/yul_proto_diff_ossfuzz
- test/tools/ossfuzz/yul_proto_ossfuzz - test/tools/ossfuzz/yul_proto_ossfuzz
- test/tools/ossfuzz/sol_proto_ossfuzz
# test result output directory # test result output directory
- artifacts_test_results: &artifacts_test_results - artifacts_test_results: &artifacts_test_results
@ -137,9 +139,9 @@ defaults:
- store_test_results: *store_test_results - store_test_results: *store_test_results
- store_artifacts: *artifacts_test_results - store_artifacts: *artifacts_test_results
- test_ubuntu1904_clang: &test_ubuntu1904_clang - test_ubuntu2004_clang: &test_ubuntu2004_clang
docker: 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: steps:
- checkout - checkout
- attach_workspace: - attach_workspace:
@ -148,9 +150,9 @@ defaults:
- store_test_results: *store_test_results - store_test_results: *store_test_results
- store_artifacts: *artifacts_test_results - store_artifacts: *artifacts_test_results
- test_ubuntu1904: &test_ubuntu1904 - test_ubuntu2004: &test_ubuntu2004
docker: 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: steps:
- checkout - checkout
- attach_workspace: - attach_workspace:
@ -160,7 +162,7 @@ defaults:
- store_artifacts: *artifacts_test_results - store_artifacts: *artifacts_test_results
- test_asan: &test_asan - test_asan: &test_asan
<<: *test_ubuntu1904 <<: *test_ubuntu2004
steps: steps:
- checkout - checkout
- attach_workspace: - attach_workspace:
@ -179,7 +181,7 @@ defaults:
tags: tags:
only: /.*/ only: /.*/
- workflow_ubuntu1904: &workflow_ubuntu1904 - workflow_ubuntu2004: &workflow_ubuntu2004
<<: *workflow_trigger_on_tags <<: *workflow_trigger_on_tags
requires: requires:
- b_ubu - b_ubu
@ -189,17 +191,17 @@ defaults:
requires: requires:
- b_ubu_ossfuzz - b_ubu_ossfuzz
- workflow_ubuntu1904_clang: &workflow_ubuntu1904_clang - workflow_ubuntu2004_clang: &workflow_ubuntu2004_clang
<<: *workflow_trigger_on_tags <<: *workflow_trigger_on_tags
requires: requires:
- b_ubu_clang - b_ubu_clang
- workflow_ubuntu1904_release: &workflow_ubuntu1904_release - workflow_ubuntu2004_release: &workflow_ubuntu2004_release
<<: *workflow_trigger_on_tags <<: *workflow_trigger_on_tags
requires: requires:
- b_ubu_release - b_ubu_release
- workflow_ubuntu1904_codecov: &workflow_ubuntu1904_codecov - workflow_ubuntu2004_codecov: &workflow_ubuntu2004_codecov
<<: *workflow_trigger_on_tags <<: *workflow_trigger_on_tags
requires: requires:
- b_ubu_codecov - b_ubu_codecov
@ -209,7 +211,7 @@ defaults:
requires: requires:
- b_osx - b_osx
- workflow_ubuntu1904_asan: &workflow_ubuntu1904_asan - workflow_ubuntu2004_asan: &workflow_ubuntu2004_asan
<<: *workflow_trigger_on_tags <<: *workflow_trigger_on_tags
requires: requires:
- b_ubu_asan - b_ubu_asan
@ -359,16 +361,16 @@ jobs:
chk_docs_pragma_min_version: chk_docs_pragma_min_version:
docker: 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: environment:
TERM: xterm TERM: xterm
steps: steps:
- checkout - checkout
- run: *run_docs_pragma_min_version - run: *run_docs_pragma_min_version
b_ubu_clang: &build_ubuntu1904_clang b_ubu_clang: &build_ubuntu2004_clang
docker: 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: environment:
CC: clang CC: clang
CXX: clang++ CXX: clang++
@ -378,9 +380,9 @@ jobs:
- store_artifacts: *artifacts_solc - store_artifacts: *artifacts_solc
- persist_to_workspace: *artifacts_executables - persist_to_workspace: *artifacts_executables
b_ubu: &build_ubuntu1904 b_ubu: &build_ubuntu2004
docker: 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: steps:
- checkout - checkout
- run: *run_build - run: *run_build
@ -388,8 +390,8 @@ jobs:
- store_artifacts: *artifacts_tools - store_artifacts: *artifacts_tools
- persist_to_workspace: *artifacts_executables - persist_to_workspace: *artifacts_executables
b_ubu_release: &build_ubuntu1904_release b_ubu_release: &build_ubuntu2004_release
<<: *build_ubuntu1904 <<: *build_ubuntu2004
environment: environment:
FORCE_RELEASE: ON FORCE_RELEASE: ON
@ -406,7 +408,7 @@ jobs:
- persist_to_workspace: *artifacts_executables - persist_to_workspace: *artifacts_executables
b_ubu_codecov: b_ubu_codecov:
<<: *build_ubuntu1904 <<: *build_ubuntu2004
environment: environment:
COVERAGE: ON COVERAGE: ON
CMAKE_BUILD_TYPE: Debug CMAKE_BUILD_TYPE: Debug
@ -416,7 +418,7 @@ jobs:
- persist_to_workspace: *artifacts_build_dir - persist_to_workspace: *artifacts_build_dir
t_ubu_codecov: t_ubu_codecov:
<<: *test_ubuntu1904 <<: *test_ubuntu2004
environment: environment:
EVM: constantinople EVM: constantinople
OPTIMIZE: 1 OPTIMIZE: 1
@ -439,7 +441,7 @@ jobs:
# Builds in C++20 mode and uses debug build in order to speed up. # 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. # Do *NOT* store any artifacts or workspace as we don't run tests on this build.
b_ubu_cxx20: b_ubu_cxx20:
<<: *build_ubuntu1904 <<: *build_ubuntu2004
environment: environment:
CMAKE_BUILD_TYPE: Debug CMAKE_BUILD_TYPE: Debug
CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF 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 # x64 ASAN build, for testing for memory related bugs
b_ubu_asan: &b_ubu_asan b_ubu_asan: &b_ubu_asan
<<: *build_ubuntu1904 <<: *build_ubuntu2004
environment: environment:
CMAKE_OPTIONS: -DSANITIZE=address CMAKE_OPTIONS: -DSANITIZE=address
CMAKE_BUILD_TYPE: Release CMAKE_BUILD_TYPE: Release
@ -603,7 +605,7 @@ jobs:
b_docs: b_docs:
docker: 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: steps:
- checkout - checkout
- run: *setup_prerelease_commit_hash - run: *setup_prerelease_commit_hash
@ -615,10 +617,27 @@ jobs:
destination: docs-html destination: docs-html
t_ubu_soltest: &t_ubu_soltest 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 t_ubu_clang_soltest: &t_ubu_clang_soltest
<<: *test_ubuntu1904_clang <<: *test_ubuntu2004_clang
environment: environment:
EVM: constantinople EVM: constantinople
OPTIMIZE: 0 OPTIMIZE: 0
@ -628,7 +647,7 @@ jobs:
t_ubu_cli: &t_ubu_cli t_ubu_cli: &t_ubu_cli
docker: 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: environment:
TERM: xterm TERM: xterm
steps: steps:
@ -816,20 +835,21 @@ workflows:
# Ubuntu build and tests # Ubuntu build and tests
- b_ubu: *workflow_trigger_on_tags - b_ubu: *workflow_trigger_on_tags
- b_ubu18: *workflow_trigger_on_tags - b_ubu18: *workflow_trigger_on_tags
- t_ubu_cli: *workflow_ubuntu1904 - t_ubu_cli: *workflow_ubuntu2004
- t_ubu_soltest: *workflow_ubuntu1904 - t_ubu_soltest: *workflow_ubuntu2004
- t_ubu_soltest_enforce_yul: *workflow_ubuntu2004
- b_ubu_clang: *workflow_trigger_on_tags - 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 # Ubuntu fake release build and tests
- b_ubu_release: *workflow_trigger_on_tags - b_ubu_release: *workflow_trigger_on_tags
- t_ubu_release_cli: *workflow_ubuntu1904_release - t_ubu_release_cli: *workflow_ubuntu2004_release
- t_ubu_release_soltest: *workflow_ubuntu1904_release - t_ubu_release_soltest: *workflow_ubuntu2004_release
# ASan build and tests # ASan build and tests
- b_ubu_asan: *workflow_trigger_on_tags - b_ubu_asan: *workflow_trigger_on_tags
- t_ubu_asan_constantinople: *workflow_ubuntu1904_asan - t_ubu_asan_constantinople: *workflow_ubuntu2004_asan
- t_ubu_asan_cli: *workflow_ubuntu1904_asan - t_ubu_asan_cli: *workflow_ubuntu2004_asan
# Emscripten build and selected tests # Emscripten build and selected tests
- b_ems: *workflow_trigger_on_tags - b_ems: *workflow_trigger_on_tags
@ -856,4 +876,4 @@ workflows:
# Code Coverage enabled build and tests # Code Coverage enabled build and tests
- b_ubu_codecov: *workflow_trigger_on_tags - 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. # (c) 2016-2019 solidity contributors.
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
FROM buildpack-deps:disco AS base FROM buildpack-deps:focal AS base
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
RUN set -ex; \ 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 update; \
apt-get install -qqy --no-install-recommends \ apt-get install -qqy --no-install-recommends \
build-essential \ build-essential \
software-properties-common \ 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-filesystem-dev libboost-test-dev libboost-system-dev \
libboost-program-options-dev \ libboost-program-options-dev \
libjsoncpp-dev \ libcvc4-dev z3 libz3-dev \
llvm-8-dev libcvc4-dev libz3-static-dev libleveldb1d \
; \ ; \
apt-get install -qy python-pip python-sphinx; \ apt-get install -qy python3-pip python3-sphinx; \
update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer /usr/bin/llvm-symbolizer-8 1; \ pip3 install codecov; \
pip install codecov; \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
FROM base AS libraries 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 # EVMONE
RUN set -ex; \ RUN set -ex; \
cd /usr/src; \ cd /usr/src; \
@ -81,7 +48,6 @@ RUN set -ex; \
cd evmone; \ cd evmone; \
mkdir build; \ mkdir build; \
cd build; \ cd build; \
# isoltest links against the evmone shared library
cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \ cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \
ninja; \ ninja; \
ninja install/strip; \ 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" 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" SOLTEST_ARGS="--evm-version=$EVM $SOLTEST_FLAGS"
test "${OPTIMIZE}" = "1" && SOLTEST_ARGS="${SOLTEST_ARGS} --optimize" 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}" echo "Running ${REPODIR}/build/test/soltest ${BOOST_TEST_ARGS} -- ${SOLTEST_ARGS}"

View File

@ -16,17 +16,25 @@ Bugfixes:
### 0.6.8 (unreleased) ### 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: Compiler Features:
* Commandline Interface: Don't ignore `--yul-optimizations` in assembly mode.
Bugfixes: 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) ### 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", "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.", "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": { "0.4.10": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -433,6 +434,7 @@
}, },
"0.4.11": { "0.4.11": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -450,6 +452,7 @@
}, },
"0.4.12": { "0.4.12": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -466,6 +469,7 @@
}, },
"0.4.13": { "0.4.13": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -482,6 +486,7 @@
}, },
"0.4.14": { "0.4.14": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -497,6 +502,7 @@
}, },
"0.4.15": { "0.4.15": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -511,6 +517,7 @@
}, },
"0.4.16": { "0.4.16": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -527,6 +534,7 @@
}, },
"0.4.17": { "0.4.17": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -544,6 +552,7 @@
}, },
"0.4.18": { "0.4.18": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -560,6 +569,7 @@
}, },
"0.4.19": { "0.4.19": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -596,6 +606,7 @@
}, },
"0.4.20": { "0.4.20": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -613,6 +624,7 @@
}, },
"0.4.21": { "0.4.21": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -630,6 +642,7 @@
}, },
"0.4.22": { "0.4.22": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -647,6 +660,7 @@
}, },
"0.4.23": { "0.4.23": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -663,6 +677,7 @@
}, },
"0.4.24": { "0.4.24": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -679,6 +694,7 @@
}, },
"0.4.25": { "0.4.25": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -693,6 +709,7 @@
}, },
"0.4.26": { "0.4.26": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -739,6 +756,7 @@
}, },
"0.4.5": { "0.4.5": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -758,6 +776,7 @@
}, },
"0.4.6": { "0.4.6": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -776,6 +795,7 @@
}, },
"0.4.7": { "0.4.7": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -794,6 +814,7 @@
}, },
"0.4.8": { "0.4.8": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -812,6 +833,7 @@
}, },
"0.4.9": { "0.4.9": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -830,6 +852,7 @@
}, },
"0.5.0": { "0.5.0": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -844,6 +867,7 @@
}, },
"0.5.1": { "0.5.1": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -858,6 +882,7 @@
}, },
"0.5.10": { "0.5.10": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -868,6 +893,7 @@
}, },
"0.5.11": { "0.5.11": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -877,6 +903,7 @@
}, },
"0.5.12": { "0.5.12": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -886,6 +913,7 @@
}, },
"0.5.13": { "0.5.13": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -895,6 +923,7 @@
}, },
"0.5.14": { "0.5.14": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -905,6 +934,7 @@
}, },
"0.5.15": { "0.5.15": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -914,6 +944,7 @@
}, },
"0.5.16": { "0.5.16": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden" "privateCanBeOverridden"
@ -922,6 +953,7 @@
}, },
"0.5.17": { "0.5.17": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow" "MemoryArrayCreationOverflow"
], ],
@ -929,6 +961,7 @@
}, },
"0.5.2": { "0.5.2": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -943,6 +976,7 @@
}, },
"0.5.3": { "0.5.3": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -957,6 +991,7 @@
}, },
"0.5.4": { "0.5.4": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -971,6 +1006,7 @@
}, },
"0.5.5": { "0.5.5": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -987,6 +1023,7 @@
}, },
"0.5.6": { "0.5.6": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -1003,6 +1040,7 @@
}, },
"0.5.7": { "0.5.7": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -1017,6 +1055,7 @@
}, },
"0.5.8": { "0.5.8": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -1030,6 +1069,7 @@
}, },
"0.5.9": { "0.5.9": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
@ -1042,6 +1082,7 @@
}, },
"0.6.0": { "0.6.0": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"YulOptimizerRedundantAssignmentBreakContinue" "YulOptimizerRedundantAssignmentBreakContinue"
@ -1050,6 +1091,7 @@
}, },
"0.6.1": { "0.6.1": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow" "MemoryArrayCreationOverflow"
], ],
@ -1057,6 +1099,7 @@
}, },
"0.6.2": { "0.6.2": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow" "MemoryArrayCreationOverflow"
], ],
@ -1064,6 +1107,7 @@
}, },
"0.6.3": { "0.6.3": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow" "MemoryArrayCreationOverflow"
], ],
@ -1071,6 +1115,7 @@
}, },
"0.6.4": { "0.6.4": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents", "TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow" "MemoryArrayCreationOverflow"
], ],
@ -1078,16 +1123,21 @@
}, },
"0.6.5": { "0.6.5": {
"bugs": [ "bugs": [
"ImplicitConstructorCallvalueCheck",
"TupleAssignmentMultiStackSlotComponents" "TupleAssignmentMultiStackSlotComponents"
], ],
"released": "2020-04-06" "released": "2020-04-06"
}, },
"0.6.6": { "0.6.6": {
"bugs": [], "bugs": [
"ImplicitConstructorCallvalueCheck"
],
"released": "2020-04-09" "released": "2020-04-09"
}, },
"0.6.7": { "0.6.7": {
"bugs": [], "bugs": [
"ImplicitConstructorCallvalueCheck"
],
"released": "2020-05-04" "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).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(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(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:: .. note::
Do not rely on ``block.timestamp`` or ``blockhash`` as a source of randomness, 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) * Shift operators: ``<<`` (left shift), ``>>`` (right shift)
* Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo), ``**`` (exponentiation) * 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:: .. warning::
Integers in Solidity are restricted to a certain range. For example, with ``uint32``, this is ``0`` up to ``2**32 - 1``. 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 Type Information
---------------- ----------------
The expression ``type(X)`` can be used to retrieve information about the The expression ``type(X)`` can be used to retrieve information about the type
type ``X``. Currently, there is limited support for this feature, but ``X``. Currently, there is limited support for this feature (``X`` can be either
it might be expanded in the future. The following properties are a contract or an integer type) but it might be expanded in the future.
available for a contract type ``C``:
The following properties are available for a contract type ``C``:
``type(C).name`` ``type(C).name``
The name of the contract. 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>`_ 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 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. 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. 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 You can override this sequence and supply your own using the ``--yul-optimizations`` option:
in Solidity mode:
.. code-block:: sh .. code-block:: sh
solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul' 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. 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. 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 Abbreviation Full name
============ =============================== ============ ===============================
f `BlockFlattener` ``f`` ``BlockFlattener``
l `CircularReferencesPruner` ``l`` ``CircularReferencesPruner``
c `CommonSubexpressionEliminator` ``c`` ``CommonSubexpressionEliminator``
C `ConditionalSimplifier` ``C`` ``ConditionalSimplifier``
U `ConditionalUnsimplifier` ``U`` ``ConditionalUnsimplifier``
n `ControlFlowSimplifier` ``n`` ``ControlFlowSimplifier``
D `DeadCodeEliminator` ``D`` ``DeadCodeEliminator``
v `EquivalentFunctionCombiner` ``v`` ``EquivalentFunctionCombiner``
e `ExpressionInliner` ``e`` ``ExpressionInliner``
j `ExpressionJoiner` ``j`` ``ExpressionJoiner``
s `ExpressionSimplifier` ``s`` ``ExpressionSimplifier``
x `ExpressionSplitter` ``x`` ``ExpressionSplitter``
I `ForLoopConditionIntoBody` ``I`` ``ForLoopConditionIntoBody``
O `ForLoopConditionOutOfBody` ``O`` ``ForLoopConditionOutOfBody``
o `ForLoopInitRewriter` ``o`` ``ForLoopInitRewriter``
i `FullInliner` ``i`` ``FullInliner``
g `FunctionGrouper` ``g`` ``FunctionGrouper``
h `FunctionHoister` ``h`` ``FunctionHoister``
T `LiteralRematerialiser` ``T`` ``LiteralRematerialiser``
L `LoadResolver` ``L`` ``LoadResolver``
M `LoopInvariantCodeMotion` ``M`` ``LoopInvariantCodeMotion``
r `RedundantAssignEliminator` ``r`` ``RedundantAssignEliminator``
m `Rematerialiser` ``m`` ``Rematerialiser``
V `SSAReverser` ``V`` ``SSAReverser``
a `SSATransform` ``a`` ``SSATransform``
t `StructuralSimplifier` ``t`` ``StructuralSimplifier``
u `UnusedPruner` ``u`` ``UnusedPruner``
d `VarDeclInitializer` ``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. 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/CommonSubexpressionEliminator.h>
#include <libevmasm/SimplificationRules.h> #include <libevmasm/SimplificationRules.h>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/noncopyable.hpp>
#include <functional> #include <functional>
#include <tuple> #include <tuple>
#include <utility> #include <utility>

View File

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

View File

@ -28,6 +28,8 @@ using namespace std;
using namespace solidity; using namespace solidity;
using namespace solidity::langutil; using namespace solidity::langutil;
ErrorId solidity::langutil::operator"" _error(unsigned long long _error) { return ErrorId{ _error }; }
ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter) ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter)
{ {
if (&_errorReporter == this) if (&_errorReporter == this)
@ -36,30 +38,31 @@ ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter)
return *this; return *this;
} }
void ErrorReporter::warning(ErrorId _error, string const& _description)
void ErrorReporter::warning(string const& _description)
{ {
error(Error::Type::Warning, SourceLocation(), _description); error(_error, Error::Type::Warning, SourceLocation(), _description);
} }
void ErrorReporter::warning( void ErrorReporter::warning(
ErrorId _error,
SourceLocation const& _location, SourceLocation const& _location,
string const& _description string const& _description
) )
{ {
error(Error::Type::Warning, _location, _description); error(_error, Error::Type::Warning, _location, _description);
} }
void ErrorReporter::warning( void ErrorReporter::warning(
ErrorId _error,
SourceLocation const& _location, SourceLocation const& _location,
string const& _description, string const& _description,
SecondarySourceLocation const& _secondaryLocation 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)) if (checkForExcessiveErrors(_type))
return; return;
@ -72,7 +75,7 @@ void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, st
m_errorList.push_back(err); 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)) if (checkForExcessiveErrors(_type))
return; return;
@ -123,15 +126,15 @@ bool ErrorReporter::checkForExcessiveErrors(Error::Type _type)
return false; 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()); 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()); BOOST_THROW_EXCEPTION(FatalError());
} }
@ -145,9 +148,10 @@ void ErrorReporter::clear()
m_errorList.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,
Error::Type::DeclarationError, Error::Type::DeclarationError,
_location, _location,
_secondaryLocation, _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,
Error::Type::DeclarationError, Error::Type::DeclarationError,
_location, _location,
_description _description
); );
} }
void ErrorReporter::fatalDeclarationError(SourceLocation const& _location, std::string const& _description) void ErrorReporter::fatalDeclarationError(ErrorId _error, SourceLocation const& _location, std::string const& _description)
{ {
fatalError( fatalError(
_error,
Error::Type::DeclarationError, Error::Type::DeclarationError,
_location, _location,
_description); _description);
} }
void ErrorReporter::parserError(SourceLocation const& _location, string const& _description) void ErrorReporter::parserError(ErrorId _error, SourceLocation const& _location, string const& _description)
{ {
error( error(
_error,
Error::Type::ParserError, Error::Type::ParserError,
_location, _location,
_description _description
); );
} }
void ErrorReporter::fatalParserError(SourceLocation const& _location, string const& _description) void ErrorReporter::fatalParserError(ErrorId _error, SourceLocation const& _location, string const& _description)
{ {
fatalError( fatalError(
_error,
Error::Type::ParserError, Error::Type::ParserError,
_location, _location,
_description _description
); );
} }
void ErrorReporter::syntaxError(SourceLocation const& _location, string const& _description) void ErrorReporter::syntaxError(ErrorId _error, SourceLocation const& _location, string const& _description)
{ {
error( error(
_error,
Error::Type::SyntaxError, Error::Type::SyntaxError,
_location, _location,
_description _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,
Error::Type::TypeError, Error::Type::TypeError,
_location, _location,
_secondaryLocation, _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,
Error::Type::TypeError, Error::Type::TypeError,
_location, _location,
_description _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( fatalError(
_error,
Error::Type::TypeError, Error::Type::TypeError,
_location, _location,
_secondaryLocation, _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, _location,
_description _description
); );
} }
void ErrorReporter::docstringParsingError(string const& _description) void ErrorReporter::docstringParsingError(ErrorId _error, string const& _description)
{ {
error( error(
_error,
Error::Type::DocstringParsingError, Error::Type::DocstringParsingError,
SourceLocation(), SourceLocation(),
_description _description
); );
} }
void ErrorReporter::docstringParsingError(SourceLocation const& _location, string const& _description) void ErrorReporter::docstringParsingError(ErrorId _error, SourceLocation const& _location, string const& _description)
{ {
error( error(
_error,
Error::Type::DocstringParsingError, Error::Type::DocstringParsingError,
_location, _location,
_description _description

View File

@ -33,6 +33,17 @@
namespace solidity::langutil 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 class ErrorReporter
{ {
public: public:
@ -50,64 +61,68 @@ public:
m_errorList += _errorList; 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( void warning(
ErrorId _error,
SourceLocation const& _location, SourceLocation const& _location,
std::string const& _description, std::string const& _description,
SecondarySourceLocation const& _secondaryLocation SecondarySourceLocation const& _secondaryLocation
); );
void error( void error(
ErrorId _error,
Error::Type _type, Error::Type _type,
SourceLocation const& _location, SourceLocation const& _location,
std::string const& _description std::string const& _description
); );
void declarationError( void declarationError(
ErrorId _error,
SourceLocation const& _location, SourceLocation const& _location,
SecondarySourceLocation const& _secondaryLocation, SecondarySourceLocation const& _secondaryLocation,
std::string const& _description 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( void typeError(
ErrorId _error,
SourceLocation const& _location, SourceLocation const& _location,
SecondarySourceLocation const& _secondaryLocation = SecondarySourceLocation(), SecondarySourceLocation const& _secondaryLocation = SecondarySourceLocation(),
std::string const& _description = std::string() 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> 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!"); solAssert(descs.size() > 0, "Need error descriptions!");
auto filterEmpty = boost::adaptors::filtered([](std::string const& _s) { return !_s.empty(); }); auto filterEmpty = boost::adaptors::filtered([](std::string const& _s) { return !_s.empty(); });
std::string errorStr = util::joinHumanReadable(descs | filterEmpty, " "); 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(ErrorId _error, 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, SecondarySourceLocation const& _secondLocation, std::string const& _description);
void docstringParsingError(std::string const& _description); void docstringParsingError(ErrorId _error, std::string const& _description);
void docstringParsingError(SourceLocation const& _location, std::string const& _description); void docstringParsingError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
ErrorList const& errors() const; ErrorList const& errors() const;
@ -124,18 +139,21 @@ public:
private: private:
void error( void error(
ErrorId _error,
Error::Type _type, Error::Type _type,
SourceLocation const& _location, SourceLocation const& _location,
SecondarySourceLocation const& _secondaryLocation, SecondarySourceLocation const& _secondaryLocation,
std::string const& _description = std::string()); std::string const& _description = std::string());
void fatalError( void fatalError(
ErrorId _error,
Error::Type _type, Error::Type _type,
SourceLocation const& _location, SourceLocation const& _location,
SecondarySourceLocation const& _secondaryLocation, SecondarySourceLocation const& _secondaryLocation,
std::string const& _description = std::string()); std::string const& _description = std::string());
void fatalError( void fatalError(
ErrorId _error,
Error::Type _type, Error::Type _type,
SourceLocation const& _location = SourceLocation(), SourceLocation const& _location = SourceLocation(),
std::string const& _description = std::string()); 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); string const expectedToken = ParserBase::tokenName(_value);
if (m_parserErrorRecovery) if (m_parserErrorRecovery)
parserError("Expected " + expectedToken + " but got " + tokenName(tok)); parserError(6635_error, "Expected " + expectedToken + " but got " + tokenName(tok));
else 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. // Do not advance so that recovery can sync or make use of the current token.
// This is especially useful if the expected token // This is especially useful if the expected token
// is the only one that is missing and is at the end of a construct. // 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. // rollback to where the token started, and raise exception to be caught at a higher level.
m_scanner->setPosition(startPosition); m_scanner->setPosition(startPosition);
m_inParserRecovery = true; m_inParserRecovery = true;
fatalParserError(errorLoc, msg); fatalParserError(1957_error, errorLoc, msg);
} }
else else
{ {
if (m_inParserRecovery) if (m_inParserRecovery)
parserWarning("Recovered in " + _currentNodeName + " at " + expectedToken + "."); parserWarning(3796_error, "Recovered in " + _currentNodeName + " at " + expectedToken + ".");
else else
parserError(errorLoc, msg + "Recovered at next " + expectedToken); parserError(1054_error, errorLoc, msg + "Recovered at next " + expectedToken);
m_inParserRecovery = false; m_inParserRecovery = false;
} }
} }
else if (m_inParserRecovery) else if (m_inParserRecovery)
{ {
string expectedToken = ParserBase::tokenName(_value); string expectedToken = ParserBase::tokenName(_value);
parserWarning("Recovered in " + _currentNodeName + " at " + expectedToken + "."); parserWarning(3347_error, "Recovered in " + _currentNodeName + " at " + expectedToken + ".");
m_inParserRecovery = false; m_inParserRecovery = false;
} }
@ -134,7 +134,7 @@ void ParserBase::increaseRecursionDepth()
{ {
m_recursionDepth++; m_recursionDepth++;
if (m_recursionDepth >= 1200) if (m_recursionDepth >= 1200)
fatalParserError("Maximum recursion depth reached during parsing."); fatalParserError(7319_error, "Maximum recursion depth reached during parsing.");
} }
void ParserBase::decreaseRecursionDepth() void ParserBase::decreaseRecursionDepth()
@ -143,27 +143,27 @@ void ParserBase::decreaseRecursionDepth()
m_recursionDepth--; 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 ErrorReporter;
class Scanner; class Scanner;
struct ErrorId;
class ParserBase class ParserBase
{ {
@ -88,17 +89,17 @@ protected:
/// Creates a @ref ParserError and annotates it with the current position and the /// Creates a @ref ParserError and annotates it with the current position and the
/// given @a _description. /// given @a _description.
void parserError(std::string const& _description); void parserError(ErrorId _error, std::string const& _description);
void parserError(SourceLocation const& _location, 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 /// Creates a @ref ParserWarning and annotates it with the current position and the
/// given @a _description. /// 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 /// Creates a @ref ParserError and annotates it with the current position and the
/// given @a _description. Throws the FatalError. /// given @a _description. Throws the FatalError.
void fatalParserError(std::string const& _description); void fatalParserError(ErrorId _error, std::string const& _description);
void fatalParserError(SourceLocation const& _location, std::string const& _description); void fatalParserError(ErrorId _error, SourceLocation const& _location, std::string const& _description);
std::shared_ptr<Scanner> m_scanner; std::shared_ptr<Scanner> m_scanner;
/// The reference to the list of errors and warning to add errors/warnings during parsing /// 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() endif()
add_library(solidity ${sources} ${z3_SRCS} ${cvc4_SRCS}) 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}) if (${Z3_FOUND})
target_link_libraries(solidity PUBLIC z3::libz3) target_link_libraries(solidity PUBLIC z3::libz3)

View File

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

View File

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

View File

@ -60,7 +60,7 @@ private:
void checkDuplicateFunctions(ContractDefinition const& _contract); void checkDuplicateFunctions(ContractDefinition const& _contract);
void checkDuplicateEvents(ContractDefinition const& _contract); void checkDuplicateEvents(ContractDefinition const& _contract);
template <class T> 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. /// Checks for unimplemented functions and modifiers.
void checkAbstractDefinitions(ContractDefinition const& _contract); void checkAbstractDefinitions(ContractDefinition const& _contract);
/// Checks that the base constructor arguments are properly provided. /// 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()); ssl.append("The variable was declared here.", variableOccurrence->declaration().location());
m_errorReporter.typeError( m_errorReporter.typeError(
3464_error,
variableOccurrence->occurrence() ? variableOccurrence->occurrence() ?
*variableOccurrence->occurrence() : *variableOccurrence->occurrence() :
variableOccurrence->declaration().location(), 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). // Extend the location, as long as the next location overlaps (unreachable is sorted).
for (; it != unreachable.end() && it->start <= location.end; ++it) for (; it != unreachable.end() && it->start <= location.end; ++it)
location.end = std::max(location.end, it->end); 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/ControlFlowGraph.h>
#include <libsolidity/analysis/ControlFlowBuilder.h> #include <libsolidity/analysis/ControlFlowBuilder.h>
#include <boost/range/adaptor/reversed.hpp>
#include <algorithm> #include <algorithm>
using namespace std; using namespace std;

View File

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

View File

@ -173,5 +173,5 @@ void DocStringAnalyser::parseDocStrings(
void DocStringAnalyser::appendError(SourceLocation const& _location, string const& _description) void DocStringAnalyser::appendError(SourceLocation const& _location, string const& _description)
{ {
m_errorOccured = true; 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("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("suicide", TypeProvider::function(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
magicVarDecl("tx", TypeProvider::magic(MagicType::Kind::Transaction)), 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( magicVarDecl("type", TypeProvider::function(
strings{"address"} /* accepts any contract type, handled by the type checker */, strings{},
strings{} /* returns a MagicType, handled by the type checker */, strings{},
FunctionType::Kind::MetaType, FunctionType::Kind::MetaType,
false, true,
StateMutability::Pure StateMutability::Pure
)), )),
}; };
@ -123,7 +125,7 @@ vector<Declaration const*> GlobalContext::declarations() const
{ {
vector<Declaration const*> declarations; vector<Declaration const*> declarations;
declarations.reserve(m_magicVariables.size()); 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()); declarations.push_back(variable.get());
return declarations; return declarations;
} }

View File

@ -22,6 +22,7 @@
#include <boost/range/adaptor/reversed.hpp> #include <boost/range/adaptor/reversed.hpp>
using namespace solidity::frontend; using namespace solidity::frontend;
using namespace solidity::langutil;
void ImmutableValidator::analyze() void ImmutableValidator::analyze()
{ {
@ -42,7 +43,7 @@ void ImmutableValidator::analyze()
visitCallableIfNew(*contract->constructor()); visitCallableIfNew(*contract->constructor());
for (ContractDefinition const* contract: linearizedContracts) 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()) if (auto args = inheritSpec->arguments())
ASTNode::listAccept(*args, *this); ASTNode::listAccept(*args, *this);
@ -165,33 +166,39 @@ void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _va
{ {
if (!m_currentConstructor) if (!m_currentConstructor)
m_errorReporter.typeError( m_errorReporter.typeError(
1581_error,
_expression.location(), _expression.location(),
"Immutable variables can only be initialized inline or assigned directly in the constructor." "Immutable variables can only be initialized inline or assigned directly in the constructor."
); );
else if (m_currentConstructor->annotation().contract->id() != _variableReference.annotation().contract->id()) else if (m_currentConstructor->annotation().contract->id() != _variableReference.annotation().contract->id())
m_errorReporter.typeError( m_errorReporter.typeError(
7484_error,
_expression.location(), _expression.location(),
"Immutable variables must be initialized in the constructor of the contract they are defined in." "Immutable variables must be initialized in the constructor of the contract they are defined in."
); );
else if (m_inLoop) else if (m_inLoop)
m_errorReporter.typeError( m_errorReporter.typeError(
6672_error,
_expression.location(), _expression.location(),
"Immutable variables can only be initialized once, not in a while statement." "Immutable variables can only be initialized once, not in a while statement."
); );
else if (m_inBranch) else if (m_inBranch)
m_errorReporter.typeError( m_errorReporter.typeError(
4599_error,
_expression.location(), _expression.location(),
"Immutable variables must be initialized unconditionally, not in an if statement." "Immutable variables must be initialized unconditionally, not in an if statement."
); );
if (!m_initializedStateVariables.emplace(&_variableReference).second) if (!m_initializedStateVariables.emplace(&_variableReference).second)
m_errorReporter.typeError( m_errorReporter.typeError(
1574_error,
_expression.location(), _expression.location(),
"Immutable state variable already initialized." "Immutable state variable already initialized."
); );
} }
else if (m_inConstructionContext) else if (m_inConstructionContext)
m_errorReporter.typeError( m_errorReporter.typeError(
7733_error,
_expression.location(), _expression.location(),
"Immutable variables cannot be read during contract creation time, which means " "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." "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 (varDecl->immutable())
if (!util::contains(m_initializedStateVariables, varDecl)) if (!util::contains(m_initializedStateVariables, varDecl))
m_errorReporter.typeError( m_errorReporter.typeError(
2658_error,
_location, _location,
solidity::langutil::SecondarySourceLocation().append("Not initialized: ", varDecl->location()), solidity::langutil::SecondarySourceLocation().append("Not initialized: ", varDecl->location()),
"Construction control flow ends without initializing all immutable state variables." "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)) if (!_sourceUnits.count(path))
{ {
m_errorReporter.declarationError( m_errorReporter.declarationError(
5073_error,
imp->location(), imp->location(),
"Import \"" + path + "\" (referenced as \"" + imp->path() + "\") not found." "Import \"" + path + "\" (referenced as \"" + imp->path() + "\") not found."
); );
@ -95,6 +96,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
if (declarations.empty()) if (declarations.empty())
{ {
m_errorReporter.declarationError( m_errorReporter.declarationError(
2904_error,
imp->location(), imp->location(),
"Declaration \"" + "Declaration \"" +
alias.symbol->name() + alias.symbol->name() +
@ -208,6 +210,7 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions()
// Don't warn the user for what the user did not. // Don't warn the user for what the user did not.
continue; continue;
m_errorReporter.warning( m_errorReporter.warning(
8261_error,
declaration->location(), declaration->location(),
"Variable is shadowed in inline assembly by an instruction of the same name" "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( m_errorReporter.declarationError(
9097_error,
secondDeclarationLocation, secondDeclarationLocation,
SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation), SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
"Identifier already declared." "Identifier already declared."
@ -343,19 +347,19 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract)
UserDefinedTypeName const& baseName = baseSpecifier->name(); UserDefinedTypeName const& baseName = baseSpecifier->name();
auto base = dynamic_cast<ContractDefinition const*>(baseName.annotation().referencedDeclaration); auto base = dynamic_cast<ContractDefinition const*>(baseName.annotation().referencedDeclaration);
if (!base) 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 // "push_front" has the effect that bases mentioned later can overwrite members of bases
// mentioned earlier // mentioned earlier
input.back().push_front(base); input.back().push_front(base);
vector<ContractDefinition const*> const& basesBases = base->annotation().linearizedBaseContracts; vector<ContractDefinition const*> const& basesBases = base->annotation().linearizedBaseContracts;
if (basesBases.empty()) 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.push_front(list<ContractDefinition const*>(basesBases.begin(), basesBases.end()));
} }
input.back().push_front(&_contract); input.back().push_front(&_contract);
vector<ContractDefinition const*> result = cThreeMerge(input); vector<ContractDefinition const*> result = cThreeMerge(input);
if (result.empty()) 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().linearizedBaseContracts = result;
_contract.annotation().contractDependencies.insert(result.begin() + 1, result.end()); _contract.annotation().contractDependencies.insert(result.begin() + 1, result.end());
} }
@ -476,6 +480,7 @@ bool DeclarationRegistrationHelper::registerDeclaration(
} }
_errorReporter.declarationError( _errorReporter.declarationError(
2333_error,
secondDeclarationLocation, secondDeclarationLocation,
SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation), SecondarySourceLocation().append("The previous declaration is here:", firstDeclarationLocation),
"Identifier already declared." "Identifier already declared."
@ -486,6 +491,7 @@ bool DeclarationRegistrationHelper::registerDeclaration(
{ {
if (dynamic_cast<MagicVariableDeclaration const*>(shadowedDeclaration)) if (dynamic_cast<MagicVariableDeclaration const*>(shadowedDeclaration))
_errorReporter.warning( _errorReporter.warning(
2319_error,
*_errorLocation, *_errorLocation,
"This declaration shadows a builtin symbol." "This declaration shadows a builtin symbol."
); );
@ -493,6 +499,7 @@ bool DeclarationRegistrationHelper::registerDeclaration(
{ {
auto shadowedLocation = shadowedDeclaration->location(); auto shadowedLocation = shadowedDeclaration->location();
_errorReporter.warning( _errorReporter.warning(
2519_error,
_declaration.location(), _declaration.location(),
"This declaration shadows an existing declaration.", "This declaration shadows an existing declaration.",
SecondarySourceLocation().append("The shadowed declaration is here:", shadowedLocation) SecondarySourceLocation().append("The shadowed declaration is here:", shadowedLocation)

View File

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

View File

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

View File

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

View File

@ -268,19 +268,19 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
void ReferencesResolver::declarationError(SourceLocation const& _location, string const& _description) void ReferencesResolver::declarationError(SourceLocation const& _location, string const& _description)
{ {
m_errorOccurred = true; 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) void ReferencesResolver::declarationError(SourceLocation const& _location, SecondarySourceLocation const& _ssl, string const& _description)
{ {
m_errorOccurred = true; 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) void ReferencesResolver::fatalDeclarationError(SourceLocation const& _location, string const& _description)
{ {
m_errorOccurred = true; 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()) if (var.first.second->isCallableOrCatchParameter())
m_errorReporter.warning( m_errorReporter.warning(
5667_error,
var.first.second->location(), var.first.second->location(),
"Unused " + "Unused " +
string(var.first.second->isTryCatchParameter() ? "try/catch" : "function") + string(var.first.second->isTryCatchParameter() ? "try/catch" : "function") +
" parameter. Remove or comment out the variable name to silence this warning." " parameter. Remove or comment out the variable name to silence this warning."
); );
else 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_localVarUseCount.clear();
m_constructor = false; m_constructor = false;
@ -159,6 +160,7 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable)
set<StructDefinition const*> structsSeen; set<StructDefinition const*> structsSeen;
if (structureSizeEstimate(*_variable.type(), structsSeen) >= bigint(1) << 64) if (structureSizeEstimate(*_variable.type(), structsSeen) >= bigint(1) << 64)
m_errorReporter.warning( m_errorReporter.warning(
3408_error,
_variable.location(), _variable.location(),
"Variable covers a large part of storage and thus makes collisions likely. " "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 " "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) if (_statement.expression().annotation().isPure)
m_errorReporter.warning( m_errorReporter.warning(
6133_error,
_statement.location(), _statement.location(),
"Statement has no effect." "Statement has no effect."
); );
@ -196,11 +199,13 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
{ {
if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "gas") if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "gas")
m_errorReporter.typeError( m_errorReporter.typeError(
1400_error,
_memberAccess.location(), _memberAccess.location(),
"\"msg.gas\" has been deprecated in favor of \"gasleft()\"" "\"msg.gas\" has been deprecated in favor of \"gasleft()\""
); );
else if (type->kind() == MagicType::Kind::Block && _memberAccess.memberName() == "blockhash") else if (type->kind() == MagicType::Kind::Block && _memberAccess.memberName() == "blockhash")
m_errorReporter.typeError( m_errorReporter.typeError(
8113_error,
_memberAccess.location(), _memberAccess.location(),
"\"block.blockhash()\" has been deprecated in favor of \"blockhash()\"" "\"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()); ContractType const& contract = dynamic_cast<ContractType const&>(*type->typeArgument());
if (m_constructorUsesAssembly->check(contract.contractDefinition())) if (m_constructorUsesAssembly->check(contract.contractDefinition()))
m_errorReporter.warning( m_errorReporter.warning(
6417_error,
_memberAccess.location(), _memberAccess.location(),
"The constructor of the contract (or its base) uses inline assembly. " "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." "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 (auto const* type = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type))
if (type->kind() == FunctionType::Kind::BareCallCode) if (type->kind() == FunctionType::Kind::BareCallCode)
m_errorReporter.typeError( m_errorReporter.typeError(
2256_error,
_memberAccess.location(), _memberAccess.location(),
"\"callcode\" has been deprecated in favour of \"delegatecall\"." "\"callcode\" has been deprecated in favour of \"delegatecall\"."
); );
@ -235,6 +242,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
{ {
if (id->name() == "this") if (id->name() == "this")
m_errorReporter.warning( m_errorReporter.warning(
5805_error,
id->location(), id->location(),
"\"this\" used in constructor. " "\"this\" used in constructor. "
"Note that external functions of a contract " "Note that external functions of a contract "
@ -285,6 +293,7 @@ bool StaticAnalyzer::visit(BinaryOperation const& _operation)
)) ))
if (rhs->isZero()) if (rhs->isZero())
m_errorReporter.typeError( m_errorReporter.typeError(
1211_error,
_operation.location(), _operation.location(),
(_operation.getOperator() == Token::Div) ? "Division by zero." : "Modulo zero." (_operation.getOperator() == Token::Div) ? "Division by zero." : "Modulo zero."
); );
@ -307,6 +316,7 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall)
)) ))
if (lastArg->isZero()) if (lastArg->isZero())
m_errorReporter.typeError( m_errorReporter.typeError(
4195_error,
_functionCall.location(), _functionCall.location(),
"Arithmetic modulo zero." "Arithmetic modulo zero."
); );
@ -317,6 +327,7 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall)
functionType->declaration().scope() == m_currentContract functionType->declaration().scope() == m_currentContract
) )
m_errorReporter.typeError( m_errorReporter.typeError(
6700_error,
_functionCall.location(), _functionCall.location(),
SecondarySourceLocation().append( SecondarySourceLocation().append(
"The function declaration is here:", "The function declaration is here:",

View File

@ -27,7 +27,6 @@
#include <liblangutil/ErrorReporter.h> #include <liblangutil/ErrorReporter.h>
#include <liblangutil/SemVerHandler.h> #include <liblangutil/SemVerHandler.h>
#include <boost/algorithm/cxx11/all_of.hpp>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <memory> #include <memory>
@ -69,7 +68,7 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit)
string(";\""); string(";\"");
// when reporting the warning, print the source name only // 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; m_sourceUnit = nullptr;
} }
@ -79,18 +78,20 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
solAssert(!_pragma.tokens().empty(), ""); solAssert(!_pragma.tokens().empty(), "");
solAssert(_pragma.tokens().size() == _pragma.literals().size(), ""); solAssert(_pragma.tokens().size() == _pragma.literals().size(), "");
if (_pragma.tokens()[0] != Token::Identifier) 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") else if (_pragma.literals()[0] == "experimental")
{ {
solAssert(m_sourceUnit, ""); solAssert(m_sourceUnit, "");
vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end()); vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end());
if (literals.empty()) if (literals.empty())
m_errorReporter.syntaxError( m_errorReporter.syntaxError(
9679_error,
_pragma.location(), _pragma.location(),
"Experimental feature name is missing." "Experimental feature name is missing."
); );
else if (literals.size() > 1) else if (literals.size() > 1)
m_errorReporter.syntaxError( m_errorReporter.syntaxError(
6022_error,
_pragma.location(), _pragma.location(),
"Stray arguments." "Stray arguments."
); );
@ -98,17 +99,17 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
{ {
string const literal = literals[0]; string const literal = literals[0];
if (literal.empty()) 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)) 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))) 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 else
{ {
auto feature = ExperimentalFeatureNames.at(literal); auto feature = ExperimentalFeatureNames.at(literal);
m_sourceUnit->annotation().experimentalFeatures.insert(feature); m_sourceUnit->annotation().experimentalFeatures.insert(feature);
if (!ExperimentalFeatureWithoutWarning.count(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)}; static SemVerVersion const currentVersion{string(VersionString)};
if (!matchExpression.matches(currentVersion)) if (!matchExpression.matches(currentVersion))
m_errorReporter.syntaxError( m_errorReporter.syntaxError(
3997_error,
_pragma.location(), _pragma.location(),
"Source file requires different compiler version (current compiler is " + "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" "strictly less than the released version"
); );
m_versionPragmaFound = true; m_versionPragmaFound = true;
} }
else 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; return true;
} }
@ -142,7 +144,7 @@ bool SyntaxChecker::visit(ModifierDefinition const&)
void SyntaxChecker::endVisit(ModifierDefinition const& _modifier) void SyntaxChecker::endVisit(ModifierDefinition const& _modifier)
{ {
if (_modifier.isImplemented() && !m_placeholderFound) 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; m_placeholderFound = false;
} }
@ -150,7 +152,7 @@ void SyntaxChecker::checkSingleStatementVariableDeclaration(ASTNode const& _stat
{ {
auto varDecl = dynamic_cast<VariableDeclarationStatement const*>(&_statement); auto varDecl = dynamic_cast<VariableDeclarationStatement const*>(&_statement);
if (varDecl) 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) bool SyntaxChecker::visit(IfStatement const& _ifStatement)
@ -189,7 +191,7 @@ bool SyntaxChecker::visit(Continue const& _continueStatement)
{ {
if (m_inLoopDepth <= 0) if (m_inLoopDepth <= 0)
// we're not in a for/while loop, report syntax error // 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; return true;
} }
@ -197,13 +199,14 @@ bool SyntaxChecker::visit(Break const& _breakStatement)
{ {
if (m_inLoopDepth <= 0) if (m_inLoopDepth <= 0)
// we're not in a for/while loop, report syntax error // 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; return true;
} }
bool SyntaxChecker::visit(Throw const& _throwStatement) bool SyntaxChecker::visit(Throw const& _throwStatement)
{ {
m_errorReporter.syntaxError( m_errorReporter.syntaxError(
4538_error,
_throwStatement.location(), _throwStatement.location(),
"\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"." "\"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: // Generic checks no matter what base this number literal is of:
if (value.back() == '_') 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; return true;
} }
if (value.find("__") != ASTString::npos) 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; return true;
} }
if (!_literal.isHexNumber()) // decimal literal if (!_literal.isHexNumber()) // decimal literal
{ {
if (value.find("._") != ASTString::npos) 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) 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) 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) 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; return true;
@ -253,7 +256,7 @@ bool SyntaxChecker::visit(Literal const& _literal)
bool SyntaxChecker::visit(UnaryOperation const& _operation) bool SyntaxChecker::visit(UnaryOperation const& _operation)
{ {
if (_operation.getOperator() == Token::Add) 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; return true;
} }
@ -265,6 +268,7 @@ bool SyntaxChecker::visit(InlineAssembly const& _inlineAssembly)
if (yul::MSizeFinder::containsMSize(_inlineAssembly.dialect(), _inlineAssembly.operations())) if (yul::MSizeFinder::containsMSize(_inlineAssembly.dialect(), _inlineAssembly.operations()))
m_errorReporter.syntaxError( m_errorReporter.syntaxError(
6553_error,
_inlineAssembly.location(), _inlineAssembly.location(),
"The msize instruction cannot be used when the Yul optimizer is activated because " "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." "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(); ASTString const& contractName = _contract.name();
for (FunctionDefinition const* function: _contract.definedFunctions()) for (FunctionDefinition const* function: _contract.definedFunctions())
if (function->name() == contractName) 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. " "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." "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"; string suggestedVisibility = _function.isFallback() || _function.isReceive() || m_isInterface ? "external" : "public";
m_errorReporter.syntaxError( m_errorReporter.syntaxError(
4937_error,
_function.location(), _function.location(),
"No visibility specified. Did you intend to add \"" + suggestedVisibility + "\"?" "No visibility specified. Did you intend to add \"" + suggestedVisibility + "\"?"
); );
} }
if (m_isInterface && !_function.modifiers().empty()) 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()) 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; return true;
} }
@ -315,11 +322,11 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node)
{ {
for (auto const& decl: _node.parameterTypeList()->parameters()) for (auto const& decl: _node.parameterTypeList()->parameters())
if (!decl->name().empty()) 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()) for (auto const& decl: _node.returnParameterTypeList()->parameters())
if (!decl->name().empty()) 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; return true;
} }
@ -327,8 +334,13 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node)
bool SyntaxChecker::visit(VariableDeclarationStatement const& _statement) bool SyntaxChecker::visit(VariableDeclarationStatement const& _statement)
{ {
// Report if none of the variable components in the tuple have a name (only possible via deprecated "var") // 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( m_errorReporter.syntaxError(
3299_error,
_statement.location(), _statement.location(),
"The use of the \"var\" keyword is disallowed. The declaration part of the statement can be removed, since it is empty." "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) bool SyntaxChecker::visit(StructDefinition const& _struct)
{ {
if (_struct.members().empty()) 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; 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() !_funDef.overrides()
) )
m_errorReporter.warning( m_errorReporter.warning(
2018_error,
_funDef.location(), _funDef.location(),
"Function state mutability can be restricted to " + stateMutabilityToString(m_bestMutabilityAndLocation.mutability) "Function state mutability can be restricted to " + stateMutabilityToString(m_bestMutabilityAndLocation.mutability)
); );
@ -229,7 +230,7 @@ void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly)
{ {
AssemblyViewPureChecker{ AssemblyViewPureChecker{
_inlineAssembly.dialect(), _inlineAssembly.dialect(),
[=](StateMutability _mutability, SourceLocation const& _location) { reportMutability(_mutability, _location); } [&](StateMutability _mutability, SourceLocation const& _location) { reportMutability(_mutability, _location); }
}(_inlineAssembly.operations()); }(_inlineAssembly.operations());
} }
@ -252,6 +253,7 @@ void ViewPureChecker::reportMutability(
)) ))
{ {
m_errorReporter.typeError( m_errorReporter.typeError(
2527_error,
_location, _location,
"Function declared as pure, but this expression (potentially) reads from the " "Function declared as pure, but this expression (potentially) reads from the "
"environment or state and thus requires \"view\"." "environment or state and thus requires \"view\"."
@ -261,6 +263,7 @@ void ViewPureChecker::reportMutability(
else if (_mutability == StateMutability::NonPayable) else if (_mutability == StateMutability::NonPayable)
{ {
m_errorReporter.typeError( m_errorReporter.typeError(
8961_error,
_location, _location,
"Function declared as " + "Function declared as " +
stateMutabilityToString(m_currentFunction->stateMutability()) + stateMutabilityToString(m_currentFunction->stateMutability()) +
@ -277,12 +280,14 @@ void ViewPureChecker::reportMutability(
{ {
if (_nestedLocation) if (_nestedLocation)
m_errorReporter.typeError( m_errorReporter.typeError(
4006_error,
_location, _location,
SecondarySourceLocation().append("\"msg.value\" or \"callvalue()\" appear here inside the modifier.", *_nestedLocation), 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." "This modifier uses \"msg.value\" or \"callvalue()\" and thus the function has to be payable or internal."
); );
else else
m_errorReporter.typeError( m_errorReporter.typeError(
5887_error,
_location, _location,
"\"msg.value\" and \"callvalue()\" can only be used in payable public functions. Make the function " "\"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." "\"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, "runtimeCode"},
{MagicType::Kind::MetaType, "name"}, {MagicType::Kind::MetaType, "name"},
{MagicType::Kind::MetaType, "interfaceId"}, {MagicType::Kind::MetaType, "interfaceId"},
{MagicType::Kind::MetaType, "min"},
{MagicType::Kind::MetaType, "max"},
}; };
set<MagicMember> static const payableMembers{ set<MagicMember> static const payableMembers{
{MagicType::Kind::Message, "value"} {MagicType::Kind::Message, "value"}

View File

@ -556,7 +556,13 @@ MagicType const* TypeProvider::magic(MagicType::Kind _kind)
MagicType const* TypeProvider::meta(Type const* _type) 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); return createAndGet<MagicType>(_type);
} }

View File

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

View File

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

View File

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

View File

@ -67,31 +67,53 @@ public:
/// @returns name of an assembly function to ABI-encode values of @a _givenTypes /// @returns name of an assembly function to ABI-encode values of @a _givenTypes
/// into memory, converting the types to @a _targetTypes on the fly. /// into memory, converting the types to @a _targetTypes on the fly.
/// Parameters are: <headStart> <value_n> ... <value_1>, i.e. /// Parameters are: <headStart> <value_1> ... <value_n>, i.e.
/// the layout on the stack is <value_1> ... <value_n> <headStart> with /// the layout on the stack is <value_n> ... <value_1> <headStart> with
/// the top of the stack on the right. /// the top of the stack on the right.
/// The values represent stack slots. If a type occupies more or less than one /// The values represent stack slots. If a type occupies more or less than one
/// stack slot, it takes exactly that number of values. /// stack slot, it takes exactly that number of values.
/// Returns a pointer to the end of the area written in memory. /// 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 /// Does not allocate memory (does not change the free memory pointer), but writes
/// to memory starting at $headStart and an unrestricted amount after that. /// 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( std::string tupleEncoder(
TypePointers const& _givenTypes, TypePointers const& _givenTypes,
TypePointers const& _targetTypes, 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 /// @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. /// with packed encoding into memory, converting the types to @a _targetTypes on the fly.
/// Parameters are: <memPos> <value_n> ... <value_1>, i.e. /// Parameters are: <memPos> <value_1> ... <value_n>, i.e.
/// the layout on the stack is <value_1> ... <value_n> <memPos> with /// the layout on the stack is <value_n> ... <value_1> <memPos> with
/// the top of the stack on the right. /// the top of the stack on the right.
/// The values represent stack slots. If a type occupies more or less than one /// The values represent stack slots. If a type occupies more or less than one
/// stack slot, it takes exactly that number of values. /// stack slot, it takes exactly that number of values.
/// Returns a pointer to the end of the area written in memory. /// 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 /// Does not allocate memory (does not change the free memory pointer), but writes
/// to memory starting at memPos and an unrestricted amount after that. /// 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 /// @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 /// 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_runtimeSub = creationCompiler.compileConstructor(_contract, _otherCompilers);
m_context.optimise(m_optimiserSettings); 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 std::shared_ptr<evmasm::Assembly> Compiler::runtimeAssemblyPtr() const

View File

@ -192,6 +192,9 @@ void CompilerContext::appendMissingLowLevelFunctions()
pair<string, set<string>> CompilerContext::requestedYulFunctions() pair<string, set<string>> CompilerContext::requestedYulFunctions()
{ {
solAssert(!m_requestedYulFunctionsRan, "requestedYulFunctions called more than once.");
m_requestedYulFunctionsRan = true;
set<string> empty; set<string> empty;
swap(empty, m_externallyUsedYulFunctions); swap(empty, m_externallyUsedYulFunctions);
return {m_yulFunctionCollector.requestedFunctions(), std::move(empty)}; 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 /// Clears the internal list, i.e. calling it again will result in an
/// empty return value. /// empty return value.
std::pair<std::string, std::set<std::string>> requestedYulFunctions(); 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). /// Returns the distance of the given local variable from the bottom of the stack (of the current function).
unsigned baseStackOffsetOfVariable(Declaration const& _declaration) const; unsigned baseStackOffsetOfVariable(Declaration const& _declaration) const;
@ -389,6 +390,8 @@ private:
YulUtilFunctions m_yulUtilFunctions; YulUtilFunctions m_yulUtilFunctions;
/// The queue of low-level functions to generate. /// The queue of low-level functions to generate.
std::queue<std::tuple<std::string, unsigned, unsigned, std::function<void(CompilerContext&)>>> m_lowLevelFunctionGenerationQueue; 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 = string encoderName =
_padToWordBoundaries ? _padToWordBoundaries ?
m_context.abiFunctions().tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes) : m_context.abiFunctions().tupleEncoderReversed(_givenTypes, _targetTypes, _encodeAsLibraryTypes) :
m_context.abiFunctions().tupleEncoderPacked(_givenTypes, _targetTypes); m_context.abiFunctions().tupleEncoderPackedReversed(_givenTypes, _targetTypes);
m_context.callYulFunction(encoderName, sizeOnStack(_givenTypes) + 1, 1); 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, // and adds the function to the compilation queue. Additionally internal functions,
// which are referenced directly or indirectly will be added. // which are referenced directly or indirectly will be added.
appendFunctionSelector(_contract); appendFunctionSelector(_contract);
// This processes the above populated queue until it is empty.
appendMissingFunctions();
} }
size_t ContractCompiler::compileConstructor( size_t ContractCompiler::compileConstructor(
@ -159,10 +157,13 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c
if (FunctionDefinition const* constructor = _contract.constructor()) if (FunctionDefinition const* constructor = _contract.constructor())
appendConstructor(*constructor); appendConstructor(*constructor);
else if (auto c = _contract.nextConstructor(m_context.mostDerivedContract()))
appendBaseConstructor(*c);
else else
{
// Implicit constructors are always non-payable.
appendCallValueCheck(); appendCallValueCheck();
if (auto c = _contract.nextConstructor(m_context.mostDerivedContract()))
appendBaseConstructor(*c);
}
} }
size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _contract) size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _contract)
@ -215,6 +216,9 @@ size_t ContractCompiler::deployLibrary(ContractDefinition const& _contract)
solAssert(!!m_runtimeCompiler, ""); solAssert(!!m_runtimeCompiler, "");
solAssert(_contract.isLibrary(), "Tried to deploy contract as library."); solAssert(_contract.isLibrary(), "Tried to deploy contract as library.");
appendMissingFunctions();
m_runtimeCompiler->appendMissingFunctions();
CompilerContext::LocationSetter locationSetter(m_context, _contract); CompilerContext::LocationSetter locationSetter(m_context, _contract);
solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered"); solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered");
@ -586,13 +590,13 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
if (!_function.isConstructor()) if (!_function.isConstructor())
// adding 1 for return address. // adding 1 for return address.
m_context.adjustStackOffset(parametersSize + 1); 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); m_context.addVariable(*variable, parametersSize);
parametersSize -= variable->annotation().type->sizeOnStack(); parametersSize -= variable->annotation().type->sizeOnStack();
} }
for (ASTPointer<VariableDeclaration const> const& variable: _function.returnParameters()) for (ASTPointer<VariableDeclaration> const& variable: _function.returnParameters())
appendStackVariableInitialisation(*variable); appendStackVariableInitialisation(*variable);
if (_function.isConstructor()) if (_function.isConstructor())
@ -649,7 +653,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
if (stackLayout[i] != i) if (stackLayout[i] != i)
solAssert(false, "Invalid stack layout on cleanup."); 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.removeVariable(*variable);
m_context.adjustStackOffset(-(int)c_returnValuesSize); m_context.adjustStackOffset(-(int)c_returnValuesSize);

View File

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

View File

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

View File

@ -28,9 +28,6 @@
#include <libsolutil/Whiskers.h> #include <libsolutil/Whiskers.h>
#include <libsolutil/StringUtils.h> #include <libsolutil/StringUtils.h>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/reversed.hpp>
using namespace std; using namespace std;
using namespace solidity; using namespace solidity;
using namespace solidity::util; using namespace solidity::util;
@ -299,9 +296,6 @@ string YulUtilFunctions::shiftRightFunction(size_t _numBits)
string YulUtilFunctions::shiftRightFunctionDynamic() 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"; string const functionName = "shift_right_unsigned_dynamic";
return m_functionCollector.createFunction(functionName, [&]() { return m_functionCollector.createFunction(functionName, [&]() {
return 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) string YulUtilFunctions::updateByteSliceFunction(size_t _numBytes, size_t _shiftBytes)
{ {
solAssert(_numBytes <= 32, ""); solAssert(_numBytes <= 32, "");
@ -393,6 +467,8 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type)
return return
Whiskers(R"( Whiskers(R"(
function <functionName>(x, y) -> sum { function <functionName>(x, y) -> sum {
x := <cleanupFunction>(x)
y := <cleanupFunction>(y)
<?signed> <?signed>
// overflow, if x >= 0 and y > (maxValue - x) // overflow, if x >= 0 and y > (maxValue - x)
if and(iszero(slt(x, 0)), sgt(y, sub(<maxValue>, x))) { revert(0, 0) } 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()) ("signed", _type.isSigned())
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue()))) ("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
("minValue", toCompactHexWithPrefix(u256(_type.minValue()))) ("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
("cleanupFunction", cleanupFunction(_type))
.render(); .render();
}); });
} }
@ -421,6 +498,8 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type)
// Multiplication by zero could be treated separately and directly return zero. // Multiplication by zero could be treated separately and directly return zero.
Whiskers(R"( Whiskers(R"(
function <functionName>(x, y) -> product { function <functionName>(x, y) -> product {
x := <cleanupFunction>(x)
y := <cleanupFunction>(y)
<?signed> <?signed>
// overflow, if x > 0, y > 0 and x > (maxValue / y) // 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) } 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()) ("signed", _type.isSigned())
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue()))) ("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
("minValue", toCompactHexWithPrefix(u256(_type.minValue()))) ("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
("cleanupFunction", cleanupFunction(_type))
.render(); .render();
}); });
} }
@ -452,6 +532,8 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
return return
Whiskers(R"( Whiskers(R"(
function <functionName>(x, y) -> r { function <functionName>(x, y) -> r {
x := <cleanupFunction>(x)
y := <cleanupFunction>(y)
if iszero(y) { revert(0, 0) } if iszero(y) { revert(0, 0) }
<?signed> <?signed>
// overflow for minVal / -1 // overflow for minVal / -1
@ -466,6 +548,7 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
("functionName", functionName) ("functionName", functionName)
("signed", _type.isSigned()) ("signed", _type.isSigned())
("minVal", toCompactHexWithPrefix(u256(_type.minValue()))) ("minVal", toCompactHexWithPrefix(u256(_type.minValue())))
("cleanupFunction", cleanupFunction(_type))
.render(); .render();
}); });
} }
@ -477,12 +560,15 @@ string YulUtilFunctions::checkedIntModFunction(IntegerType const& _type)
return return
Whiskers(R"( Whiskers(R"(
function <functionName>(x, y) -> r { function <functionName>(x, y) -> r {
x := <cleanupFunction>(x)
y := <cleanupFunction>(y)
if iszero(y) { revert(0, 0) } if iszero(y) { revert(0, 0) }
r := <?signed>s</signed>mod(x, y) r := <?signed>s</signed>mod(x, y)
} }
)") )")
("functionName", functionName) ("functionName", functionName)
("signed", _type.isSigned()) ("signed", _type.isSigned())
("cleanupFunction", cleanupFunction(_type))
.render(); .render();
}); });
} }
@ -494,6 +580,8 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type)
return return
Whiskers(R"( Whiskers(R"(
function <functionName>(x, y) -> diff { function <functionName>(x, y) -> diff {
x := <cleanupFunction>(x)
y := <cleanupFunction>(y)
<?signed> <?signed>
// underflow, if y >= 0 and x < (minValue + y) // underflow, if y >= 0 and x < (minValue + y)
if and(iszero(slt(y, 0)), slt(x, add(<minValue>, y))) { revert(0, 0) } 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()) ("signed", _type.isSigned())
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue()))) ("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
("minValue", toCompactHexWithPrefix(u256(_type.minValue()))) ("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
("cleanupFunction", cleanupFunction(_type))
.render(); .render();
}); });
} }
@ -1033,13 +1122,12 @@ string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingT
return m_functionCollector.createFunction(functionName, [&]() { return m_functionCollector.createFunction(functionName, [&]() {
if (_mappingType.keyType()->isDynamicallySized()) if (_mappingType.keyType()->isDynamicallySized())
return Whiskers(R"( return Whiskers(R"(
function <functionName>(slot <comma> <key>) -> dataSlot { function <functionName>(slot <?+key>,</+key> <key>) -> dataSlot {
dataSlot := <hash>(slot <comma> <key>) dataSlot := <hash>(<key> <?+key>,</+key> slot)
} }
)") )")
("functionName", functionName) ("functionName", functionName)
("key", _keyType.sizeOnStack() > 0 ? "key" : "") ("key", _keyType.sizeOnStack() > 0 ? "key" : "")
("comma", _keyType.sizeOnStack() > 0 ? "," : "")
("hash", packedHashFunction( ("hash", packedHashFunction(
{&_keyType, TypeProvider::uint256()}, {&_keyType, TypeProvider::uint256()},
{_mappingType.keyType(), TypeProvider::uint256()} {_mappingType.keyType(), TypeProvider::uint256()}
@ -1764,8 +1852,18 @@ string YulUtilFunctions::cleanupFunction(Type const& _type)
solUnimplemented("Fixed point types not implemented."); solUnimplemented("Fixed point types not implemented.");
break; break;
case Type::Category::Function: case Type::Category::Function:
solAssert(dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::External, ""); switch (dynamic_cast<FunctionType const&>(_type).kind())
templ("body", "cleaned := " + cleanupFunction(FixedBytesType(24)) + "(value)"); {
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; break;
case Type::Category::Array: case Type::Category::Array:
case Type::Category::Struct: case Type::Category::Struct:
@ -1938,14 +2036,16 @@ std::string YulUtilFunctions::decrementCheckedFunction(Type const& _type)
return Whiskers(R"( return Whiskers(R"(
function <functionName>(value) -> ret { function <functionName>(value) -> ret {
value := <cleanupFunction>(value)
if <lt>(value, <minval>) { revert(0,0) } if <lt>(value, <minval>) { revert(0,0) }
ret := sub(value, 1) ret := sub(value, 1)
} }
)") )")
("functionName", functionName) ("functionName", functionName)
("minval", toCompactHexWithPrefix(minintval)) ("minval", toCompactHexWithPrefix(minintval))
("lt", type.isSigned() ? "slt" : "lt") ("lt", type.isSigned() ? "slt" : "lt")
.render(); ("cleanupFunction", cleanupFunction(_type))
.render();
}); });
} }
@ -1966,14 +2066,16 @@ std::string YulUtilFunctions::incrementCheckedFunction(Type const& _type)
return Whiskers(R"( return Whiskers(R"(
function <functionName>(value) -> ret { function <functionName>(value) -> ret {
value := <cleanupFunction>(value)
if <gt>(value, <maxval>) { revert(0,0) } if <gt>(value, <maxval>) { revert(0,0) }
ret := add(value, 1) ret := add(value, 1)
} }
)") )")
("functionName", functionName) ("functionName", functionName)
("maxval", toCompactHexWithPrefix(maxintval)) ("maxval", toCompactHexWithPrefix(maxintval))
("gt", type.isSigned() ? "sgt" : "gt") ("gt", type.isSigned() ? "sgt" : "gt")
.render(); ("cleanupFunction", cleanupFunction(_type))
.render();
}); });
} }
@ -1988,15 +2090,17 @@ string YulUtilFunctions::negateNumberCheckedFunction(Type const& _type)
return m_functionCollector.createFunction(functionName, [&]() { return m_functionCollector.createFunction(functionName, [&]() {
return Whiskers(R"( return Whiskers(R"(
function <functionName>(_value) -> ret { function <functionName>(value) -> ret {
if slt(_value, <minval>) { revert(0,0) } value := <cleanupFunction>(value)
ret := sub(0, _value) if slt(value, <minval>) { revert(0,0) }
ret := sub(0, value)
} }
)") )")
("functionName", functionName) ("functionName", functionName)
("minval", toCompactHexWithPrefix(minintval)) ("minval", toCompactHexWithPrefix(minintval))
.render(); ("cleanupFunction", cleanupFunction(_type))
}); .render();
});
} }
string YulUtilFunctions::zeroValueFunction(Type const& _type, bool _splitFunctionTypes) string YulUtilFunctions::zeroValueFunction(Type const& _type, bool _splitFunctionTypes)
@ -2214,7 +2318,6 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC
} }
solAssert(_type.isValueType(), ""); solAssert(_type.isValueType(), "");
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type)) if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
if (funType->kind() == FunctionType::Kind::External) if (funType->kind() == FunctionType::Kind::External)
return Whiskers(R"( return Whiskers(R"(
@ -2228,18 +2331,23 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC
("splitFunction", splitExternalFunctionIdFunction()) ("splitFunction", splitExternalFunctionIdFunction())
.render(); .render();
return Whiskers(R"( return Whiskers(R"(
function <functionName>(memPtr) -> value { function <functionName>(ptr) -> value {
value := <load>(memPtr) <?fromCalldata>
<?needsValidation> value := calldataload(ptr)
<validate>(value) <validate>(value)
</needsValidation> <!fromCalldata>
value := <cleanup>(mload(ptr))
</fromCalldata>
} }
)") )")
("functionName", functionName) ("functionName", functionName)
("load", _fromCalldata ? "calldataload" : "mload") ("fromCalldata", _fromCalldata)
("needsValidation", _fromCalldata) ("validate", validatorFunction(_type))
("validate", _fromCalldata ? 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(); .render();
}); });
} }

View File

@ -81,6 +81,14 @@ public:
std::string shiftLeftFunctionDynamic(); std::string shiftLeftFunctionDynamic();
std::string shiftRightFunction(size_t _numBits); std::string shiftRightFunction(size_t _numBits);
std::string shiftRightFunctionDynamic(); 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 /// @returns the name of a function which replaces the
/// _numBytes bytes starting at byte position _shiftBytes (counted from the least significant /// _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 readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes);
std::string readFromStorageDynamic(Type const& _type, 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 /// signature: (addr) -> value
std::string readFromMemory(Type const& _type); std::string readFromMemory(Type const& _type);
/// @returns a function that reads a value type from calldata. /// @returns a function that reads a value type from calldata.

View File

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

View File

@ -42,7 +42,6 @@
#include <libsolutil/Keccak256.h> #include <libsolutil/Keccak256.h>
#include <libsolutil/Visitor.h> #include <libsolutil/Visitor.h>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/adaptor/transformed.hpp> #include <boost/range/adaptor/transformed.hpp>
using namespace std; using namespace std;
@ -117,10 +116,12 @@ struct CopyTranslate: public yul::ASTCopier
reference.isOffset == false && reference.isSlot == false, reference.isOffset == false && reference.isSlot == false,
"Should not be called for offset/slot" "Should not be called for offset/slot"
); );
auto const& var = m_context.localVariable(*varDecl);
solAssert(var.type().sizeOnStack() == 1, "");
return yul::Identifier{ return yul::Identifier{
_identifier.location, _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; 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) void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement)
{ {
if (Expression const* expression = _varDeclStatement.initialValue()) if (Expression const* expression = _varDeclStatement.initialValue())
@ -230,29 +253,53 @@ bool IRGeneratorForStatements::visit(Conditional const& _conditional)
bool IRGeneratorForStatements::visit(Assignment const& _assignment) bool IRGeneratorForStatements::visit(Assignment const& _assignment)
{ {
_assignment.rightHandSide().accept(*this); _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); _assignment.leftHandSide().accept(*this);
solAssert(!!m_currentLValue, "LValue not retrieved."); solAssert(!!m_currentLValue, "LValue not retrieved.");
if (_assignment.assignmentOperator() != Token::Assign) if (assignmentOperator != Token::Assign)
{ {
solAssert(type(_assignment.leftHandSide()) == *intermediateType, ""); solAssert(type(_assignment.leftHandSide()).isValueType(), "Compound operators only available for value types.");
solAssert(intermediateType->isValueType(), "Compound operators only available for value types."); solAssert(rightIntermediateType->isValueType(), "Compound operators only available for value types.");
IRVariable leftIntermediate = readFromLValue(*m_currentLValue); IRVariable leftIntermediate = readFromLValue(*m_currentLValue);
m_code << value.name() << " := " << binaryOperation( if (TokenTraits::isShiftOp(binaryOperator))
TokenTraits::AssignmentToBinaryOp(_assignment.assignmentOperator()), {
*intermediateType, solAssert(type(_assignment) == leftIntermediate.type(), "");
leftIntermediate.name(), solAssert(type(_assignment) == type(_assignment.leftHandSide()), "");
value.name() 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); writeToLValue(*m_currentLValue, value);
m_currentLValue.reset(); m_currentLValue.reset();
if (*_assignment.annotation().type != *TypeProvider::emptyTuple()) if (*_assignment.annotation().type != *TypeProvider::emptyTuple())
define(_assignment, value); define(_assignment, value);
@ -477,12 +524,16 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
return false; 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.leftExpression().accept(*this);
_binOp.rightExpression().accept(*this); _binOp.rightExpression().accept(*this);
if (commonType->category() == Type::Category::RationalNumber) if (TokenTraits::isCompareOp(op))
define(_binOp) << toCompactHexWithPrefix(commonType->literalValue(nullptr)) << "\n";
else if (TokenTraits::isCompareOp(op))
{ {
if (auto type = dynamic_cast<FunctionType const*>(commonType)) if (auto type = dynamic_cast<FunctionType const*>(commonType))
{ {
@ -496,9 +547,9 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
isSigned = type->isSigned(); isSigned = type->isSigned();
string args = string args =
expressionAsType(_binOp.leftExpression(), *commonType) + expressionAsType(_binOp.leftExpression(), *commonType, true) +
", " + ", " +
expressionAsType(_binOp.rightExpression(), *commonType); expressionAsType(_binOp.rightExpression(), *commonType, true);
string expr; string expr;
if (op == Token::Equal) if (op == Token::Equal)
@ -517,6 +568,12 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
solAssert(false, "Unknown comparison operator."); solAssert(false, "Unknown comparison operator.");
define(_binOp) << expr << "\n"; 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 else
{ {
string left = expressionAsType(_binOp.leftExpression(), *commonType); string left = expressionAsType(_binOp.leftExpression(), *commonType);
@ -584,13 +641,6 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
break; break;
case FunctionType::Kind::Internal: 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; optional<FunctionDefinition const*> functionDef;
if (auto memberAccess = dynamic_cast<MemberAccess const*>(&_functionCall.expression())) 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.has_value(), "");
solAssert(functionDef.value() == nullptr || functionDef.value()->isImplemented(), ""); 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) if (functionDef.value() != nullptr)
define(_functionCall) << define(_functionCall) <<
m_context.enqueueFunctionForCodeGeneration(*functionDef.value()) << m_context.enqueueFunctionForCodeGeneration(*functionDef.value()) <<
@ -649,10 +706,12 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
} }
case FunctionType::Kind::External: case FunctionType::Kind::External:
case FunctionType::Kind::DelegateCall: case FunctionType::Kind::DelegateCall:
appendExternalFunctionCall(_functionCall, arguments);
break;
case FunctionType::Kind::BareCall: case FunctionType::Kind::BareCall:
case FunctionType::Kind::BareDelegateCall: case FunctionType::Kind::BareDelegateCall:
case FunctionType::Kind::BareStaticCall: case FunctionType::Kind::BareStaticCall:
appendExternalFunctionCall(_functionCall, arguments); appendBareCall(_functionCall, arguments);
break; break;
case FunctionType::Kind::BareCallCode: case FunctionType::Kind::BareCallCode:
solAssert(false, "Callcode has been removed."); 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()); ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
vector<IRVariable> indexedArgs; vector<IRVariable> indexedArgs;
string nonIndexedArgs; vector<string> nonIndexedArgs;
TypePointers nonIndexedArgTypes; TypePointers nonIndexedArgTypes;
TypePointers nonIndexedParamTypes; TypePointers nonIndexedParamTypes;
if (!event.isAnonymous()) if (!event.isAnonymous())
@ -686,10 +745,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
} }
else else
{ {
string vars = IRVariable(arg).commaSeparatedList(); nonIndexedArgs += IRVariable(arg).stackSlots();
if (!vars.empty())
// In reverse because abi_encode expects it like that.
nonIndexedArgs = ", " + move(vars) + nonIndexedArgs;
nonIndexedArgTypes.push_back(arg.annotation().type); nonIndexedArgTypes.push_back(arg.annotation().type);
nonIndexedParamTypes.push_back(paramTypes[i]); nonIndexedParamTypes.push_back(paramTypes[i]);
} }
@ -704,7 +760,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
templ("end", m_context.newYulVariable()); templ("end", m_context.newYulVariable());
templ("freeMemory", freeMemory()); templ("freeMemory", freeMemory());
templ("encode", abi.tupleEncoder(nonIndexedArgTypes, nonIndexedParamTypes)); templ("encode", abi.tupleEncoder(nonIndexedArgTypes, nonIndexedParamTypes));
templ("nonIndexedArgs", nonIndexedArgs); templ("nonIndexedArgs", joinHumanReadablePrefixed(nonIndexedArgs));
templ("log", "log" + to_string(indexedArgs.size())); templ("log", "log" + to_string(indexedArgs.size()));
templ("indexedArgs", joinHumanReadablePrefixed(indexedArgs | boost::adaptors::transformed([&](auto const& _arg) { templ("indexedArgs", joinHumanReadablePrefixed(indexedArgs | boost::adaptors::transformed([&](auto const& _arg) {
return _arg.commaSeparatedList(); return _arg.commaSeparatedList();
@ -731,13 +787,134 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
break; 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: case FunctionType::Kind::Revert:
{ {
solAssert(arguments.size() == parameterTypes.size(), ""); solAssert(arguments.size() == parameterTypes.size(), "");
if (arguments.empty()) if (arguments.empty())
m_code << "revert(0, 0)\n"; m_code << "revert(0, 0)\n";
else 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; break;
} }
@ -760,6 +937,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
solAssert(arguments.size() == 1, ""); solAssert(arguments.size() == 1, "");
ArrayType const* arrayType = TypeProvider::bytesMemory(); ArrayType const* arrayType = TypeProvider::bytesMemory();
auto array = convert(*arguments[0], *arrayType); auto array = convert(*arguments[0], *arrayType);
define(_functionCall) << define(_functionCall) <<
@ -905,11 +1083,11 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
); );
TypePointers argumentTypes; TypePointers argumentTypes;
string constructorParams; vector<string> constructorParams;
for (ASTPointer<Expression const> const& arg: arguments) for (ASTPointer<Expression const> const& arg: arguments)
{ {
argumentTypes.push_back(arg->annotation().type); argumentTypes.push_back(arg->annotation().type);
constructorParams += ", " + IRVariable{*arg}.commaSeparatedList(); constructorParams += IRVariable{*arg}.stackSlots();
} }
ContractDefinition const* contract = ContractDefinition const* contract =
@ -935,9 +1113,9 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
t("releaseTemporaryMemory", m_utils.releaseTemporaryMemoryFunction()); t("releaseTemporaryMemory", m_utils.releaseTemporaryMemoryFunction());
t("object", m_context.creationObjectName(*contract)); t("object", m_context.creationObjectName(*contract));
t("abiEncode", 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("value", functionType->valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
t("saltSet", functionType->saltSet()); t("saltSet", functionType->saltSet());
if (functionType->saltSet()) if (functionType->saltSet())
@ -1126,7 +1304,20 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
solAssert(false, "Blockhash has been removed."); solAssert(false, "Blockhash has been removed.");
else if (member == "creationCode" || member == "runtimeCode") 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") else if (member == "name")
{ {
@ -1141,6 +1332,16 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
result ^= fromBigEndian<uint64_t>(function.first.ref()); result ^= fromBigEndian<uint64_t>(function.first.ref());
define(_memberAccess) << formatNumber(u256{result} << (256 - 32)) << "\n"; 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)) else if (set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}.count(member))
{ {
// no-op // no-op
@ -1312,14 +1513,11 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
solAssert(keyType.sizeOnStack() <= 1, ""); solAssert(keyType.sizeOnStack() <= 1, "");
string slot = m_context.newYulVariable(); 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("slot", slot);
templ("indexAccess", m_utils.mappingIndexAccessFunction(mappingType, keyType)); templ("indexAccess", m_utils.mappingIndexAccessFunction(mappingType, keyType));
templ("base", IRVariable(_indexAccess.baseExpression()).commaSeparatedList()); templ("base", IRVariable(_indexAccess.baseExpression()).commaSeparatedList());
if (keyType.sizeOnStack() == 0) templ("key", IRVariable(*_indexAccess.indexExpression()).commaSeparatedList());
templ("key", "");
else
templ("key", ", " + IRVariable(*_indexAccess.indexExpression()).commaSeparatedList());
m_code << templ.render(); m_code << templ.render();
setLValue(_indexAccess, IRLValue{ setLValue(_indexAccess, IRLValue{
*_indexAccess.annotation().type, *_indexAccess.annotation().type,
@ -1542,11 +1740,9 @@ void IRGeneratorForStatements::handleVariableReference(
Expression const& _referencingExpression Expression const& _referencingExpression
) )
{ {
// TODO for the constant case, we have to be careful: if (_variable.isStateVariable() && _variable.isConstant())
// If the value is visited twice, `defineExpression` is called twice on define(_referencingExpression) << constantValueFunction(_variable) << "()\n";
// the same expression. else if (_variable.isStateVariable() && _variable.immutable())
solUnimplementedAssert(!_variable.isConstant(), "");
if (_variable.isStateVariable() && _variable.immutable())
setLValue(_referencingExpression, IRLValue{ setLValue(_referencingExpression, IRLValue{
*_variable.annotation().type, *_variable.annotation().type,
IRLValue::Immutable{&_variable} IRLValue::Immutable{&_variable}
@ -1574,18 +1770,20 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
) )
{ {
FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression())); FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression()));
solAssert( solAssert(!funType.takesArbitraryParameters(), "");
funType.takesArbitraryParameters() || solAssert(_arguments.size() == funType.parameterTypes().size(), "");
_arguments.size() == funType.parameterTypes().size(), "" solAssert(!funType.isBareCall(), "");
);
solUnimplementedAssert(!funType.bound(), "");
FunctionType::Kind const funKind = funType.kind(); FunctionType::Kind const funKind = funType.kind();
solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), ""); solAssert(
solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed."); 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; solUnimplementedAssert(!funType.bound(), "");
bool const useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall());
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}; ReturnInfo const returnInfo{m_context.evmVersion(), funType};
@ -1594,12 +1792,9 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
for (auto const& arg: _arguments) for (auto const& arg: _arguments)
{ {
argumentTypes.emplace_back(&type(*arg)); argumentTypes.emplace_back(&type(*arg));
if (IRVariable(*arg).type().sizeOnStack() > 0) argumentStrings += IRVariable(*arg).stackSlots();
argumentStrings.emplace_back(IRVariable(*arg).commaSeparatedList());
} }
string argumentString = argumentStrings.empty() ? ""s : (", " + joinHumanReadable(argumentStrings));
solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, "");
if (!m_context.evmVersion().canOverchargeGasForCall()) if (!m_context.evmVersion().canOverchargeGasForCall())
{ {
@ -1611,33 +1806,19 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
m_code << "mstore(add(" << freeMemory() << ", " << to_string(returnInfo.estimatedReturnSize) << "), 0)\n"; 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"( Whiskers templ(R"(
<?checkExistence> if iszero(extcodesize(<address>)) { revert(0, 0) }
if iszero(extcodesize(<address>)) { revert(0, 0) }
</checkExistence>
// storage for arguments and returned data // storage for arguments and returned data
let <pos> := <freeMemory> let <pos> := <freeMemory>
<?bareCall> mstore(<pos>, <shl28>(<funId>))
<!bareCall> let <end> := <encodeArgs>(add(<pos>, 4) <argumentString>)
mstore(<pos>, <shl28>(<funId>))
</bareCall>
let <end> := <encodeArgs>(
<?bareCall>
<pos>
<!bareCall>
add(<pos>, 4)
</bareCall>
<argumentString>
)
let <success> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <reservedReturnSize>) let <success> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <reservedReturnSize>)
<?noTryCall> <?noTryCall>
if iszero(<success>) { <forwardingRevert>() } if iszero(<success>) { <forwardingRevert>() }
</noTryCall> </noTryCall>
<?hasRetVars> let <retVars> </hasRetVars> <?+retVars> let <retVars> </+retVars>
if <success> { if <success> {
<?dynamicReturnSize> <?dynamicReturnSize>
// copy dynamic return data out // copy dynamic return data out
@ -1648,12 +1829,11 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
mstore(<freeMemoryPointer>, add(<pos>, <roundUp>(<returnSize>))) mstore(<freeMemoryPointer>, add(<pos>, <roundUp>(<returnSize>)))
// decode return parameters from external try-call into retVars // 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("pos", m_context.newYulVariable());
templ("end", m_context.newYulVariable()); templ("end", m_context.newYulVariable());
templ("bareCall", funType.isBareCall());
if (_functionCall.annotation().tryCall) if (_functionCall.annotation().tryCall)
templ("success", m_context.trySuccessConditionVariable(_functionCall)); templ("success", m_context.trySuccessConditionVariable(_functionCall));
else else
@ -1661,17 +1841,8 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
templ("freeMemory", freeMemory()); templ("freeMemory", freeMemory());
templ("shl28", m_utils.shiftLeftFunction(8 * (32 - 4))); templ("shl28", m_utils.shiftLeftFunction(8 * (32 - 4)));
if (!funType.isBareCall()) templ("funId", IRVariable(_functionCall.expression()).part("functionIdentifier").name());
templ("funId", IRVariable(_functionCall.expression()).part("functionIdentifier").name()); templ("address", IRVariable(_functionCall.expression()).part("address").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());
// Always use the actual return length, and not our calculated expected length, if returndatacopy is supported. // 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. // This ensures it can catch badly formatted input from external calls.
@ -1684,39 +1855,20 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
string const retVars = IRVariable(_functionCall).commaSeparatedList(); string const retVars = IRVariable(_functionCall).commaSeparatedList();
templ("retVars", retVars); templ("retVars", retVars);
templ("hasRetVars", !retVars.empty());
solAssert(retVars.empty() == returnInfo.returnTypes.empty(), ""); solAssert(retVars.empty() == returnInfo.returnTypes.empty(), "");
templ("roundUp", m_utils.roundUpFunction()); 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("dynamicReturnSize", returnInfo.dynamicReturnSize);
templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer)); templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer));
templ("noTryCall", !_functionCall.annotation().tryCall); 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; bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall;
solUnimplementedAssert(encodeInPlace == !funType.padArguments(), ""); solAssert(funType.padArguments(), "");
if (encodeInPlace) templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, funType.parameterTypes(), encodeForLibraryCall));
{ templ("argumentString", joinHumanReadablePrefixed(argumentStrings));
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(!isDelegateCall || !funType.valueSet(), "Value set for delegatecall"); solAssert(!isDelegateCall || !funType.valueSet(), "Value set for delegatecall");
solAssert(!useStaticCall || !funType.valueSet(), "Value set for staticcall"); solAssert(!useStaticCall || !funType.valueSet(), "Value set for staticcall");
@ -1724,10 +1876,6 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
templ("hasValue", !isDelegateCall && !useStaticCall); templ("hasValue", !isDelegateCall && !useStaticCall);
templ("value", funType.valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0"); 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()) if (funType.gasSet())
templ("gas", IRVariable(_functionCall.expression()).part("gas").name()); templ("gas", IRVariable(_functionCall.expression()).part("gas").name());
else if (m_context.evmVersion().canOverchargeGasForCall()) else if (m_context.evmVersion().canOverchargeGasForCall())
@ -1740,8 +1888,6 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10; u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10;
if (funType.valueSet()) if (funType.valueSet())
gasNeededByCaller += evmasm::GasCosts::callValueTransferGas; gasNeededByCaller += evmasm::GasCosts::callValueTransferGas;
if (!checkExistence)
gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know
templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")"); templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
} }
// Order is important here, STATICCALL might overlap with DELEGATECALL. // Order is important here, STATICCALL might overlap with DELEGATECALL.
@ -1754,8 +1900,103 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
templ("forwardingRevert", m_utils.forwardingRevertFunction()); templ("forwardingRevert", m_utils.forwardingRevertFunction());
solUnimplementedAssert(funKind != FunctionType::Kind::RIPEMD160, ""); m_code << templ.render();
solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, ""); }
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(); 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); IRVariable from(_expression);
if (from.type() == _to) if (from.type() == _to)
return from.commaSeparatedList(); {
if (_forceCleanup)
return m_utils.cleanupFunction(_to) + "(" + from.commaSeparatedList() + ")";
else
return from.commaSeparatedList();
}
else else
return m_utils.conversionFunction(from.type(), _to) + "(" + from.commaSeparatedList() + ")"; return m_utils.conversionFunction(from.type(), _to) + "(" + from.commaSeparatedList() + ")";
} }
@ -1860,6 +2106,10 @@ string IRGeneratorForStatements::binaryOperation(
string const& _right 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)) if (IntegerType const* type = dynamic_cast<IntegerType const*>(&_type))
{ {
string fun; string fun;
@ -1903,6 +2153,31 @@ string IRGeneratorForStatements::binaryOperation(
return {}; 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) void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _binOp)
{ {
langutil::Token const op = _binOp.getOperator(); langutil::Token const op = _binOp.getOperator();
@ -2021,13 +2296,7 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue)
")\n"; ")\n";
}, },
[&](IRLValue::Memory const& _memory) { [&](IRLValue::Memory const& _memory) {
if (_memory.byteArrayElement) if (_lvalue.type.isValueType())
define(result) <<
m_utils.cleanupFunction(_lvalue.type) <<
"(mload(" <<
_memory.address <<
"))\n";
else if (_lvalue.type.isValueType())
define(result) << define(result) <<
m_utils.readFromMemory(_lvalue.type) << m_utils.readFromMemory(_lvalue.type) <<
"(" << "(" <<

View File

@ -54,6 +54,10 @@ public:
/// Calculates expression's value and returns variable where it was stored /// Calculates expression's value and returns variable where it was stored
IRVariable evaluateExpression(Expression const& _expression, Type const& _to); 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; void endVisit(VariableDeclarationStatement const& _variableDeclaration) override;
bool visit(Conditional const& _conditional) override; bool visit(Conditional const& _conditional) override;
bool visit(Assignment const& _assignment) override; bool visit(Assignment const& _assignment) override;
@ -102,6 +106,13 @@ private:
std::vector<ASTPointer<Expression const>> const& _arguments 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 /// @returns code that evaluates to the first unused memory slot (which does not have to
/// be empty). /// be empty).
static std::string freeMemory(); static std::string freeMemory();
@ -112,7 +123,8 @@ private:
/// @returns a Yul expression representing the current value of @a _expression, /// @returns a Yul expression representing the current value of @a _expression,
/// converted to type @a _to if it does not yet have that type. /// 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 /// @returns an output stream that can be used to define @a _var using a function call or
/// single stack slot expression. /// single stack slot expression.
@ -142,6 +154,11 @@ private:
std::string const& _right 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. /// Assigns the value of @a _value to the lvalue @a _lvalue.
void writeToLValue(IRLValue const& _lvalue, IRVariable const& _value); void writeToLValue(IRLValue const& _lvalue, IRVariable const& _value);
/// @returns a fresh IR variable containing the value of the lvalue @a _lvalue. /// @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, ""); solAssert(itemName.empty() || itemType, "");
return IRVariable{suffixedName(itemName), itemType ? *itemType : m_type}; 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 vector<string> IRVariable::stackSlots() const
@ -89,7 +89,7 @@ string IRVariable::name() const
{ {
solAssert(m_type.sizeOnStack() == 1, ""); solAssert(m_type.sizeOnStack() == 1, "");
auto const& [itemName, type] = m_type.stackItems().front(); auto const& [itemName, type] = m_type.stackItems().front();
solAssert(!type, ""); solAssert(!type, "Expected null type for name " + itemName);
return suffixedName(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 /// 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. /// in ``m_type.stackItems()`` and may again occupy multiple stack slots.
IRVariable part(std::string const& _slot) const; IRVariable part(std::string const& _slot) const;
private:
/// @returns a vector containing the names of the stack slots of the variable. /// @returns a vector containing the names of the stack slots of the variable.
std::vector<std::string> stackSlots() const; std::vector<std::string> stackSlots() const;
private:
/// @returns a name consisting of the base name appended with an underscore and @æ _suffix, /// @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. /// unless @a _suffix is empty, in which case the base name itself is returned.
std::string suffixedName(std::string const& _suffix) const; std::string suffixedName(std::string const& _suffix) const;

View File

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

View File

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

View File

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

View File

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

View File

@ -73,9 +73,10 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
); );
abi.emplace(std::move(method)); 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(); FunctionType const* externalFunctionType = constrType.interfaceFunctionType();
solAssert(!!externalFunctionType, ""); solAssert(!!externalFunctionType, "");
Json::Value method; Json::Value method;

View File

@ -67,7 +67,7 @@
#include <json/json.h> #include <json/json.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string/replace.hpp>
#include <utility> #include <utility>
using namespace std; using namespace std;
@ -239,7 +239,7 @@ bool CompilerStack::parse()
m_errorReporter.clear(); m_errorReporter.clear();
if (SemVerVersion{string(VersionString)}.isPrerelease()) 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}; Parser parser{m_errorReporter, m_evmVersion, m_parserErrorRecovery};
@ -949,6 +949,7 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string
else else
{ {
m_errorReporter.parserError( m_errorReporter.parserError(
6275_error,
import->location(), import->location(),
string("Source \"" + importPath + "\" not found: " + result.responseOrErrorMessage) string("Source \"" + importPath + "\" not found: " + result.responseOrErrorMessage)
); );
@ -1110,6 +1111,7 @@ void CompilerStack::compileContract(
compiledContract.runtimeObject.bytecode.size() > 0x6000 compiledContract.runtimeObject.bytecode.size() > 0x6000
) )
m_errorReporter.warning( m_errorReporter.warning(
5574_error,
_contract.location(), _contract.location(),
"Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). " "Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). "
"This contract may not be deployable on mainnet. " "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())) if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
{ {
Json::Value method(devDocumentation(fun->annotation().docTags)); 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()) 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; methods[it.second->externalSignature()] = method;
}
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -29,8 +29,6 @@
#include <libsolutil/CommonData.h> #include <libsolutil/CommonData.h>
#include <boost/range/adaptor/reversed.hpp>
#include <memory> #include <memory>
#include <functional> #include <functional>
@ -141,6 +139,7 @@ bool ScopeFiller::registerVariable(TypedName const& _name, SourceLocation const&
{ {
//@TODO secondary location //@TODO secondary location
m_errorReporter.declarationError( m_errorReporter.declarationError(
1395_error,
_location, _location,
"Variable name " + _name.name.str() + " already taken in this scope." "Variable name " + _name.name.str() + " already taken in this scope."
); );
@ -161,6 +160,7 @@ bool ScopeFiller::registerFunction(FunctionDefinition const& _funDef)
{ {
//@TODO secondary location //@TODO secondary location
m_errorReporter.declarationError( m_errorReporter.declarationError(
6052_error,
_funDef.location, _funDef.location,
"Function name " + _funDef.name.str() + " already taken in this scope." "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); RecursionGuard guard(*this);
if (currentToken() != Token::Identifier || currentLiteral() != "object") if (currentToken() != Token::Identifier || currentLiteral() != "object")
fatalParserError("Expected keyword \"object\"."); fatalParserError(4294_error, "Expected keyword \"object\".");
advance(); advance();
shared_ptr<Object> ret = make_shared<Object>(); 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") else if (currentToken() == Token::Identifier && currentLiteral() == "data")
parseData(*ret); parseData(*ret);
else else
fatalParserError("Expected keyword \"data\" or \"object\" or \"}\"."); fatalParserError(8143_error, "Expected keyword \"data\" or \"object\" or \"}\".");
} }
if (_containingObject) if (_containingObject)
addNamedSubObject(*_containingObject, ret->name, ret); addNamedSubObject(*_containingObject, ret->name, ret);
@ -96,7 +96,7 @@ shared_ptr<Object> ObjectParser::parseObject(Object* _containingObject)
shared_ptr<Block> ObjectParser::parseCode() shared_ptr<Block> ObjectParser::parseCode()
{ {
if (currentToken() != Token::Identifier || currentLiteral() != "code") if (currentToken() != Token::Identifier || currentLiteral() != "code")
fatalParserError("Expected keyword \"code\"."); fatalParserError(4846_error, "Expected keyword \"code\".");
advance(); advance();
return parseBlock(); return parseBlock();
@ -133,11 +133,11 @@ YulString ObjectParser::parseUniqueName(Object const* _containingObject)
expectToken(Token::StringLiteral, false); expectToken(Token::StringLiteral, false);
YulString name{currentLiteral()}; YulString name{currentLiteral()};
if (name.empty()) if (name.empty())
parserError("Object name cannot be empty."); parserError(3287_error, "Object name cannot be empty.");
else if (_containingObject && _containingObject->name == name) 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)) 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(); advance();
return name; return name;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,6 +4,7 @@ planned state of the optimiser.
Table of Contents: Table of Contents:
- [Selecting optimisations](#selecting-optimisations)
- [Preprocessing](#preprocessing) - [Preprocessing](#preprocessing)
- [Pseudo-SSA Transformation](#pseudo-ssa-transformation) - [Pseudo-SSA Transformation](#pseudo-ssa-transformation)
- [Tools](#tools) - [Tools](#tools)
@ -33,6 +34,17 @@ the following transformation steps are the main components:
- [Redundant Assign Eliminator](#redundant-assign-eliminator) - [Redundant Assign Eliminator](#redundant-assign-eliminator)
- [Full Function Inliner](#full-function-inliner) - [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 ## Preprocessing
The preprocessing components perform transformations to get the program 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) 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)); return holds_alternative<Assignment>(_statement) && m_toRemove.count(&std::get<Assignment>(_statement));
}); });

View File

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

View File

@ -29,8 +29,6 @@
#include <libyul/Dialect.h> #include <libyul/Dialect.h>
#include <libyul/SideEffects.h> #include <libyul/SideEffects.h>
#include <boost/algorithm/cxx11/none_of.hpp>
using namespace std; using namespace std;
using namespace solidity; using namespace solidity;
using namespace solidity::yul; 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 // 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 // replace `let a := f()` by `pop(f())` (in pure Yul, this will be
// `drop(f())`). // `drop(f())`).
if (boost::algorithm::none_of( if (std::none_of(
varDecl.variables, varDecl.variables.begin(),
[=](TypedName const& _typedName) { return used(_typedName.name); } varDecl.variables.end(),
[&](TypedName const& _typedName) { return used(_typedName.name); }
)) ))
{ {
if (!varDecl.value) 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 set -e
cd docs cd docs
pip install -r requirements.txt pip3 install -r requirements.txt
sphinx-build -nW -b html -d _build/doctrees . _build/html sphinx-build -nW -b html -d _build/doctrees . _build/html
cd .. cd ..

View File

@ -100,7 +100,7 @@ do
force_abiv2_flag="" force_abiv2_flag=""
if [[ "$abiv2" == "yes" ]] if [[ "$abiv2" == "yes" ]]
then then
force_abiv2_flag="--abiencoderv2 --optimize-yul" force_abiv2_flag="--abiencoderv2"
fi fi
printTask "--> Running tests using "$optimize" --evm-version "$vm" $force_abiv2_flag..." 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(); string pathName = (p / _fileName).string();
if (fs::exists(pathName) && !m_args.count(g_strOverwrite)) 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; m_error = true;
return; return;
} }
@ -719,10 +719,10 @@ bool CommandLineInterface::parseArguments(int _argc, char** _argv)
g_hasOutput = false; g_hasOutput = false;
// Declare the supported options. // 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 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. for details.
Usage: solc [options] [input_file...] 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 Imports are automatically read from the filesystem, but it is also possible to
remap paths using the context:prefix=path syntax. remap paths using the context:prefix=path syntax.
Example: 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,
po::options_description::m_default_line_length - 23 po::options_description::m_default_line_length - 23
); );
@ -780,20 +780,27 @@ Allowed options)",
) )
( (
g_argImportAst.c_str(), g_argImportAst.c_str(),
"Import ASTs to be compiled, assumes input holds the AST in compact JSON format. " ("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" "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(), 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(), 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(), 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(), g_strYulDialect.c_str(),
@ -807,8 +814,8 @@ Allowed options)",
) )
( (
g_argLink.c_str(), g_argLink.c_str(),
"Switch to linker mode, ignoring all options apart from --libraries " ("Switch to linker mode, ignoring all options apart from --" + g_argLibraries + " "
"and modify binaries in place." "and modify binaries in place.").c_str()
) )
( (
g_argMetadataHash.c_str(), g_argMetadataHash.c_str(),
@ -835,7 +842,7 @@ Allowed options)",
"Set for how many contract runs to optimize. " "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." "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_strNoOptimizeYul.c_str(), "Disable Yul optimizer in Solidity.")
( (
g_strYulOptimizations.c_str(), 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(","))) for (string const& item: boost::split(requests, m_args[g_argCombinedJson].as<string>(), boost::is_any_of(",")))
if (!g_combinedJsonArgs.count(item)) if (!g_combinedJsonArgs.count(item))
{ {
serr() << "Invalid option to --combined-json: " << item << endl; serr() << "Invalid option to --" << g_argCombinedJson << ": " << item << endl;
return false; return false;
} }
} }
@ -1047,7 +1054,7 @@ bool CommandLineInterface::processInput()
std::optional<langutil::EVMVersion> versionOption = langutil::EVMVersion::fromString(versionOptionStr); std::optional<langutil::EVMVersion> versionOption = langutil::EVMVersion::fromString(versionOptionStr);
if (!versionOption) if (!versionOption)
{ {
serr() << "Invalid option for --evm-version: " << versionOptionStr << endl; serr() << "Invalid option for --" << g_strEVMVersion << ": " << versionOptionStr << endl;
return false; return false;
} }
m_evmVersion = *versionOption; m_evmVersion = *versionOption;
@ -1064,14 +1071,37 @@ bool CommandLineInterface::processInput()
bool optimize = m_args.count(g_argOptimize); bool optimize = m_args.count(g_argOptimize);
if (m_args.count(g_strOptimizeYul)) 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; return false;
} }
if (m_args.count(g_strNoOptimizeYul)) 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; 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)) if (m_args.count(g_argMachine))
{ {
string machine = m_args[g_argMachine].as<string>(); string machine = m_args[g_argMachine].as<string>();
@ -1083,7 +1113,7 @@ bool CommandLineInterface::processInput()
targetMachine = Machine::Ewasm; targetMachine = Machine::Ewasm;
else else
{ {
serr() << "Invalid option for --machine: " << machine << endl; serr() << "Invalid option for --" << g_argMachine << ": " << machine << endl;
return false; return false;
} }
} }
@ -1099,13 +1129,14 @@ bool CommandLineInterface::processInput()
inputLanguage = Input::Ewasm; inputLanguage = Input::Ewasm;
if (targetMachine != Machine::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; return false;
} }
} }
else else
{ {
serr() << "Invalid option for --yul-dialect: " << dialect << endl; serr() << "Invalid option for --" << g_strYulDialect << ": " << dialect << endl;
return false; return false;
} }
} }
@ -1122,7 +1153,7 @@ bool CommandLineInterface::processInput()
"Warning: Yul is still experimental. Please use the output with care." << "Warning: Yul is still experimental. Please use the output with care." <<
endl; endl;
return assemble(inputLanguage, targetMachine, optimize); return assemble(inputLanguage, targetMachine, optimize, yulOptimiserSteps);
} }
if (m_args.count(g_argLink)) if (m_args.count(g_argLink))
{ {
@ -1142,7 +1173,7 @@ bool CommandLineInterface::processInput()
m_metadataHash = CompilerStack::MetadataHash::None; m_metadataHash = CompilerStack::MetadataHash::None;
else else
{ {
serr() << "Invalid option for --metadata-hash: " << hashStr << endl; serr() << "Invalid option for --" << g_argMetadataHash << ": " << hashStr << endl;
return false; return false;
} }
} }
@ -1534,18 +1565,21 @@ string CommandLineInterface::objectWithLinkRefsHex(evmasm::LinkerObject const& _
bool CommandLineInterface::assemble( bool CommandLineInterface::assemble(
yul::AssemblyStack::Language _language, yul::AssemblyStack::Language _language,
yul::AssemblyStack::Machine _targetMachine, yul::AssemblyStack::Machine _targetMachine,
bool _optimize bool _optimize,
optional<string> _yulOptimiserSteps
) )
{ {
solAssert(_optimize || !_yulOptimiserSteps.has_value(), "");
bool successful = true; bool successful = true;
map<string, yul::AssemblyStack> assemblyStacks; map<string, yul::AssemblyStack> assemblyStacks;
for (auto const& src: m_sourceCodes) for (auto const& src: m_sourceCodes)
{ {
auto& stack = assemblyStacks[src.first] = yul::AssemblyStack( OptimiserSettings settings = _optimize ? OptimiserSettings::full() : OptimiserSettings::minimal();
m_evmVersion, if (_yulOptimiserSteps.has_value())
_language, settings.yulOptimiserSteps = _yulOptimiserSteps.value();
_optimize ? OptimiserSettings::full() : OptimiserSettings::minimal()
); auto& stack = assemblyStacks[src.first] = yul::AssemblyStack(m_evmVersion, _language, settings);
try try
{ {
if (!stack.parseAndAnalyze(src.first, src.second)) if (!stack.parseAndAnalyze(src.first, src.second))

View File

@ -56,7 +56,12 @@ private:
/// @returns the full object with library placeholder hints in hex. /// @returns the full object with library placeholder hints in hex.
static std::string objectWithLinkRefsHex(evmasm::LinkerObject const& _obj); 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(); void outputCompilationResults();

View File

@ -187,7 +187,7 @@ add_executable(soltest ${sources}
${libsolidity_util_sources} ${libsolidity_util_sources}
${yul_phaser_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) # 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") ("evmonepath", po::value<fs::path>(&evmonePath)->default_value(EVMOneEnvOrDefaultPath()), "path to evmone library")
("no-smt", po::bool_switch(&disableSMT), "disable SMT checker") ("no-smt", po::bool_switch(&disableSMT), "disable SMT checker")
("optimize", po::bool_switch(&optimize), "enables optimization") ("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") ("abiencoderv2", po::bool_switch(&useABIEncoderV2), "enables abi encoder v2")
("show-messages", po::bool_switch(&showMessages), "enables message output") ("show-messages", po::bool_switch(&showMessages), "enables message output")
("show-metadata", po::bool_switch(&showMetadata), "enables metadata 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 evmonePath;
boost::filesystem::path testPath; boost::filesystem::path testPath;
bool optimize = false; bool optimize = false;
bool optimizeYul = false; bool enforceViaYul = false;
bool disableSMT = false; bool disableSMT = false;
bool useABIEncoderV2 = false; bool useABIEncoderV2 = false;
bool showMessages = false; bool showMessages = false;

View File

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

View File

@ -40,6 +40,11 @@ void TestCase::printSettings(ostream& _stream, const string& _linePrefix, const
_stream << _linePrefix << "// " << setting.first << ": " << setting.second << endl; _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) bool TestCase::isTestFilename(boost::filesystem::path const& _filename)
{ {
string extension = _filename.extension().string(); string extension = _filename.extension().string();

View File

@ -38,6 +38,7 @@ public:
{ {
std::string filename; std::string filename;
langutil::EVMVersion evmVersion; langutil::EVMVersion evmVersion;
bool enforceCompileViaYul;
}; };
enum class TestResult { Success, Failure, FatalError }; 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; virtual void printSource(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false) const = 0;
/// Outputs settings. /// Outputs settings.
virtual void printSettings(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false); 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. /// Outputs test expectations to @arg _stream that match the actual results of the test.
/// Each line of output is prefixed with @arg _linePrefix. /// Each line of output is prefixed with @arg _linePrefix.
virtual void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const = 0; 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::unit_test::test_suite& _suite,
boost::filesystem::path const& _basepath, boost::filesystem::path const& _basepath,
boost::filesystem::path const& _path, boost::filesystem::path const& _path,
bool _enforceViaYul,
TestCase::TestCaseCreator _testCaseCreator TestCase::TestCaseCreator _testCaseCreator
) )
{ {
int numTestsAdded = 0; int numTestsAdded = 0;
fs::path fullpath = _basepath / _path; 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)) if (fs::is_directory(fullpath))
{ {
test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string()); test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string());
@ -78,7 +79,12 @@ int registerTests(
fs::directory_iterator() fs::directory_iterator()
)) ))
if (fs::is_directory(entry.path()) || TestCase::isTestFilename(entry.path().filename())) 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); _suite.add(sub_suite);
} }
else else
@ -164,6 +170,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
master, master,
options.testPath / ts.path, options.testPath / ts.path,
ts.subpath, ts.subpath,
options.enforceViaYul,
ts.testCaseCreator ts.testCaseCreator
) > 0, std::string("no ") + ts.title + " tests found"); ) > 0, std::string("no ") + ts.title + " tests found");
} }

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